From 7f642e51670bc38a4ef782a363936850bc2b0ba9 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 06:38:23 -0700 Subject: Move dutil into libs/dutil --- src/.editorconfig | 37 + src/CustomizedNativeRecommendedRules.ruleset | 8 - src/Directory.Build.props | 26 - src/Directory.Build.targets | 73 - src/Directory.csproj.props | 13 - src/Directory.vcxproj.props | 115 - src/NativeMultiTargeting.Build.props | 10 - src/dutil/acl2util.cpp | 135 - src/dutil/aclutil.cpp | 1044 ---- src/dutil/apputil.cpp | 124 - src/dutil/apuputil.cpp | 700 --- src/dutil/atomutil.cpp | 1297 ----- src/dutil/buffutil.cpp | 529 -- src/dutil/build/WixToolset.DUtil.props | 28 - src/dutil/butil.cpp | 257 - src/dutil/cabcutil.cpp | 1539 ------ src/dutil/cabutil.cpp | 617 --- src/dutil/certutil.cpp | 342 -- src/dutil/conutil.cpp | 673 --- src/dutil/cryputil.cpp | 404 -- src/dutil/deputil.cpp | 699 --- src/dutil/dictutil.cpp | 784 --- src/dutil/dirutil.cpp | 441 -- src/dutil/dlutil.cpp | 802 --- src/dutil/dpiutil.cpp | 274 - src/dutil/dutil.cpp | 524 -- src/dutil/dutil.nuspec | 27 - src/dutil/dutil.vcxproj | 183 - src/dutil/dutil.vcxproj.filters | 372 -- src/dutil/eseutil.cpp | 1340 ----- src/dutil/fileutil.cpp | 2032 ------- src/dutil/gdiputil.cpp | 227 - src/dutil/guidutil.cpp | 54 - src/dutil/iis7util.cpp | 535 -- src/dutil/inc/aclutil.h | 154 - src/dutil/inc/apputil.h | 45 - src/dutil/inc/apuputil.h | 87 - src/dutil/inc/atomutil.h | 146 - src/dutil/inc/buffutil.h | 91 - src/dutil/inc/butil.h | 60 - src/dutil/inc/cabcutil.h | 62 - src/dutil/inc/cabutil.h | 56 - src/dutil/inc/certutil.h | 66 - src/dutil/inc/conutil.h | 80 - src/dutil/inc/cryputil.h | 106 - src/dutil/inc/deputil.h | 147 - src/dutil/inc/dictutil.h | 69 - src/dutil/inc/dirutil.h | 59 - src/dutil/inc/dlutil.h | 59 - src/dutil/inc/dpiutil.h | 120 - src/dutil/inc/dutil.h | 190 - src/dutil/inc/dutilsources.h | 76 - src/dutil/inc/eseutil.h | 223 - src/dutil/inc/fileutil.h | 247 - src/dutil/inc/gdiputil.h | 39 - src/dutil/inc/guidutil.h | 21 - src/dutil/inc/iis7util.h | 222 - src/dutil/inc/inetutil.h | 39 - src/dutil/inc/iniutil.h | 79 - src/dutil/inc/jsonutil.h | 112 - src/dutil/inc/locutil.h | 120 - src/dutil/inc/logutil.h | 192 - src/dutil/inc/memutil.h | 80 - src/dutil/inc/metautil.h | 52 - src/dutil/inc/monutil.h | 108 - src/dutil/inc/osutil.h | 42 - src/dutil/inc/pathutil.h | 252 - src/dutil/inc/perfutil.h | 24 - src/dutil/inc/polcutil.h | 39 - src/dutil/inc/procutil.h | 75 - src/dutil/inc/regutil.h | 246 - src/dutil/inc/resrutil.h | 43 - src/dutil/inc/reswutil.h | 31 - src/dutil/inc/rexutil.h | 54 - src/dutil/inc/rmutil.h | 46 - src/dutil/inc/rssutil.h | 89 - src/dutil/inc/sceutil.h | 273 - src/dutil/inc/sczutil.h | 30 - src/dutil/inc/shelutil.h | 47 - src/dutil/inc/sqlutil.h | 136 - src/dutil/inc/srputil.h | 45 - src/dutil/inc/strutil.h | 316 -- src/dutil/inc/svcutil.h | 21 - src/dutil/inc/thmutil.h | 765 --- src/dutil/inc/timeutil.h | 38 - src/dutil/inc/uncutil.h | 20 - src/dutil/inc/uriutil.h | 100 - src/dutil/inc/userutil.h | 32 - src/dutil/inc/verutil.h | 93 - src/dutil/inc/wiutil.h | 402 -- src/dutil/inc/wuautil.h | 19 - src/dutil/inc/xmlutil.h | 167 - src/dutil/inetutil.cpp | 155 - src/dutil/iniutil.cpp | 768 --- src/dutil/jsonutil.cpp | 687 --- src/dutil/locutil.cpp | 628 --- src/dutil/logutil.cpp | 961 ---- src/dutil/memutil.cpp | 336 -- src/dutil/metautil.cpp | 378 -- src/dutil/monutil.cpp | 2019 ------- src/dutil/osutil.cpp | 251 - src/dutil/packages.config | 7 - src/dutil/path2utl.cpp | 104 - src/dutil/pathutil.cpp | 1083 ---- src/dutil/perfutil.cpp | 82 - src/dutil/polcutil.cpp | 126 - src/dutil/precomp.h | 98 - src/dutil/proc2utl.cpp | 83 - src/dutil/proc3utl.cpp | 129 - src/dutil/procutil.cpp | 522 -- src/dutil/regutil.cpp | 1035 ---- src/dutil/resrutil.cpp | 266 - src/dutil/reswutil.cpp | 386 -- src/dutil/rexutil.cpp | 601 --- src/dutil/rmutil.cpp | 488 -- src/dutil/rssutil.cpp | 647 --- src/dutil/sceutil.cpp | 2489 --------- src/dutil/shelutil.cpp | 342 -- src/dutil/sqlutil.cpp | 1002 ---- src/dutil/srputil.cpp | 252 - src/dutil/strutil.cpp | 2824 ---------- src/dutil/svcutil.cpp | 59 - src/dutil/thmutil.cpp | 5709 -------------------- src/dutil/timeutil.cpp | 385 -- src/dutil/uncutil.cpp | 69 - src/dutil/uriutil.cpp | 553 -- src/dutil/userutil.cpp | 300 - src/dutil/verutil.cpp | 647 --- src/dutil/wiutil.cpp | 1629 ------ src/dutil/wuautil.cpp | 104 - src/dutil/xmlutil.cpp | 1332 ----- src/dutil/xsd/thmutil.xsd | 1188 ---- .../dutil/CustomizedNativeRecommendedRules.ruleset | 8 + src/libs/dutil/Directory.Build.props | 26 + src/libs/dutil/Directory.Build.targets | 73 + src/libs/dutil/Directory.csproj.props | 13 + src/libs/dutil/Directory.vcxproj.props | 115 + src/libs/dutil/NativeMultiTargeting.Build.props | 10 + src/libs/dutil/README.md | 2 + src/libs/dutil/WixToolset.DUtil/acl2util.cpp | 135 + src/libs/dutil/WixToolset.DUtil/aclutil.cpp | 1044 ++++ src/libs/dutil/WixToolset.DUtil/apputil.cpp | 124 + src/libs/dutil/WixToolset.DUtil/apuputil.cpp | 700 +++ src/libs/dutil/WixToolset.DUtil/atomutil.cpp | 1297 +++++ src/libs/dutil/WixToolset.DUtil/buffutil.cpp | 529 ++ .../WixToolset.DUtil/build/WixToolset.DUtil.props | 28 + src/libs/dutil/WixToolset.DUtil/butil.cpp | 257 + src/libs/dutil/WixToolset.DUtil/cabcutil.cpp | 1539 ++++++ src/libs/dutil/WixToolset.DUtil/cabutil.cpp | 617 +++ src/libs/dutil/WixToolset.DUtil/certutil.cpp | 342 ++ src/libs/dutil/WixToolset.DUtil/conutil.cpp | 673 +++ src/libs/dutil/WixToolset.DUtil/cryputil.cpp | 404 ++ src/libs/dutil/WixToolset.DUtil/deputil.cpp | 699 +++ src/libs/dutil/WixToolset.DUtil/dictutil.cpp | 784 +++ src/libs/dutil/WixToolset.DUtil/dirutil.cpp | 441 ++ src/libs/dutil/WixToolset.DUtil/dlutil.cpp | 802 +++ src/libs/dutil/WixToolset.DUtil/dpiutil.cpp | 274 + src/libs/dutil/WixToolset.DUtil/dutil.cpp | 524 ++ src/libs/dutil/WixToolset.DUtil/dutil.nuspec | 27 + src/libs/dutil/WixToolset.DUtil/dutil.vcxproj | 183 + .../dutil/WixToolset.DUtil/dutil.vcxproj.filters | 372 ++ src/libs/dutil/WixToolset.DUtil/eseutil.cpp | 1340 +++++ src/libs/dutil/WixToolset.DUtil/fileutil.cpp | 2032 +++++++ src/libs/dutil/WixToolset.DUtil/gdiputil.cpp | 227 + src/libs/dutil/WixToolset.DUtil/guidutil.cpp | 54 + src/libs/dutil/WixToolset.DUtil/iis7util.cpp | 535 ++ src/libs/dutil/WixToolset.DUtil/inc/aclutil.h | 154 + src/libs/dutil/WixToolset.DUtil/inc/apputil.h | 45 + src/libs/dutil/WixToolset.DUtil/inc/apuputil.h | 87 + src/libs/dutil/WixToolset.DUtil/inc/atomutil.h | 146 + src/libs/dutil/WixToolset.DUtil/inc/buffutil.h | 91 + src/libs/dutil/WixToolset.DUtil/inc/butil.h | 60 + src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h | 62 + src/libs/dutil/WixToolset.DUtil/inc/cabutil.h | 56 + src/libs/dutil/WixToolset.DUtil/inc/certutil.h | 66 + src/libs/dutil/WixToolset.DUtil/inc/conutil.h | 80 + src/libs/dutil/WixToolset.DUtil/inc/cryputil.h | 106 + src/libs/dutil/WixToolset.DUtil/inc/deputil.h | 147 + src/libs/dutil/WixToolset.DUtil/inc/dictutil.h | 69 + src/libs/dutil/WixToolset.DUtil/inc/dirutil.h | 59 + src/libs/dutil/WixToolset.DUtil/inc/dlutil.h | 59 + src/libs/dutil/WixToolset.DUtil/inc/dpiutil.h | 120 + src/libs/dutil/WixToolset.DUtil/inc/dutil.h | 190 + src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h | 76 + src/libs/dutil/WixToolset.DUtil/inc/eseutil.h | 223 + src/libs/dutil/WixToolset.DUtil/inc/fileutil.h | 247 + src/libs/dutil/WixToolset.DUtil/inc/gdiputil.h | 39 + src/libs/dutil/WixToolset.DUtil/inc/guidutil.h | 21 + src/libs/dutil/WixToolset.DUtil/inc/iis7util.h | 222 + src/libs/dutil/WixToolset.DUtil/inc/inetutil.h | 39 + src/libs/dutil/WixToolset.DUtil/inc/iniutil.h | 79 + src/libs/dutil/WixToolset.DUtil/inc/jsonutil.h | 112 + src/libs/dutil/WixToolset.DUtil/inc/locutil.h | 120 + src/libs/dutil/WixToolset.DUtil/inc/logutil.h | 192 + src/libs/dutil/WixToolset.DUtil/inc/memutil.h | 80 + src/libs/dutil/WixToolset.DUtil/inc/metautil.h | 52 + src/libs/dutil/WixToolset.DUtil/inc/monutil.h | 108 + src/libs/dutil/WixToolset.DUtil/inc/osutil.h | 42 + src/libs/dutil/WixToolset.DUtil/inc/pathutil.h | 252 + src/libs/dutil/WixToolset.DUtil/inc/perfutil.h | 24 + src/libs/dutil/WixToolset.DUtil/inc/polcutil.h | 39 + src/libs/dutil/WixToolset.DUtil/inc/procutil.h | 75 + src/libs/dutil/WixToolset.DUtil/inc/regutil.h | 246 + src/libs/dutil/WixToolset.DUtil/inc/resrutil.h | 43 + src/libs/dutil/WixToolset.DUtil/inc/reswutil.h | 31 + src/libs/dutil/WixToolset.DUtil/inc/rexutil.h | 54 + src/libs/dutil/WixToolset.DUtil/inc/rmutil.h | 46 + src/libs/dutil/WixToolset.DUtil/inc/rssutil.h | 89 + src/libs/dutil/WixToolset.DUtil/inc/sceutil.h | 273 + src/libs/dutil/WixToolset.DUtil/inc/sczutil.h | 30 + src/libs/dutil/WixToolset.DUtil/inc/shelutil.h | 47 + src/libs/dutil/WixToolset.DUtil/inc/sqlutil.h | 136 + src/libs/dutil/WixToolset.DUtil/inc/srputil.h | 45 + src/libs/dutil/WixToolset.DUtil/inc/strutil.h | 316 ++ src/libs/dutil/WixToolset.DUtil/inc/svcutil.h | 21 + src/libs/dutil/WixToolset.DUtil/inc/thmutil.h | 765 +++ src/libs/dutil/WixToolset.DUtil/inc/timeutil.h | 38 + src/libs/dutil/WixToolset.DUtil/inc/uncutil.h | 20 + src/libs/dutil/WixToolset.DUtil/inc/uriutil.h | 100 + src/libs/dutil/WixToolset.DUtil/inc/userutil.h | 32 + src/libs/dutil/WixToolset.DUtil/inc/verutil.h | 93 + src/libs/dutil/WixToolset.DUtil/inc/wiutil.h | 402 ++ src/libs/dutil/WixToolset.DUtil/inc/wuautil.h | 19 + src/libs/dutil/WixToolset.DUtil/inc/xmlutil.h | 167 + src/libs/dutil/WixToolset.DUtil/inetutil.cpp | 155 + src/libs/dutil/WixToolset.DUtil/iniutil.cpp | 768 +++ src/libs/dutil/WixToolset.DUtil/jsonutil.cpp | 687 +++ src/libs/dutil/WixToolset.DUtil/locutil.cpp | 628 +++ src/libs/dutil/WixToolset.DUtil/logutil.cpp | 961 ++++ src/libs/dutil/WixToolset.DUtil/memutil.cpp | 336 ++ src/libs/dutil/WixToolset.DUtil/metautil.cpp | 378 ++ src/libs/dutil/WixToolset.DUtil/monutil.cpp | 2019 +++++++ src/libs/dutil/WixToolset.DUtil/osutil.cpp | 251 + src/libs/dutil/WixToolset.DUtil/packages.config | 7 + src/libs/dutil/WixToolset.DUtil/path2utl.cpp | 104 + src/libs/dutil/WixToolset.DUtil/pathutil.cpp | 1083 ++++ src/libs/dutil/WixToolset.DUtil/perfutil.cpp | 82 + src/libs/dutil/WixToolset.DUtil/polcutil.cpp | 126 + src/libs/dutil/WixToolset.DUtil/precomp.h | 98 + src/libs/dutil/WixToolset.DUtil/proc2utl.cpp | 83 + src/libs/dutil/WixToolset.DUtil/proc3utl.cpp | 129 + src/libs/dutil/WixToolset.DUtil/procutil.cpp | 522 ++ src/libs/dutil/WixToolset.DUtil/regutil.cpp | 1035 ++++ src/libs/dutil/WixToolset.DUtil/resrutil.cpp | 266 + src/libs/dutil/WixToolset.DUtil/reswutil.cpp | 386 ++ src/libs/dutil/WixToolset.DUtil/rexutil.cpp | 601 +++ src/libs/dutil/WixToolset.DUtil/rmutil.cpp | 488 ++ src/libs/dutil/WixToolset.DUtil/rssutil.cpp | 647 +++ src/libs/dutil/WixToolset.DUtil/sceutil.cpp | 2489 +++++++++ src/libs/dutil/WixToolset.DUtil/shelutil.cpp | 342 ++ src/libs/dutil/WixToolset.DUtil/sqlutil.cpp | 1002 ++++ src/libs/dutil/WixToolset.DUtil/srputil.cpp | 252 + src/libs/dutil/WixToolset.DUtil/strutil.cpp | 2824 ++++++++++ src/libs/dutil/WixToolset.DUtil/svcutil.cpp | 59 + src/libs/dutil/WixToolset.DUtil/thmutil.cpp | 5709 ++++++++++++++++++++ src/libs/dutil/WixToolset.DUtil/timeutil.cpp | 385 ++ src/libs/dutil/WixToolset.DUtil/uncutil.cpp | 69 + src/libs/dutil/WixToolset.DUtil/uriutil.cpp | 553 ++ src/libs/dutil/WixToolset.DUtil/userutil.cpp | 300 + src/libs/dutil/WixToolset.DUtil/verutil.cpp | 647 +++ src/libs/dutil/WixToolset.DUtil/wiutil.cpp | 1629 ++++++ src/libs/dutil/WixToolset.DUtil/wuautil.cpp | 104 + src/libs/dutil/WixToolset.DUtil/xmlutil.cpp | 1332 +++++ src/libs/dutil/WixToolset.DUtil/xsd/thmutil.xsd | 1188 ++++ src/libs/dutil/appveyor.cmd | 24 + src/libs/dutil/appveyor.yml | 44 + src/libs/dutil/dutil.sln | 47 + src/libs/dutil/nuget.config | 8 + .../dutil/test/DUtilUnitTest/ApupUtilTests.cpp | 46 + src/libs/dutil/test/DUtilUnitTest/AssemblyInfo.cpp | 12 + src/libs/dutil/test/DUtilUnitTest/DUtilTests.cpp | 35 + .../dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 99 + .../DUtilUnitTest/DUtilUnitTest.vcxproj.filters | 80 + src/libs/dutil/test/DUtilUnitTest/DictUtilTest.cpp | 191 + src/libs/dutil/test/DUtilUnitTest/DirUtilTests.cpp | 70 + src/libs/dutil/test/DUtilUnitTest/FileUtilTest.cpp | 125 + src/libs/dutil/test/DUtilUnitTest/GuidUtilTest.cpp | 60 + src/libs/dutil/test/DUtilUnitTest/IniUtilTest.cpp | 345 ++ src/libs/dutil/test/DUtilUnitTest/MemUtilTest.cpp | 505 ++ src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp | 487 ++ src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp | 80 + src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp | 488 ++ src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp | 192 + .../TestData/ApupUtilTests/FeedBv2.0.xml | 68 + src/libs/dutil/test/DUtilUnitTest/UnitTest.rc | 6 + src/libs/dutil/test/DUtilUnitTest/UriUtilTest.cpp | 98 + src/libs/dutil/test/DUtilUnitTest/VerUtilTests.cpp | 933 ++++ src/libs/dutil/test/DUtilUnitTest/error.cpp | 26 + src/libs/dutil/test/DUtilUnitTest/error.h | 14 + src/libs/dutil/test/DUtilUnitTest/packages.config | 13 + src/libs/dutil/test/DUtilUnitTest/precomp.cpp | 3 + src/libs/dutil/test/DUtilUnitTest/precomp.h | 32 + src/libs/dutil/test/DUtilUnitTest/resource.h | 2 + src/signing.json | 13 + src/test/DUtilUnitTest/ApupUtilTests.cpp | 46 - src/test/DUtilUnitTest/AssemblyInfo.cpp | 12 - src/test/DUtilUnitTest/DUtilTests.cpp | 35 - src/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 99 - .../DUtilUnitTest/DUtilUnitTest.vcxproj.filters | 80 - src/test/DUtilUnitTest/DictUtilTest.cpp | 191 - src/test/DUtilUnitTest/DirUtilTests.cpp | 70 - src/test/DUtilUnitTest/FileUtilTest.cpp | 125 - src/test/DUtilUnitTest/GuidUtilTest.cpp | 60 - src/test/DUtilUnitTest/IniUtilTest.cpp | 345 -- src/test/DUtilUnitTest/MemUtilTest.cpp | 505 -- src/test/DUtilUnitTest/MonUtilTest.cpp | 487 -- src/test/DUtilUnitTest/PathUtilTest.cpp | 80 - src/test/DUtilUnitTest/SceUtilTest.cpp | 488 -- src/test/DUtilUnitTest/StrUtilTest.cpp | 192 - .../TestData/ApupUtilTests/FeedBv2.0.xml | 68 - src/test/DUtilUnitTest/UnitTest.rc | 6 - src/test/DUtilUnitTest/UriUtilTest.cpp | 98 - src/test/DUtilUnitTest/VerUtilTests.cpp | 933 ---- src/test/DUtilUnitTest/error.cpp | 26 - src/test/DUtilUnitTest/error.h | 14 - src/test/DUtilUnitTest/packages.config | 13 - src/test/DUtilUnitTest/precomp.cpp | 3 - src/test/DUtilUnitTest/precomp.h | 32 - src/test/DUtilUnitTest/resource.h | 2 - src/version.json | 11 + 320 files changed, 57800 insertions(+), 57614 deletions(-) create mode 100644 src/.editorconfig delete mode 100644 src/CustomizedNativeRecommendedRules.ruleset delete mode 100644 src/Directory.Build.props delete mode 100644 src/Directory.Build.targets delete mode 100644 src/Directory.csproj.props delete mode 100644 src/Directory.vcxproj.props delete mode 100644 src/NativeMultiTargeting.Build.props delete mode 100644 src/dutil/acl2util.cpp delete mode 100644 src/dutil/aclutil.cpp delete mode 100644 src/dutil/apputil.cpp delete mode 100644 src/dutil/apuputil.cpp delete mode 100644 src/dutil/atomutil.cpp delete mode 100644 src/dutil/buffutil.cpp delete mode 100644 src/dutil/build/WixToolset.DUtil.props delete mode 100644 src/dutil/butil.cpp delete mode 100644 src/dutil/cabcutil.cpp delete mode 100644 src/dutil/cabutil.cpp delete mode 100644 src/dutil/certutil.cpp delete mode 100644 src/dutil/conutil.cpp delete mode 100644 src/dutil/cryputil.cpp delete mode 100644 src/dutil/deputil.cpp delete mode 100644 src/dutil/dictutil.cpp delete mode 100644 src/dutil/dirutil.cpp delete mode 100644 src/dutil/dlutil.cpp delete mode 100644 src/dutil/dpiutil.cpp delete mode 100644 src/dutil/dutil.cpp delete mode 100644 src/dutil/dutil.nuspec delete mode 100644 src/dutil/dutil.vcxproj delete mode 100644 src/dutil/dutil.vcxproj.filters delete mode 100644 src/dutil/eseutil.cpp delete mode 100644 src/dutil/fileutil.cpp delete mode 100644 src/dutil/gdiputil.cpp delete mode 100644 src/dutil/guidutil.cpp delete mode 100644 src/dutil/iis7util.cpp delete mode 100644 src/dutil/inc/aclutil.h delete mode 100644 src/dutil/inc/apputil.h delete mode 100644 src/dutil/inc/apuputil.h delete mode 100644 src/dutil/inc/atomutil.h delete mode 100644 src/dutil/inc/buffutil.h delete mode 100644 src/dutil/inc/butil.h delete mode 100644 src/dutil/inc/cabcutil.h delete mode 100644 src/dutil/inc/cabutil.h delete mode 100644 src/dutil/inc/certutil.h delete mode 100644 src/dutil/inc/conutil.h delete mode 100644 src/dutil/inc/cryputil.h delete mode 100644 src/dutil/inc/deputil.h delete mode 100644 src/dutil/inc/dictutil.h delete mode 100644 src/dutil/inc/dirutil.h delete mode 100644 src/dutil/inc/dlutil.h delete mode 100644 src/dutil/inc/dpiutil.h delete mode 100644 src/dutil/inc/dutil.h delete mode 100644 src/dutil/inc/dutilsources.h delete mode 100644 src/dutil/inc/eseutil.h delete mode 100644 src/dutil/inc/fileutil.h delete mode 100644 src/dutil/inc/gdiputil.h delete mode 100644 src/dutil/inc/guidutil.h delete mode 100644 src/dutil/inc/iis7util.h delete mode 100644 src/dutil/inc/inetutil.h delete mode 100644 src/dutil/inc/iniutil.h delete mode 100644 src/dutil/inc/jsonutil.h delete mode 100644 src/dutil/inc/locutil.h delete mode 100644 src/dutil/inc/logutil.h delete mode 100644 src/dutil/inc/memutil.h delete mode 100644 src/dutil/inc/metautil.h delete mode 100644 src/dutil/inc/monutil.h delete mode 100644 src/dutil/inc/osutil.h delete mode 100644 src/dutil/inc/pathutil.h delete mode 100644 src/dutil/inc/perfutil.h delete mode 100644 src/dutil/inc/polcutil.h delete mode 100644 src/dutil/inc/procutil.h delete mode 100644 src/dutil/inc/regutil.h delete mode 100644 src/dutil/inc/resrutil.h delete mode 100644 src/dutil/inc/reswutil.h delete mode 100644 src/dutil/inc/rexutil.h delete mode 100644 src/dutil/inc/rmutil.h delete mode 100644 src/dutil/inc/rssutil.h delete mode 100644 src/dutil/inc/sceutil.h delete mode 100644 src/dutil/inc/sczutil.h delete mode 100644 src/dutil/inc/shelutil.h delete mode 100644 src/dutil/inc/sqlutil.h delete mode 100644 src/dutil/inc/srputil.h delete mode 100644 src/dutil/inc/strutil.h delete mode 100644 src/dutil/inc/svcutil.h delete mode 100644 src/dutil/inc/thmutil.h delete mode 100644 src/dutil/inc/timeutil.h delete mode 100644 src/dutil/inc/uncutil.h delete mode 100644 src/dutil/inc/uriutil.h delete mode 100644 src/dutil/inc/userutil.h delete mode 100644 src/dutil/inc/verutil.h delete mode 100644 src/dutil/inc/wiutil.h delete mode 100644 src/dutil/inc/wuautil.h delete mode 100644 src/dutil/inc/xmlutil.h delete mode 100644 src/dutil/inetutil.cpp delete mode 100644 src/dutil/iniutil.cpp delete mode 100644 src/dutil/jsonutil.cpp delete mode 100644 src/dutil/locutil.cpp delete mode 100644 src/dutil/logutil.cpp delete mode 100644 src/dutil/memutil.cpp delete mode 100644 src/dutil/metautil.cpp delete mode 100644 src/dutil/monutil.cpp delete mode 100644 src/dutil/osutil.cpp delete mode 100644 src/dutil/packages.config delete mode 100644 src/dutil/path2utl.cpp delete mode 100644 src/dutil/pathutil.cpp delete mode 100644 src/dutil/perfutil.cpp delete mode 100644 src/dutil/polcutil.cpp delete mode 100644 src/dutil/precomp.h delete mode 100644 src/dutil/proc2utl.cpp delete mode 100644 src/dutil/proc3utl.cpp delete mode 100644 src/dutil/procutil.cpp delete mode 100644 src/dutil/regutil.cpp delete mode 100644 src/dutil/resrutil.cpp delete mode 100644 src/dutil/reswutil.cpp delete mode 100644 src/dutil/rexutil.cpp delete mode 100644 src/dutil/rmutil.cpp delete mode 100644 src/dutil/rssutil.cpp delete mode 100644 src/dutil/sceutil.cpp delete mode 100644 src/dutil/shelutil.cpp delete mode 100644 src/dutil/sqlutil.cpp delete mode 100644 src/dutil/srputil.cpp delete mode 100644 src/dutil/strutil.cpp delete mode 100644 src/dutil/svcutil.cpp delete mode 100644 src/dutil/thmutil.cpp delete mode 100644 src/dutil/timeutil.cpp delete mode 100644 src/dutil/uncutil.cpp delete mode 100644 src/dutil/uriutil.cpp delete mode 100644 src/dutil/userutil.cpp delete mode 100644 src/dutil/verutil.cpp delete mode 100644 src/dutil/wiutil.cpp delete mode 100644 src/dutil/wuautil.cpp delete mode 100644 src/dutil/xmlutil.cpp delete mode 100644 src/dutil/xsd/thmutil.xsd create mode 100644 src/libs/dutil/CustomizedNativeRecommendedRules.ruleset create mode 100644 src/libs/dutil/Directory.Build.props create mode 100644 src/libs/dutil/Directory.Build.targets create mode 100644 src/libs/dutil/Directory.csproj.props create mode 100644 src/libs/dutil/Directory.vcxproj.props create mode 100644 src/libs/dutil/NativeMultiTargeting.Build.props create mode 100644 src/libs/dutil/README.md create mode 100644 src/libs/dutil/WixToolset.DUtil/acl2util.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/aclutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/apputil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/apuputil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/atomutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/buffutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/build/WixToolset.DUtil.props create mode 100644 src/libs/dutil/WixToolset.DUtil/butil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/cabcutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/cabutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/certutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/conutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/cryputil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/deputil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/dictutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/dirutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/dlutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/dpiutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/dutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/dutil.nuspec create mode 100644 src/libs/dutil/WixToolset.DUtil/dutil.vcxproj create mode 100644 src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters create mode 100644 src/libs/dutil/WixToolset.DUtil/eseutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/fileutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/gdiputil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/guidutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/iis7util.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/aclutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/apputil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/apuputil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/atomutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/buffutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/butil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/cabutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/certutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/conutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/cryputil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/deputil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/dictutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/dirutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/dlutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/dpiutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/dutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/eseutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/fileutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/gdiputil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/guidutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/iis7util.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/inetutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/iniutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/jsonutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/locutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/logutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/memutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/metautil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/monutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/osutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/pathutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/perfutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/polcutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/procutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/regutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/resrutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/reswutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/rexutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/rmutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/rssutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/sceutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/sczutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/shelutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/sqlutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/srputil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/strutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/svcutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/thmutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/timeutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/uncutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/uriutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/userutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/verutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/wiutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/wuautil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/xmlutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/inetutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/iniutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/jsonutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/locutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/logutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/memutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/metautil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/monutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/osutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/packages.config create mode 100644 src/libs/dutil/WixToolset.DUtil/path2utl.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/pathutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/perfutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/polcutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/precomp.h create mode 100644 src/libs/dutil/WixToolset.DUtil/proc2utl.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/proc3utl.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/procutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/regutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/resrutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/reswutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/rexutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/rmutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/rssutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/sceutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/shelutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/sqlutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/srputil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/strutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/svcutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/thmutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/timeutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/uncutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/uriutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/userutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/verutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/wiutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/wuautil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/xmlutil.cpp create mode 100644 src/libs/dutil/WixToolset.DUtil/xsd/thmutil.xsd create mode 100644 src/libs/dutil/appveyor.cmd create mode 100644 src/libs/dutil/appveyor.yml create mode 100644 src/libs/dutil/dutil.sln create mode 100644 src/libs/dutil/nuget.config create mode 100644 src/libs/dutil/test/DUtilUnitTest/ApupUtilTests.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/AssemblyInfo.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/DUtilTests.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj create mode 100644 src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters create mode 100644 src/libs/dutil/test/DUtilUnitTest/DictUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/DirUtilTests.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/FileUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/GuidUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/IniUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/MemUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml create mode 100644 src/libs/dutil/test/DUtilUnitTest/UnitTest.rc create mode 100644 src/libs/dutil/test/DUtilUnitTest/UriUtilTest.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/VerUtilTests.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/error.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/error.h create mode 100644 src/libs/dutil/test/DUtilUnitTest/packages.config create mode 100644 src/libs/dutil/test/DUtilUnitTest/precomp.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/precomp.h create mode 100644 src/libs/dutil/test/DUtilUnitTest/resource.h create mode 100644 src/signing.json delete mode 100644 src/test/DUtilUnitTest/ApupUtilTests.cpp delete mode 100644 src/test/DUtilUnitTest/AssemblyInfo.cpp delete mode 100644 src/test/DUtilUnitTest/DUtilTests.cpp delete mode 100644 src/test/DUtilUnitTest/DUtilUnitTest.vcxproj delete mode 100644 src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters delete mode 100644 src/test/DUtilUnitTest/DictUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/DirUtilTests.cpp delete mode 100644 src/test/DUtilUnitTest/FileUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/GuidUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/IniUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/MemUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/MonUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/PathUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/SceUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/StrUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml delete mode 100644 src/test/DUtilUnitTest/UnitTest.rc delete mode 100644 src/test/DUtilUnitTest/UriUtilTest.cpp delete mode 100644 src/test/DUtilUnitTest/VerUtilTests.cpp delete mode 100644 src/test/DUtilUnitTest/error.cpp delete mode 100644 src/test/DUtilUnitTest/error.h delete mode 100644 src/test/DUtilUnitTest/packages.config delete mode 100644 src/test/DUtilUnitTest/precomp.cpp delete mode 100644 src/test/DUtilUnitTest/precomp.h delete mode 100644 src/test/DUtilUnitTest/resource.h create mode 100644 src/version.json (limited to 'src') diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 00000000..1d72e683 --- /dev/null +++ b/src/.editorconfig @@ -0,0 +1,37 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\.editorconfig +# then update all of the repos. + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.{cs,vb}] +dotnet_sort_system_directives_first = true + +[*.cs] +csharp_indent_case_contents = true : error +csharp_indent_switch_labels = true : error +csharp_new_line_before_open_brace = all +csharp_prefer_braces = true : error +csharp_style_expression_bodied_methods = when_on_single_line : suggestion +csharp_style_expression_bodied_constructors = when_on_single_line : suggestion +csharp_style_expression_bodied_operators = when_on_single_line : suggestion +csharp_style_expression_bodied_properties = when_on_single_line : suggestion +csharp_style_expression_bodied_indexers = when_on_single_line : suggestion +csharp_style_expression_bodied_accessors = when_on_single_line : suggestion +csharp_style_var_elsewhere = true : suggestion +csharp_style_var_for_built_in_types = true : suggestion +csharp_style_var_when_type_is_apparent = true : suggestion +dotnet_style_qualification_for_event = true : error +dotnet_style_qualification_for_field = true : error +dotnet_style_qualification_for_method = true : error +dotnet_style_qualification_for_property = true : error + +[*.targets] +indent_size = 2 diff --git a/src/CustomizedNativeRecommendedRules.ruleset b/src/CustomizedNativeRecommendedRules.ruleset deleted file mode 100644 index 142b141c..00000000 --- a/src/CustomizedNativeRecommendedRules.ruleset +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props deleted file mode 100644 index fb34d54e..00000000 --- a/src/Directory.Build.props +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - Debug - false - - $(MSBuildProjectName) - $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) - $(BaseOutputPath)obj\$(ProjectName)\ - $(BaseOutputPath)$(Configuration)\ - - WiX Toolset Team - WiX Toolset - Copyright (c) .NET Foundation and contributors. All rights reserved. - MS-RL - WiX Toolset - - - - - diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets deleted file mode 100644 index 44701fb6..00000000 --- a/src/Directory.Build.targets +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - $(BaseOutputPath)obj\.tools - $(SigningToolFolder)\SignClient.exe - $(SigningToolFolder)\empty-filelist.txt - $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), signing.json))\signing.json - - - - false - $(OutputPath)\$(AssemblyName).xml - - - - - $(PrivateRepositoryUrl.Replace('.git','')) - - $(MSBuildProjectName).nuspec - $(MSBuildProjectDirectory) - $(NuspecProperties);Id=$(PackageId);Authors="$(Authors)";Configuration=$(Configuration);Copyright="$(Copyright)";Description="$(Description)";Title="$(Title)" - $(NuspecProperties);Version=$(NPMPackageVersion);RepositoryCommit=$(SourceRevisionId);RepositoryType=$(RepositoryType);RepositoryUrl=$(PrivateRepositoryUrl);ProjectFolder=$(MSBuildProjectDirectory)\;ProjectUrl=$(ProjectUrl) - true - snupkg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Directory.csproj.props b/src/Directory.csproj.props deleted file mode 100644 index 81d24ad1..00000000 --- a/src/Directory.csproj.props +++ /dev/null @@ -1,13 +0,0 @@ - - - - - true - true - $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk)) - false - - diff --git a/src/Directory.vcxproj.props b/src/Directory.vcxproj.props deleted file mode 100644 index 9ea7071b..00000000 --- a/src/Directory.vcxproj.props +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - Win32 - $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ - $(OutputPath)$(Platform)\ - - - $(Company) - $(Copyright) - - win-x86;win-x64;win-arm64 - native,Version=v0.0 - - - - $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) - - - - $(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset - - - - - $(DisableSpecificCompilerWarnings) - Level4 - $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) - Use - precomp.h - StdCall - true - false - -YlprecompDefine - /Zc:threadSafeInit- %(AdditionalOptions) - true - - - $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) - $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) - - - $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) - - - $(ProjectSubSystem) - $(ProjectModuleDefinitionFile) - $(ResourceOnlyDll) - true - $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) - $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) - /IGNORE:4099 %(AdditionalOptions) - - - - - - NoExtensions - - - - - CDecl - - - - - OldStyle - true - true - - - - - Disabled - EnableFastChecks - _DEBUG;DEBUG;%(PreprocessorDefinitions) - MultiThreadedDebug - - - - - - MultiThreadedDebugDll - - - - - MinSpace - NDEBUG;%(PreprocessorDefinitions) - true - true - MultiThreaded - - - true - true - - - - - - MultiThreadedDll - - - - - $(LinkKeyFile) - $(LinkDelaySign) - - - diff --git a/src/NativeMultiTargeting.Build.props b/src/NativeMultiTargeting.Build.props deleted file mode 100644 index 1ff46559..00000000 --- a/src/NativeMultiTargeting.Build.props +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - $(BaseIntermediateOutputPath)$(Configuration)\$(PlatformToolset)\$(PlatformTarget)\ - $(OutputPath)$(PlatformToolset)\$(PlatformTarget)\ - - diff --git a/src/dutil/acl2util.cpp b/src/dutil/acl2util.cpp deleted file mode 100644 index 598f12e7..00000000 --- a/src/dutil/acl2util.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define AclExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) -#define AclExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) -#define AclExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) -#define AclExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) -#define AclExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ACLUTIL, e, x, s, __VA_ARGS__) -#define AclExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ACLUTIL, g, x, s, __VA_ARGS__) - -/******************************************************************** -AclCalculateServiceSidString - gets the SID string for the given service name - -NOTE: psczSid should be freed with StrFree() -********************************************************************/ -extern "C" HRESULT DAPI AclCalculateServiceSidString( - __in LPCWSTR wzServiceName, - __in SIZE_T cchServiceName, - __deref_out_z LPWSTR* psczSid - ) -{ - // TODO: use undocumented RtlCreateServiceSid function? - // http://blogs.technet.com/b/voy/archive/2007/03/22/per-service-sid.aspx - // Assume little endian. - HRESULT hr = S_OK; - LPWSTR sczUpperServiceName = NULL; - DWORD cbHash = SHA1_HASH_LEN; - BYTE* pbHash = NULL; - - Assert(psczSid); - - if (0 == cchServiceName) - { - hr = ::StringCchLengthW(wzServiceName, STRSAFE_MAX_CCH, reinterpret_cast(&cchServiceName)); - AclExitOnFailure(hr, "Failed to get the length of the service name."); - } - - hr = StrAllocStringToUpperInvariant(&sczUpperServiceName, wzServiceName, cchServiceName); - AclExitOnFailure(hr, "Failed to upper case the service name."); - - pbHash = reinterpret_cast(MemAlloc(cbHash, TRUE)); - AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to allocate hash byte array."); - - hr = CrypHashBuffer(reinterpret_cast(sczUpperServiceName), cchServiceName * sizeof(WCHAR), PROV_RSA_FULL, CALG_SHA1, pbHash, cbHash); - AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to hash the service name."); - - hr = StrAllocFormatted(psczSid, L"S-1-5-80-%u-%u-%u-%u-%u", - MAKEDWORD(MAKEWORD(pbHash[0], pbHash[1]), MAKEWORD(pbHash[2], pbHash[3])), - MAKEDWORD(MAKEWORD(pbHash[4], pbHash[5]), MAKEWORD(pbHash[6], pbHash[7])), - MAKEDWORD(MAKEWORD(pbHash[8], pbHash[9]), MAKEWORD(pbHash[10], pbHash[11])), - MAKEDWORD(MAKEWORD(pbHash[12], pbHash[13]), MAKEWORD(pbHash[14], pbHash[15])), - MAKEDWORD(MAKEWORD(pbHash[16], pbHash[17]), MAKEWORD(pbHash[18], pbHash[19]))); - -LExit: - ReleaseMem(pbHash); - ReleaseStr(sczUpperServiceName); - - return hr; -} - - -/******************************************************************** -AclGetAccountSidStringEx - gets a string version of the account's SID - calculates a service's SID if lookup fails - -NOTE: psczSid should be freed with StrFree() -********************************************************************/ -extern "C" HRESULT DAPI AclGetAccountSidStringEx( - __in_z LPCWSTR wzSystem, - __in_z LPCWSTR wzAccount, - __deref_out_z LPWSTR* psczSid - ) -{ - HRESULT hr = S_OK; - SIZE_T cchAccount = 0; - PSID psid = NULL; - LPWSTR pwz = NULL; - LPWSTR sczSid = NULL; - - Assert(psczSid); - - hr = AclGetAccountSid(wzSystem, wzAccount, &psid); - if (SUCCEEDED(hr)) - { - Assert(::IsValidSid(psid)); - - if (!::ConvertSidToStringSidW(psid, &pwz)) - { - AclExitWithLastError(hr, "Failed to convert SID to string for Account: %ls", wzAccount); - } - - hr = StrAllocString(psczSid, pwz, 0); - } - else - { - if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr) - { - HRESULT hrLength = ::StringCchLengthW(wzAccount, STRSAFE_MAX_CCH, reinterpret_cast(&cchAccount)); - AclExitOnFailure(hrLength, "Failed to get the length of the account name."); - - if (11 < cchAccount && CSTR_EQUAL == CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"NT SERVICE\\", 11, wzAccount, 11)) - { - // If the service is not installed then LookupAccountName doesn't resolve the SID, but we can calculate it. - LPCWSTR wzServiceName = &wzAccount[11]; - hr = AclCalculateServiceSidString(wzServiceName, cchAccount - 11, &sczSid); - AclExitOnFailure(hr, "Failed to calculate the service SID for %ls", wzServiceName); - - *psczSid = sczSid; - sczSid = NULL; - } - } - AclExitOnFailure(hr, "Failed to get SID for account: %ls", wzAccount); - } - -LExit: - ReleaseStr(sczSid); - if (pwz) - { - ::LocalFree(pwz); - } - if (psid) - { - AclFreeSid(psid); - } - - return hr; -} diff --git a/src/dutil/aclutil.cpp b/src/dutil/aclutil.cpp deleted file mode 100644 index c9733033..00000000 --- a/src/dutil/aclutil.cpp +++ /dev/null @@ -1,1044 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define AclExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) -#define AclExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) -#define AclExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) -#define AclExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) -#define AclExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) -#define AclExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ACLUTIL, e, x, s, __VA_ARGS__) -#define AclExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ACLUTIL, g, x, s, __VA_ARGS__) - -/******************************************************************** -AclCheckAccess - determines if token has appropriate privileges - -NOTE: paa->fDenyAccess and paa->dwAccessMask are ignored and must be zero -if hToken is NULL, the thread will be checked -if hToken is not NULL the token must be an impersonation token -********************************************************************/ -extern "C" HRESULT DAPI AclCheckAccess( - __in HANDLE hToken, - __in ACL_ACCESS* paa - ) -{ - HRESULT hr = S_OK; - PSID psid = NULL; - BOOL fIsMember = FALSE; - - AclExitOnNull(paa, hr, E_INVALIDARG, "Failed to check ACL access, because no acl access provided to check"); - Assert(0 == paa->fDenyAccess && 0 == paa->dwAccessMask); - - if (paa->pwzAccountName) - { - hr = AclGetAccountSid(NULL, paa->pwzAccountName, &psid); - AclExitOnFailure(hr, "failed to get SID for account: %ls", paa->pwzAccountName); - } - else - { - if (!::AllocateAndInitializeSid(&paa->sia, paa->nSubAuthorityCount, paa->nSubAuthority[0], paa->nSubAuthority[1], paa->nSubAuthority[2], paa->nSubAuthority[3], paa->nSubAuthority[4], paa->nSubAuthority[5], paa->nSubAuthority[6], paa->nSubAuthority[7], &psid)) - { - AclExitWithLastError(hr, "failed to initialize SID"); - } - } - - if (!::CheckTokenMembership(hToken, psid, &fIsMember)) - { - AclExitWithLastError(hr, "failed to check membership"); - } - - fIsMember ? hr = S_OK : hr = S_FALSE; - -LExit: - if (psid) - { - ::FreeSid(psid); // TODO: does this have bad behavior if SID was allocated by Heap from AclGetAccountSid? - } - - return hr; -} - - -/******************************************************************** -AclCheckAdministratorAccess - determines if token has Administrator privileges - -NOTE: if hToken is NULL, the thread will be checked -if hToken is not NULL the token must be an impersonation token -********************************************************************/ -extern "C" HRESULT DAPI AclCheckAdministratorAccess( - __in HANDLE hToken - ) -{ - ACL_ACCESS aa; - SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; - - memset(&aa, 0, sizeof(aa)); - aa.sia = siaNt; - aa.nSubAuthorityCount = 2; - aa.nSubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID; - aa.nSubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS; - - return AclCheckAccess(hToken, &aa); -} - - -/******************************************************************** -AclCheckLocalSystemAccess - determines if token has LocalSystem privileges - -NOTE: if hToken is NULL, the thread will be checked -if hToken is not NULL the token must be an impersonation token -********************************************************************/ -extern "C" HRESULT DAPI AclCheckLocalSystemAccess( - __in HANDLE hToken - ) -{ - ACL_ACCESS aa; - SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; - - memset(&aa, 0, sizeof(aa)); - aa.sia = siaNt; - aa.nSubAuthorityCount = 1; - aa.nSubAuthority[0] = SECURITY_LOCAL_SYSTEM_RID; - - return AclCheckAccess(hToken, &aa); -} - - -/******************************************************************** -AclGetWellKnownSid - returns a SID for the specified account - -********************************************************************/ -extern "C" HRESULT DAPI AclGetWellKnownSid( - __in WELL_KNOWN_SID_TYPE wkst, - __deref_out PSID* ppsid - ) -{ - Assert(ppsid); - - HRESULT hr = S_OK;; - PSID psid = NULL; - DWORD cbSid = SECURITY_MAX_SID_SIZE; - - PSID psidTemp = NULL; -#if(_WIN32_WINNT < 0x0501) - SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY; - SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY; - SID_IDENTIFIER_AUTHORITY siaCreator = SECURITY_CREATOR_SID_AUTHORITY; - BOOL fSuccess = FALSE; -#endif - - // - // allocate memory for the SID and get it - // - psid = static_cast(MemAlloc(cbSid, TRUE)); - AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed allocate memory for well known SID"); - -#if(_WIN32_WINNT < 0x0501) - switch (wkst) - { - case WinWorldSid: // Everyone - fSuccess = ::AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinAuthenticatedUserSid: // Authenticated Users - fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_AUTHENTICATED_USER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinLocalSystemSid: // LocalSystem - fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinLocalServiceSid: // LocalService - fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinNetworkServiceSid: // NetworkService - fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_NETWORK_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinBuiltinGuestsSid: // Guests - fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinBuiltinAdministratorsSid: // Administrators - fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinBuiltinUsersSid: // Users - fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinCreatorOwnerSid: //CREATOR OWNER - fSuccess = ::AllocateAndInitializeSid(&siaCreator, 1, SECURITY_CREATOR_OWNER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - case WinInteractiveSid: // INTERACTIVE - fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_INTERACTIVE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); - break; - default: - hr = E_INVALIDARG; - AclExitOnFailure(hr, "unknown well known SID: %d", wkst); - } - - if (!fSuccess) - AclExitOnLastError(hr, "failed to allocate well known SID: %d", wkst); - - if (!::CopySid(cbSid, psid, psidTemp)) - AclExitOnLastError(hr, "failed to create well known SID: %d", wkst); -#else - Assert(NULL == psidTemp); - if (!::CreateWellKnownSid(wkst, NULL, psid, &cbSid)) - { - AclExitWithLastError(hr, "failed to create well known SID: %d", wkst); - } -#endif - - *ppsid = psid; - psid = NULL; // null it here so it won't be released below - - Assert(S_OK == hr && ::IsValidSid(*ppsid)); -LExit: - if (psidTemp) - { - ::FreeSid(psidTemp); - } - - ReleaseMem(psid); - - return hr; -} - - -/******************************************************************** -AclGetAccountSid - returns a SID for the specified account - -********************************************************************/ -extern "C" HRESULT DAPI AclGetAccountSid( - __in_opt LPCWSTR wzSystem, - __in_z LPCWSTR wzAccount, - __deref_out PSID* ppsid - ) -{ - Assert(wzAccount && *wzAccount && ppsid); - - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - PSID psid = NULL; - DWORD cbSid = SECURITY_MAX_SID_SIZE; - LPWSTR pwzDomainName = NULL; - DWORD cbDomainName = 255; - SID_NAME_USE peUse; - - // - // allocate memory for the SID and domain name - // - psid = static_cast(MemAlloc(cbSid, TRUE)); - AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed to allocate memory for SID"); - hr = StrAlloc(&pwzDomainName, cbDomainName); - AclExitOnFailure(hr, "failed to allocate string for domain name"); - - // - // try to lookup the account now - // - if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse)) - { - // if one of the buffers wasn't large enough - er = ::GetLastError(); - if (ERROR_INSUFFICIENT_BUFFER == er) - { - if (SECURITY_MAX_SID_SIZE < cbSid) - { - PSID psidNew = static_cast(MemReAlloc(psid, cbSid, TRUE)); - AclExitOnNullWithLastError(psidNew, hr, "failed to allocate memory for account: %ls", wzAccount); - - psid = psidNew; - } - if (255 < cbDomainName) - { - hr = StrAlloc(&pwzDomainName, cbDomainName); - AclExitOnFailure(hr, "failed to allocate string for domain name"); - } - - if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse)) - { - AclExitWithLastError(hr, "failed to lookup account: %ls", wzAccount); - } - } - else - { - AclExitOnWin32Error(er, hr, "failed to lookup account: %ls", wzAccount); - } - } - - *ppsid = psid; - psid = NULL; - - hr = S_OK; -LExit: - ReleaseStr(pwzDomainName); - ReleaseMem(psid); - - return hr; -} - - -/******************************************************************** -AclGetAccountSidString - gets a string version of the user's SID - -NOTE: ppwzSid should be freed with StrFree() -********************************************************************/ -extern "C" HRESULT DAPI AclGetAccountSidString( - __in_z LPCWSTR wzSystem, - __in_z LPCWSTR wzAccount, - __deref_out_z LPWSTR* ppwzSid - ) -{ - Assert(ppwzSid); - HRESULT hr = S_OK; - PSID psid = NULL; - LPWSTR pwz = NULL; - - *ppwzSid = NULL; - - hr = AclGetAccountSid(wzSystem, wzAccount, &psid); - AclExitOnFailure(hr, "failed to get SID for account: %ls", wzAccount); - Assert(::IsValidSid(psid)); - - if (!::ConvertSidToStringSidW(psid, &pwz)) - { - AclExitWithLastError(hr, "failed to convert SID to string for Account: %ls", wzAccount); - } - - hr = StrAllocString(ppwzSid, pwz, 0); - -LExit: - if (FAILED(hr)) - { - ReleaseNullStr(*ppwzSid); - } - - if (pwz) - { - ::LocalFree(pwz); - } - - if (psid) - { - AclFreeSid(psid); - } - - return hr; -} - - -/******************************************************************** -AclCreateDacl - creates a DACL from ACL_ACE structures - -********************************************************************/ -extern "C" HRESULT DAPI AclCreateDacl( - __in_ecount(cDeny) ACL_ACE rgaaDeny[], - __in DWORD cDeny, - __in_ecount(cAllow) ACL_ACE rgaaAllow[], - __in DWORD cAllow, - __deref_out ACL** ppAcl - ) -{ - Assert(ppAcl); - HRESULT hr = S_OK; - ACL* pAcl = NULL; - DWORD cbAcl = 0; - DWORD i; - - *ppAcl = NULL; - - // initialize the ACL - cbAcl = sizeof(ACL); - for (i = 0; i < cDeny; ++i) - { - cbAcl += sizeof(ACCESS_DENIED_ACE) + ::GetLengthSid(rgaaDeny[i].psid) - sizeof(DWORD); - } - - for (i = 0; i < cAllow; ++i) - { - cbAcl += sizeof(ACCESS_ALLOWED_ACE) + ::GetLengthSid(rgaaAllow[i].psid) - sizeof(DWORD); - } - - pAcl = static_cast(MemAlloc(cbAcl, TRUE)); - AclExitOnNull(pAcl, hr, E_OUTOFMEMORY, "failed to allocate ACL"); - -#pragma prefast(push) -#pragma prefast(disable:25029) - if (!::InitializeAcl(pAcl, cbAcl, ACL_REVISION)) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to initialize ACL"); - } - - // add in the ACEs (denied first) - for (i = 0; i < cDeny; ++i) - { -#pragma prefast(push) -#pragma prefast(disable:25029) - if (!::AddAccessDeniedAceEx(pAcl, ACL_REVISION, rgaaDeny[i].dwFlags, rgaaDeny[i].dwMask, rgaaDeny[i].psid)) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to add access denied ACE #%d to ACL", i); - } - } - for (i = 0; i < cAllow; ++i) - { -#pragma prefast(push) -#pragma prefast(disable:25029) - if (!::AddAccessAllowedAceEx(pAcl, ACL_REVISION, rgaaAllow[i].dwFlags, rgaaAllow[i].dwMask, rgaaAllow[i].psid)) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to add access allowed ACE #%d to ACL", i); - } - } - - *ppAcl = pAcl; - pAcl = NULL; - AssertSz(::IsValidAcl(*ppAcl), "AclCreateDacl() - created invalid ACL"); - Assert(S_OK == hr); -LExit: - if (pAcl) - { - AclFreeDacl(pAcl); - } - - return hr; -} - - -/******************************************************************** -AclAddToDacl - creates a new DACL from an ACL plus new ACL_ACE structure - -********************************************************************/ -extern "C" HRESULT DAPI AclAddToDacl( - __in ACL* pAcl, - __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[], - __in DWORD cDeny, - __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[], - __in DWORD cAllow, - __deref_out ACL** ppAclNew - ) -{ - Assert(pAcl && ::IsValidAcl(pAcl) && ppAclNew); - HRESULT hr = S_OK; - - ACL_SIZE_INFORMATION asi; - ACL_ACE* paaNewDeny = NULL; - DWORD cNewDeny = 0; - ACL_ACE* paaNewAllow = NULL; - DWORD cNewAllow = 0; - - ACCESS_ALLOWED_ACE* paaa; - ACCESS_DENIED_ACE* pada; - DWORD i; - - // allocate memory for all the new ACEs (NOTE: this over calculates the memory necessary, but that's okay) - if (!::GetAclInformation(pAcl, &asi, sizeof(asi), AclSizeInformation)) - { - AclExitWithLastError(hr, "failed to get information about original ACL"); - } - - if ((asi.AceCount + cDeny) < asi.AceCount || // check for overflow - (asi.AceCount + cDeny) < cDeny || // check for overflow - (asi.AceCount + cDeny) >= MAXSIZE_T / sizeof(ACL_ACE)) - { - hr = E_OUTOFMEMORY; - AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cDeny)); - } - - paaNewDeny = static_cast(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cDeny), TRUE)); - AclExitOnNull(paaNewDeny, hr, E_OUTOFMEMORY, "failed to allocate memory for new deny ACEs"); - - if ((asi.AceCount + cAllow) < asi.AceCount || // check for overflow - (asi.AceCount + cAllow) < cAllow || // check for overflow - (asi.AceCount + cAllow) >= MAXSIZE_T / sizeof(ACL_ACE)) - { - hr = E_OUTOFMEMORY; - AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cAllow)); - } - - paaNewAllow = static_cast(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cAllow), TRUE)); - AclExitOnNull(paaNewAllow, hr, E_OUTOFMEMORY, "failed to allocate memory for new allow ACEs"); - - // fill in the new structures with old data then new data (denied first) - for (i = 0; i < asi.AceCount; ++i) - { - if (!::GetAce(pAcl, i, reinterpret_cast(&pada))) - { - AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i); - } - - if (ACCESS_DENIED_ACE_TYPE != pada->Header.AceType) - { - continue; // skip non-denied aces - } - - paaNewDeny[i].dwFlags = pada->Header.AceFlags; - paaNewDeny[i].dwMask = pada->Mask; - paaNewDeny[i].psid = reinterpret_cast(&(pada->SidStart)); - ++cNewDeny; - } - - memcpy(paaNewDeny + cNewDeny, rgaaDeny, sizeof(ACL_ACE) * cDeny); - cNewDeny += cDeny; - - - for (i = 0; i < asi.AceCount; ++i) - { - if (!::GetAce(pAcl, i, reinterpret_cast(&paaa))) - { - AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i); - } - - if (ACCESS_ALLOWED_ACE_TYPE != paaa->Header.AceType) - { - continue; // skip non-allowed aces - } - - paaNewAllow[i].dwFlags = paaa->Header.AceFlags; - paaNewAllow[i].dwMask = paaa->Mask; - paaNewAllow[i].psid = reinterpret_cast(&(paaa->SidStart)); - ++cNewAllow; - } - - memcpy(paaNewAllow + cNewAllow, rgaaAllow, sizeof(ACL_ACE) * cAllow); - cNewAllow += cAllow; - - // create the dacl with the new - hr = AclCreateDacl(paaNewDeny, cNewDeny, paaNewAllow, cNewAllow, ppAclNew); - AclExitOnFailure(hr, "failed to create new ACL from existing ACL"); - - AssertSz(::IsValidAcl(*ppAclNew), "AclAddToDacl() - created invalid ACL"); - Assert(S_OK == hr); -LExit: - ReleaseMem(paaNewAllow); - ReleaseMem(paaNewDeny); - - return hr; -} - - -/******************************************************************** -AclMergeDacls - creates a new DACL from two existing ACLs - -********************************************************************/ -extern "C" HRESULT DAPI AclMergeDacls( - __in const ACL* pAcl1, - __in const ACL* pAcl2, - __deref_out ACL** ppAclNew - ) -{ - HRESULT hr = E_NOTIMPL; - - Assert(pAcl1 && pAcl2 && ppAclNew); - UNREFERENCED_PARAMETER(pAcl1); - UNREFERENCED_PARAMETER(pAcl2); - UNREFERENCED_PARAMETER(ppAclNew); - -//LExit: - return hr; -} - - -/******************************************************************** -AclCreateDaclOld - creates a DACL from an ACL_ACCESS structure - -********************************************************************/ -extern "C" HRESULT DAPI AclCreateDaclOld( - __in_ecount(cAclAccesses) ACL_ACCESS* paa, - __in DWORD cAclAccesses, - __deref_out ACL** ppACL - ) -{ - Assert(ppACL); - HRESULT hr = S_OK; - DWORD* pdwAccessMask = NULL; - PSID* ppsid = NULL; - - DWORD i; - int cbAcl; - - *ppACL = NULL; - - // - // create the SIDs and calculate the space for the ACL - // - pdwAccessMask = static_cast(MemAlloc(sizeof(DWORD) * cAclAccesses, TRUE)); - AclExitOnNull(pdwAccessMask, hr, E_OUTOFMEMORY, "failed allocate memory for access mask"); - ppsid = static_cast(MemAlloc(sizeof(PSID) * cAclAccesses, TRUE)); - AclExitOnNull(ppsid, hr, E_OUTOFMEMORY, "failed allocate memory for sid"); - - cbAcl = sizeof (ACL); // start with the size of the header - for (i = 0; i < cAclAccesses; ++i) - { - if (paa[i].pwzAccountName) - { - hr = AclGetAccountSid(NULL, paa[i].pwzAccountName, ppsid + i); - AclExitOnFailure(hr, "failed to get SID for account: %ls", paa[i].pwzAccountName); - } - else - { - if ((!::AllocateAndInitializeSid(&paa[i].sia, paa[i].nSubAuthorityCount, - paa[i].nSubAuthority[0], paa[i].nSubAuthority[1], - paa[i].nSubAuthority[2], paa[i].nSubAuthority[3], - paa[i].nSubAuthority[4], paa[i].nSubAuthority[5], - paa[i].nSubAuthority[6], paa[i].nSubAuthority[7], - (void**)(ppsid + i)))) - { - AclExitWithLastError(hr, "failed to initialize SIDs #%u", i); - } - } - - // add the newly allocated SID size to the count of bytes for this ACL - cbAcl +=::GetLengthSid(*(ppsid + i)) - sizeof(DWORD); - if (paa[i].fDenyAccess) - { - cbAcl += sizeof(ACCESS_DENIED_ACE); - } - else - { - cbAcl += sizeof(ACCESS_ALLOWED_ACE); - } - - pdwAccessMask[i] = paa[i].dwAccessMask; - } - - // - // allocate the ACL and set the appropriate ACEs - // - *ppACL = static_cast(MemAlloc(cbAcl, FALSE)); - AclExitOnNull(*ppACL, hr, E_OUTOFMEMORY, "failed allocate memory for ACL"); - -#pragma prefast(push) -#pragma prefast(disable:25029) - if (!::InitializeAcl(*ppACL, cbAcl, ACL_REVISION)) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to initialize ACLs"); - } - - // add an access-allowed ACE for each of the SIDs - for (i = 0; i < cAclAccesses; ++i) - { - if (paa[i].fDenyAccess) - { -#pragma prefast(push) -#pragma prefast(disable:25029) - if (!::AddAccessDeniedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i))) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to add access denied for ACE"); - } - } - else - { -#pragma prefast(push) -#pragma prefast(disable:25029) - if (!::AddAccessAllowedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i))) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to add access allowed for ACE"); - } - } - } - -LExit: - if (FAILED(hr)) - { - ReleaseNullMem(*ppACL); - } - - if (ppsid) - { - for (i = 0; i < cAclAccesses; ++i) - { - if (ppsid[i]) - { - ::FreeSid(ppsid[i]); - } - } - - MemFree(ppsid); - } - - ReleaseMem(pdwAccessMask); - - return hr; -} - - -/******************************************************************** -AclCreateSecurityDescriptorFromDacl - creates a self-relative security -descriptor from an existing DACL - -********************************************************************/ -extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromDacl( - __in ACL* pACL, - __deref_out SECURITY_DESCRIPTOR** ppsd - ) -{ - HRESULT hr = S_OK; - - SECURITY_DESCRIPTOR sd; - DWORD cbSD; - - AclExitOnNull(pACL, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no DACL was provided"); - AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no output object was provided"); - - *ppsd = NULL; - - // - // create the absolute security descriptor - // - - // initialize our security descriptor, throw the ACL into it, and set the owner -#pragma prefast(push) -#pragma prefast(disable:25028) // We only call this when pACL isn't NULL, so this call is safe according to the docs -#pragma prefast(disable:25029) - if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) || - (!::SetSecurityDescriptorDacl(&sd, TRUE, pACL, FALSE)) || - (!::SetSecurityDescriptorOwner(&sd, NULL, FALSE))) -#pragma prefast(pop) - { - AclExitWithLastError(hr, "failed to initialize security descriptor"); - } - - // - // create the self-relative security descriptor - // - cbSD = ::GetSecurityDescriptorLength(&sd); - *ppsd = static_cast(MemAlloc(cbSD, FALSE)); - AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); - - ::MakeSelfRelativeSD(&sd, (BYTE*)*ppsd, &cbSD); - Assert(::IsValidSecurityDescriptor(*ppsd)); - -LExit: - if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) - { - MemFree(*ppsd); - *ppsd = NULL; - } - - return hr; -} - - -/******************************************************************** -AclCreateSecurityDescriptor - creates a self-relative security descriptor from an -ACL_ACCESS structure - -NOTE: ppsd should be freed with AclFreeSecurityDescriptor() -********************************************************************/ -extern "C" HRESULT DAPI AclCreateSecurityDescriptor( - __in_ecount(cAclAccesses) ACL_ACCESS* paa, - __in DWORD cAclAccesses, - __deref_out SECURITY_DESCRIPTOR** ppsd - ) -{ - Assert(ppsd); - HRESULT hr = S_OK; - - ACL* pACL; - - *ppsd = NULL; - - // - // create the DACL - // - hr = AclCreateDaclOld(paa, cAclAccesses, &pACL); - AclExitOnFailure(hr, "failed to create DACL for security descriptor"); - - // - // create self-relative security descriptor - // - hr = AclCreateSecurityDescriptorFromDacl(pACL, ppsd); - -LExit: - return hr; -} - - -/******************************************************************** -AclCreateSecurityDescriptorFromString - creates a self-relative security -descriptor from an SDDL string - -NOTE: ppsd should be freed with AclFreeSecurityDescriptor() -********************************************************************/ -extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromString( - __deref_out SECURITY_DESCRIPTOR** ppsd, - __in_z __format_string LPCWSTR wzSddlFormat, - ... - ) -{ - Assert(ppsd); - HRESULT hr = S_OK; - LPWSTR pwzSddl = NULL; - va_list args; - PSECURITY_DESCRIPTOR psd = NULL; - DWORD cbSD = 0; - - *ppsd = NULL; - - va_start(args, wzSddlFormat); - hr = StrAllocFormattedArgs(&pwzSddl, wzSddlFormat, args); - va_end(args); - AclExitOnFailure(hr, "failed to create SDDL string for format: %ls", wzSddlFormat); - - if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSddl, SDDL_REVISION_1, &psd, &cbSD)) - { - AclExitWithLastError(hr, "failed to create security descriptor from SDDL: %ls", pwzSddl); - } - - *ppsd = static_cast(MemAlloc(cbSD, FALSE)); - AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed to allocate memory for security descriptor"); - - memcpy(*ppsd, psd, cbSD); - Assert(::IsValidSecurityDescriptor(*ppsd)); - - Assert(S_OK == hr); - -LExit: - if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) - { - MemFree(*ppsd); - *ppsd = NULL; - } - - if (psd) - { - ::LocalFree(psd); - } - - ReleaseStr(pwzSddl); - return hr; -} - - -/******************************************************************** -AclDuplicateSecurityDescriptor - creates a copy of a self-relative security descriptor - -NOTE: passed in security descriptor must be in self-relative format -********************************************************************/ -extern "C" HRESULT DAPI AclDuplicateSecurityDescriptor( - __in SECURITY_DESCRIPTOR* psd, - __deref_out SECURITY_DESCRIPTOR** ppsd - ) -{ - HRESULT hr = S_OK; - DWORD cbSD; - - AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get duplicate ACL security descriptor because no place to output was provided"); - *ppsd = NULL; - - // - // create the self-relative security descriptor - // - cbSD = ::GetSecurityDescriptorLength(psd); - *ppsd = static_cast(MemAlloc(cbSD, 0)); - AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); - - memcpy(*ppsd, psd, cbSD); - Assert(::IsValidSecurityDescriptor(*ppsd)); - -LExit: - if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) - { - MemFree(*ppsd); - *ppsd = NULL; - } - - return hr; -} - - -/******************************************************************** -AclGetSecurityDescriptor - returns self-relative security descriptor for named object - -NOTE: free ppsd with AclFreeSecurityDescriptor() -********************************************************************/ -extern "C" HRESULT DAPI AclGetSecurityDescriptor( - __in_z LPCWSTR wzObject, - __in SE_OBJECT_TYPE sot, - __in SECURITY_INFORMATION securityInformation, - __deref_out SECURITY_DESCRIPTOR** ppsd - ) -{ - HRESULT hr = S_OK; - DWORD er; - PSECURITY_DESCRIPTOR psd = NULL; - DWORD cbSD; - - AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get ACL Security Descriptor because no place to output was provided"); - *ppsd = NULL; - - // get the security descriptor for the object - er = ::GetNamedSecurityInfoW(const_cast(wzObject), sot, securityInformation, NULL, NULL, NULL, NULL, &psd); - AclExitOnWin32Error(er, hr, "failed to get security info from object: %ls", wzObject); - Assert(::IsValidSecurityDescriptor(psd)); - - // copy the self-relative security descriptor - cbSD = ::GetSecurityDescriptorLength(psd); - *ppsd = static_cast(MemAlloc(cbSD, 0)); - AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); - - memcpy(*ppsd, psd, cbSD); - Assert(::IsValidSecurityDescriptor(*ppsd)); - -LExit: - if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) - { - MemFree(*ppsd); - *ppsd = NULL; - } - - if (psd) - { - ::LocalFree(psd); - } - - return hr; -} - - -extern "C" HRESULT DAPI AclSetSecurityWithRetry( - __in_z LPCWSTR wzObject, - __in SE_OBJECT_TYPE sot, - __in SECURITY_INFORMATION securityInformation, - __in_opt PSID psidOwner, - __in_opt PSID psidGroup, - __in_opt PACL pDacl, - __in_opt PACL pSacl, - __in DWORD cRetry, - __in DWORD dwWaitMilliseconds - ) -{ - HRESULT hr = S_OK; - LPWSTR sczObject = NULL; - DWORD i = 0; - - hr = StrAllocString(&sczObject, wzObject, 0); - AclExitOnFailure(hr, "Failed to copy object to secure."); - - hr = E_FAIL; - for (i = 0; FAILED(hr) && i <= cRetry; ++i) - { - if (0 < i) - { - ::Sleep(dwWaitMilliseconds); - } - - DWORD er = ::SetNamedSecurityInfoW(sczObject, sot, securityInformation, psidOwner, psidGroup, pDacl, pSacl); - hr = HRESULT_FROM_WIN32(er); - } - AclExitOnRootFailure(hr, "Failed to set security on object '%ls' after %u retries.", wzObject, i); - -LExit: - ReleaseStr(sczObject); - - return hr; -} - - -/******************************************************************** -AclFreeSid - frees a SID created by any Acl* functions - -********************************************************************/ -extern "C" HRESULT DAPI AclFreeSid( - __in PSID psid - ) -{ - Assert(psid && ::IsValidSid(psid)); - HRESULT hr = S_OK; - - hr = MemFree(psid); - - return hr; -} - - -/******************************************************************** -AclFreeDacl - frees a DACL created by any Acl* functions - -********************************************************************/ -extern "C" HRESULT DAPI AclFreeDacl( - __in ACL* pACL - ) -{ - Assert(pACL); - HRESULT hr = S_OK; - - hr = MemFree(pACL); - - return hr; -} - - -/******************************************************************** -AclFreeSecurityDescriptor - frees a security descriptor created by any Acl* functions - -********************************************************************/ -extern "C" HRESULT DAPI AclFreeSecurityDescriptor( - __in SECURITY_DESCRIPTOR* psd - ) -{ - Assert(psd && ::IsValidSecurityDescriptor(psd)); - HRESULT hr = S_OK; - - hr = MemFree(psd); - - return hr; -} - - -/******************************************************************** -AclAddAdminToSecurityDescriptor - Adds the Administrators group to a security descriptor - -********************************************************************/ -extern "C" HRESULT DAPI AclAddAdminToSecurityDescriptor( - __in SECURITY_DESCRIPTOR* pSecurity, - __deref_out SECURITY_DESCRIPTOR** ppSecurityNew - ) -{ - HRESULT hr = S_OK; - PACL pAcl = NULL; - PACL pAclNew = NULL; - BOOL fValid, fDaclDefaulted; - ACL_ACE ace[1]; - SECURITY_DESCRIPTOR* pSecurityNew; - - if (!::GetSecurityDescriptorDacl(pSecurity, &fValid, &pAcl, &fDaclDefaulted) || !fValid) - { - AclExitOnLastError(hr, "Failed to get acl from security descriptor"); - } - - hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &ace[0].psid); - AclExitOnFailure(hr, "failed to get sid for Administrators group"); - - ace[0].dwFlags = NO_PROPAGATE_INHERIT_ACE; - ace[0].dwMask = GENERIC_ALL; - - hr = AclAddToDacl(pAcl, NULL, 0, ace, 1, &pAclNew); - AclExitOnFailure(hr, "failed to add Administrators ACE to ACL"); - - hr = AclCreateSecurityDescriptorFromDacl(pAclNew, &pSecurityNew); - AclExitOnLastError(hr, "Failed to create new security descriptor"); - - // The DACL is referenced by, not copied into, the security descriptor. Make sure not to free it. - pAclNew = NULL; - - *ppSecurityNew = pSecurityNew; - -LExit: - if (pAclNew) - { - AclFreeDacl(pAclNew); - } - if (ace[0].psid) - { - AclFreeSid(ace[0].psid); - } - - return hr; -} diff --git a/src/dutil/apputil.cpp b/src/dutil/apputil.cpp deleted file mode 100644 index 589a09dd..00000000 --- a/src/dutil/apputil.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define AppExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) -#define AppExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) -#define AppExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) -#define AppExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) -#define AppExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) -#define AppExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) -#define AppExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__) -#define AppExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__) -#define AppExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__) -#define AppExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__) -#define AppExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_APPUTIL, e, x, s, __VA_ARGS__) -#define AppExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_APPUTIL, g, x, s, __VA_ARGS__) - -const DWORD PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; -typedef BOOL(WINAPI *LPFN_SETDEFAULTDLLDIRECTORIES)(DWORD); -typedef BOOL(WINAPI *LPFN_SETDLLDIRECTORYW)(LPCWSTR); - -extern "C" void DAPI AppFreeCommandLineArgs( - __in LPWSTR* argv - ) -{ - // The "ignored" hack in AppParseCommandLine requires an adjustment. - LPWSTR* argvOriginal = argv - 1; - ::LocalFree(argvOriginal); -} - -/******************************************************************** -AppInitialize - initializes the standard safety precautions for an - installation application. - -********************************************************************/ -extern "C" void DAPI AppInitialize( - __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[], - __in DWORD cSafelyLoadSystemDlls - ) -{ - HRESULT hr = S_OK; - HMODULE hIgnored = NULL; - BOOL fSetDefaultDllDirectories = FALSE; - - ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); - - // Best effort call to initialize default DLL directories to system only. - HMODULE hKernel32 = ::GetModuleHandleW(L"kernel32"); - Assert(hKernel32); - LPFN_SETDEFAULTDLLDIRECTORIES pfnSetDefaultDllDirectories = (LPFN_SETDEFAULTDLLDIRECTORIES)::GetProcAddress(hKernel32, "SetDefaultDllDirectories"); - if (pfnSetDefaultDllDirectories) - { - if (pfnSetDefaultDllDirectories(PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32)) - { - fSetDefaultDllDirectories = TRUE; - } - else - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - TraceError(hr, "Failed to call SetDefaultDllDirectories."); - } - } - - // Only need to safely load if the default DLL directories was not - // able to be set. - if (!fSetDefaultDllDirectories) - { - // Remove current working directory from search order. - LPFN_SETDLLDIRECTORYW pfnSetDllDirectory = (LPFN_SETDLLDIRECTORYW)::GetProcAddress(hKernel32, "SetDllDirectoryW"); - if (!pfnSetDllDirectory || !pfnSetDllDirectory(L"")) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - TraceError(hr, "Failed to call SetDllDirectory."); - } - - for (DWORD i = 0; i < cSafelyLoadSystemDlls; ++i) - { - hr = LoadSystemLibrary(rgsczSafelyLoadSystemDlls[i], &hIgnored); - if (FAILED(hr)) - { - TraceError(hr, "Failed to safety load: %ls", rgsczSafelyLoadSystemDlls[i]); - } - } - } -} - -extern "C" void DAPI AppInitializeUnsafe() -{ - ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); -} - -extern "C" DAPI_(HRESULT) AppParseCommandLine( - __in LPCWSTR wzCommandLine, - __in int* pArgc, - __in LPWSTR** pArgv - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCommandLine = NULL; - LPWSTR* argv = NULL; - int argc = 0; - - // CommandLineToArgvW tries to treat the first argument as the path to the process, - // which fails pretty miserably if your first argument is something like - // FOO="C:\Program Files\My Company". So give it something harmless to play with. - hr = StrAllocConcat(&sczCommandLine, L"ignored ", 0); - AppExitOnFailure(hr, "Failed to initialize command line."); - - hr = StrAllocConcat(&sczCommandLine, wzCommandLine, 0); - AppExitOnFailure(hr, "Failed to copy command line."); - - argv = ::CommandLineToArgvW(sczCommandLine, &argc); - AppExitOnNullWithLastError(argv, hr, "Failed to parse command line."); - - // Skip "ignored" argument/hack. - *pArgv = argv + 1; - *pArgc = argc - 1; - -LExit: - ReleaseStr(sczCommandLine); - - return hr; -} diff --git a/src/dutil/apuputil.cpp b/src/dutil/apuputil.cpp deleted file mode 100644 index eb96d515..00000000 --- a/src/dutil/apuputil.cpp +++ /dev/null @@ -1,700 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define ApupExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) -#define ApupExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) -#define ApupExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) -#define ApupExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) -#define ApupExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) -#define ApupExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) -#define ApupExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_APUPUTIL, p, x, e, s, __VA_ARGS__) -#define ApupExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, p, x, s, __VA_ARGS__) -#define ApupExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_APUPUTIL, p, x, e, s, __VA_ARGS__) -#define ApupExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, p, x, s, __VA_ARGS__) -#define ApupExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_APUPUTIL, e, x, s, __VA_ARGS__) -#define ApupExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_APUPUTIL, g, x, s, __VA_ARGS__) - -// prototypes -static HRESULT ProcessEntry( - __in ATOM_ENTRY* pAtomEntry, - __in LPCWSTR wzDefaultAppId, - __inout APPLICATION_UPDATE_ENTRY* pApupEntry - ); -static HRESULT ParseEnclosure( - __in ATOM_LINK* pLink, - __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure - ); -static __callback int __cdecl CompareEntries( - void* pvContext, - const void* pvLeft, - const void* pvRight - ); -static HRESULT FilterEntries( - __in APPLICATION_UPDATE_ENTRY* rgEntries, - __in DWORD cEntries, - __in VERUTIL_VERSION* pCurrentVersion, - __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries, - __inout DWORD* pcFilteredEntries - ); -static HRESULT CopyEntry( - __in const APPLICATION_UPDATE_ENTRY* pSrc, - __in APPLICATION_UPDATE_ENTRY* pDest - ); -static HRESULT CopyEnclosure( - __in const APPLICATION_UPDATE_ENCLOSURE* pSrc, - __in APPLICATION_UPDATE_ENCLOSURE* pDest - ); -static void FreeEntry( - __in APPLICATION_UPDATE_ENTRY* pApupEntry - ); -static void FreeEnclosure( - __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure - ); - - -// -// ApupCalculateChainFromAtom - returns the chain of application updates found in an ATOM feed. -// -extern "C" HRESULT DAPI ApupAllocChainFromAtom( - __in ATOM_FEED* pFeed, - __out APPLICATION_UPDATE_CHAIN** ppChain - ) -{ - HRESULT hr = S_OK; - APPLICATION_UPDATE_CHAIN* pChain = NULL; - - pChain = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE)); - - // First search the ATOM feed's custom elements to try and find the default application identity. - for (ATOM_UNKNOWN_ELEMENT* pElement = pFeed->pUnknownElements; pElement; pElement = pElement->pNext) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1)) - { - hr = StrAllocString(&pChain->wzDefaultApplicationId, pElement->wzValue, 0); - ApupExitOnFailure(hr, "Failed to allocate default application id."); - - for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1)) - { - hr = StrAllocString(&pChain->wzDefaultApplicationType, pAttribute->wzValue, 0); - ApupExitOnFailure(hr, "Failed to allocate default application type."); - } - } - } - } - } - - // Assume there will be as many application updates entries as their are feed entries. - if (pFeed->cEntries) - { - pChain->rgEntries = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_ENTRY) * pFeed->cEntries, TRUE)); - ApupExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to allocate memory for update entries."); - - // Process each entry, building up the chain. - for (DWORD i = 0; i < pFeed->cEntries; ++i) - { - hr = ProcessEntry(pFeed->rgEntries + i, pChain->wzDefaultApplicationId, pChain->rgEntries + pChain->cEntries); - ApupExitOnFailure(hr, "Failed to process ATOM entry."); - - if (S_FALSE != hr) - { - ++pChain->cEntries; - } - } - - // Sort the chain by descending version and ascending total size. - qsort_s(pChain->rgEntries, pChain->cEntries, sizeof(APPLICATION_UPDATE_ENTRY), CompareEntries, NULL); - } - - // Trim the unused entries from the end, if any of the entries failed to parse or validate - if (pChain->cEntries != pFeed->cEntries) - { - if (pChain->cEntries > 0) - { - pChain->rgEntries = static_cast(MemReAlloc(pChain->rgEntries, sizeof(APPLICATION_UPDATE_ENTRY) * pChain->cEntries, FALSE)); - ApupExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to reallocate memory for update entries."); - } - else - { - ReleaseNullMem(pChain->rgEntries); - } - } - - *ppChain = pChain; - pChain = NULL; - -LExit: - ReleaseApupChain(pChain); - - return hr; -} - - -// -// ApupFilterChain - remove the unneeded update elements from the chain. -// -HRESULT DAPI ApupFilterChain( - __in APPLICATION_UPDATE_CHAIN* pChain, - __in VERUTIL_VERSION* pVersion, - __out APPLICATION_UPDATE_CHAIN** ppFilteredChain - ) -{ - HRESULT hr = S_OK; - APPLICATION_UPDATE_CHAIN* pNewChain = NULL; - APPLICATION_UPDATE_ENTRY* prgEntries = NULL; - DWORD cEntries = NULL; - - pNewChain = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE)); - ApupExitOnNull(pNewChain, hr, E_OUTOFMEMORY, "Failed to allocate filtered chain."); - - hr = FilterEntries(pChain->rgEntries, pChain->cEntries, pVersion, &prgEntries, &cEntries); - ApupExitOnFailure(hr, "Failed to filter entries by version."); - - if (pChain->wzDefaultApplicationId) - { - hr = StrAllocString(&pNewChain->wzDefaultApplicationId, pChain->wzDefaultApplicationId, 0); - ApupExitOnFailure(hr, "Failed to copy default application id."); - } - - if (pChain->wzDefaultApplicationType) - { - hr = StrAllocString(&pNewChain->wzDefaultApplicationType, pChain->wzDefaultApplicationType, 0); - ApupExitOnFailure(hr, "Failed to copy default application type."); - } - - pNewChain->rgEntries = prgEntries; - pNewChain->cEntries = cEntries; - - *ppFilteredChain = pNewChain; - pNewChain = NULL; - -LExit: - ReleaseApupChain(pNewChain); - return hr; -} - - -// -// ApupFreeChain - frees a previously allocated application update chain. -// -extern "C" void DAPI ApupFreeChain( - __in APPLICATION_UPDATE_CHAIN* pChain - ) -{ - if (pChain) - { - for (DWORD i = 0; i < pChain->cEntries; ++i) - { - FreeEntry(pChain->rgEntries + i); - } - - ReleaseMem(pChain->rgEntries); - ReleaseStr(pChain->wzDefaultApplicationType); - ReleaseStr(pChain->wzDefaultApplicationId); - ReleaseMem(pChain); - } -} - - -static HRESULT ProcessEntry( - __in ATOM_ENTRY* pAtomEntry, - __in LPCWSTR wzDefaultAppId, - __inout APPLICATION_UPDATE_ENTRY* pApupEntry - ) -{ - HRESULT hr = S_OK; - int nCompareResult = 0; - - // First search the ATOM entry's custom elements to try and find the application update information. - for (ATOM_UNKNOWN_ELEMENT* pElement = pAtomEntry->pUnknownElements; pElement; pElement = pElement->pNext) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1)) - { - hr = StrAllocString(&pApupEntry->wzApplicationId, pElement->wzValue, 0); - ApupExitOnFailure(hr, "Failed to allocate application identity."); - - for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1)) - { - hr = StrAllocString(&pApupEntry->wzApplicationType, pAttribute->wzValue, 0); - ApupExitOnFailure(hr, "Failed to allocate application type."); - } - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"upgrade", -1)) - { - hr = StrAllocString(&pApupEntry->wzUpgradeId, pElement->wzValue, 0); - ApupExitOnFailure(hr, "Failed to allocate upgrade id."); - - for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"version", -1)) - { - hr = VerParseVersion(pAttribute->wzValue, 0, FALSE, &pApupEntry->pUpgradeVersion); - ApupExitOnFailure(hr, "Failed to parse upgrade version string '%ls' from ATOM entry.", pAttribute->wzValue); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"exclusive", -1)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzValue, -1, L"true", -1)) - { - pApupEntry->fUpgradeExclusive = TRUE; - } - } - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"version", -1)) - { - hr = VerParseVersion(pElement->wzValue, 0, FALSE, &pApupEntry->pVersion); - ApupExitOnFailure(hr, "Failed to parse version string '%ls' from ATOM entry.", pElement->wzValue); - } - } - } - - // If there is no application identity or no version, skip the whole thing. - if ((!pApupEntry->wzApplicationId && !wzDefaultAppId) || !pApupEntry->pVersion) - { - ExitFunction1(hr = S_FALSE); // skip this update since it has no application id or version. - } - - if (pApupEntry->pUpgradeVersion) - { - hr = VerCompareParsedVersions(pApupEntry->pUpgradeVersion, pApupEntry->pVersion, &nCompareResult); - ApupExitOnFailure(hr, "Failed to compare version to upgrade version."); - - if (nCompareResult >= 0) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ApupExitOnRootFailure(hr, "Upgrade version is greater than or equal to application version."); - } - } - - if (pAtomEntry->wzTitle) - { - hr = StrAllocString(&pApupEntry->wzTitle, pAtomEntry->wzTitle, 0); - ApupExitOnFailure(hr, "Failed to allocate application title."); - } - - if (pAtomEntry->wzSummary) - { - hr = StrAllocString(&pApupEntry->wzSummary, pAtomEntry->wzSummary, 0); - ApupExitOnFailure(hr, "Failed to allocate application summary."); - } - - if (pAtomEntry->pContent) - { - if (pAtomEntry->pContent->wzType) - { - hr = StrAllocString(&pApupEntry->wzContentType, pAtomEntry->pContent->wzType, 0); - ApupExitOnFailure(hr, "Failed to allocate content type."); - } - - if (pAtomEntry->pContent->wzValue) - { - hr = StrAllocString(&pApupEntry->wzContent, pAtomEntry->pContent->wzValue, 0); - ApupExitOnFailure(hr, "Failed to allocate content."); - } - } - // Now process the enclosures. Assume every link in the ATOM entry is an enclosure. - pApupEntry->rgEnclosures = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_ENCLOSURE) * pAtomEntry->cLinks, TRUE)); - ApupExitOnNull(pApupEntry->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate enclosures for application update entry."); - - for (DWORD i = 0; i < pAtomEntry->cLinks; ++i) - { - ATOM_LINK* pLink = pAtomEntry->rgLinks + i; - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLink->wzRel, -1, L"enclosure", -1)) - { - hr = ParseEnclosure(pLink, pApupEntry->rgEnclosures + pApupEntry->cEnclosures); - ApupExitOnFailure(hr, "Failed to parse enclosure."); - - pApupEntry->dw64TotalSize += pApupEntry->rgEnclosures[pApupEntry->cEnclosures].dw64Size; // total up the size of the enclosures - - ++pApupEntry->cEnclosures; - } - } - -LExit: - if (S_OK != hr) // if anything went wrong, free the entry. - { - FreeEntry(pApupEntry); - memset(pApupEntry, 0, sizeof(APPLICATION_UPDATE_ENTRY)); - } - - return hr; -} - - -static HRESULT ParseEnclosure( - __in ATOM_LINK* pLink, - __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure - ) -{ - HRESULT hr = S_OK; - DWORD dwDigestLength = 0; - DWORD dwDigestStringLength = 0; - size_t cchDigestString = 0; - - // First search the ATOM link's custom elements to try and find the application update enclosure information. - for (ATOM_UNKNOWN_ELEMENT* pElement = pLink->pUnknownElements; pElement; pElement = pElement->pNext) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"digest", -1, pElement->wzElement, -1)) - { - // Find the digest[@algorithm] which is required. Everything else is ignored. - for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) - { - dwDigestLength = 0; - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"algorithm", -1, pAttribute->wzAttribute, -1)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"md5", -1, pAttribute->wzValue, -1)) - { - pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_MD5; - dwDigestLength = MD5_HASH_LEN; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha1", -1, pAttribute->wzValue, -1)) - { - pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA1; - dwDigestLength = SHA1_HASH_LEN; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha256", -1, pAttribute->wzValue, -1)) - { - pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA256; - dwDigestLength = SHA256_HASH_LEN; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha512", -1, pAttribute->wzValue, -1)) - { - pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA512; - dwDigestLength = SHA512_HASH_LEN; - } - break; - } - } - - if (dwDigestLength) - { - dwDigestStringLength = 2 * dwDigestLength; - - hr = ::StringCchLengthW(pElement->wzValue, STRSAFE_MAX_CCH, &cchDigestString); - ApupExitOnFailure(hr, "Failed to get string length of digest value."); - - if (dwDigestStringLength != cchDigestString) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ApupExitOnRootFailure(hr, "Invalid digest length (%Iu) for digest algorithm (%u).", cchDigestString, dwDigestStringLength); - } - - pEnclosure->cbDigest = sizeof(BYTE) * dwDigestLength; - pEnclosure->rgbDigest = static_cast(MemAlloc(pEnclosure->cbDigest, TRUE)); - ApupExitOnNull(pEnclosure->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for digest."); - - hr = StrHexDecode(pElement->wzValue, pEnclosure->rgbDigest, pEnclosure->cbDigest); - ApupExitOnFailure(hr, "Failed to decode digest value."); - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ApupExitOnRootFailure(hr, "Unknown algorithm type for digest."); - } - - break; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"name", -1, pElement->wzElement, -1)) - { - hr = StrAllocString(&pEnclosure->wzLocalName, pElement->wzValue, 0); - ApupExitOnFailure(hr, "Failed to copy local name."); - } - } - } - - pEnclosure->dw64Size = pLink->dw64Length; - - hr = StrAllocString(&pEnclosure->wzUrl, pLink->wzUrl, 0); - ApupExitOnFailure(hr, "Failed to allocate enclosure URL."); - - pEnclosure->fInstaller = FALSE; - pEnclosure->wzLocalName = NULL; - -LExit: - return hr; -} - - -static __callback int __cdecl CompareEntries( - void* /*pvContext*/, - const void* pvLeft, - const void* pvRight - ) -{ - int ret = 0; - const APPLICATION_UPDATE_ENTRY* pEntryLeft = static_cast(pvLeft); - const APPLICATION_UPDATE_ENTRY* pEntryRight = static_cast(pvRight); - - VerCompareParsedVersions(pEntryLeft->pVersion, pEntryRight->pVersion, &ret); - if (0 == ret) - { - VerCompareParsedVersions(pEntryLeft->pUpgradeVersion, pEntryRight->pUpgradeVersion, &ret); - if (0 == ret) - { - ret = (pEntryLeft->dw64TotalSize < pEntryRight->dw64TotalSize) ? -1 : - (pEntryLeft->dw64TotalSize > pEntryRight->dw64TotalSize) ? 1 : 0; - } - } - - // Sort descending. - ret = -ret; - - return ret; -} - - -static HRESULT FilterEntries( - __in APPLICATION_UPDATE_ENTRY* rgEntries, - __in DWORD cEntries, - __in VERUTIL_VERSION* pCurrentVersion, - __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries, - __inout DWORD* pcFilteredEntries - ) -{ - HRESULT hr = S_OK; - int nCompareResult = 0; - size_t cbAllocSize = 0; - const APPLICATION_UPDATE_ENTRY* pRequired = NULL;; - LPVOID pv = NULL; - - if (cEntries) - { - for (DWORD i = 0; i < cEntries; ++i) - { - const APPLICATION_UPDATE_ENTRY* pEntry = rgEntries + i; - - hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pVersion, &nCompareResult); - ApupExitOnFailure(hr, "Failed to compare versions."); - - if (nCompareResult >= 0) - { - continue; - } - - hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pUpgradeVersion, &nCompareResult); - ApupExitOnFailure(hr, "Failed to compare upgrade versions."); - - if (nCompareResult > 0 || (!pEntry->fUpgradeExclusive && nCompareResult == 0)) - { - pRequired = pEntry; - break; - } - } - - if (pRequired) - { - DWORD cNewFilteredEntries = *pcFilteredEntries + 1; - - hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENTRY), cNewFilteredEntries, &cbAllocSize); - ApupExitOnFailure(hr, "Overflow while calculating alloc size for more entries - number of entries: %u", cNewFilteredEntries); - - if (*prgFilteredEntries) - { - pv = MemReAlloc(*prgFilteredEntries, cbAllocSize, FALSE); - ApupExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for more entries."); - } - else - { - pv = MemAlloc(cbAllocSize, TRUE); - ApupExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for entries."); - } - - *pcFilteredEntries = cNewFilteredEntries; - *prgFilteredEntries = static_cast(pv); - pv = NULL; - - hr = CopyEntry(pRequired, *prgFilteredEntries + *pcFilteredEntries - 1); - ApupExitOnFailure(hr, "Failed to deep copy entry."); - - hr = VerCompareParsedVersions(pRequired->pVersion, rgEntries[0].pVersion, &nCompareResult); - ApupExitOnFailure(hr, "Failed to compare required version."); - - if (nCompareResult < 0) - { - FilterEntries(rgEntries, cEntries, pRequired->pVersion, prgFilteredEntries, pcFilteredEntries); - } - } - } - -LExit: - ReleaseMem(pv); - return hr; -} - - -static HRESULT CopyEntry( - __in const APPLICATION_UPDATE_ENTRY* pSrc, - __in APPLICATION_UPDATE_ENTRY* pDest - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - - memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENTRY)); - - if (pSrc->wzApplicationId) - { - hr = StrAllocString(&pDest->wzApplicationId, pSrc->wzApplicationId, 0); - ApupExitOnFailure(hr, "Failed to copy application id."); - } - - if (pSrc->wzApplicationType) - { - hr = StrAllocString(&pDest->wzApplicationType, pSrc->wzApplicationType, 0); - ApupExitOnFailure(hr, "Failed to copy application type."); - } - - if (pSrc->wzUpgradeId) - { - hr = StrAllocString(&pDest->wzUpgradeId, pSrc->wzUpgradeId, 0); - ApupExitOnFailure(hr, "Failed to copy upgrade id."); - } - - if (pSrc->wzTitle) - { - hr = StrAllocString(&pDest->wzTitle, pSrc->wzTitle, 0); - ApupExitOnFailure(hr, "Failed to copy title."); - } - - if (pSrc->wzSummary) - { - hr = StrAllocString(&pDest->wzSummary, pSrc->wzSummary, 0); - ApupExitOnFailure(hr, "Failed to copy summary."); - } - - if (pSrc->wzContentType) - { - hr = StrAllocString(&pDest->wzContentType, pSrc->wzContentType, 0); - ApupExitOnFailure(hr, "Failed to copy content type."); - } - - if (pSrc->wzContent) - { - hr = StrAllocString(&pDest->wzContent, pSrc->wzContent, 0); - ApupExitOnFailure(hr, "Failed to copy content."); - } - - pDest->dw64TotalSize = pSrc->dw64TotalSize; - - hr = VerCopyVersion(pSrc->pUpgradeVersion, &pDest->pUpgradeVersion); - ApupExitOnFailure(hr, "Failed to copy upgrade version."); - - hr = VerCopyVersion(pSrc->pVersion, &pDest->pVersion); - ApupExitOnFailure(hr, "Failed to copy version."); - - pDest->fUpgradeExclusive = pSrc->fUpgradeExclusive; - - hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENCLOSURE), pSrc->cEnclosures, &cbAllocSize); - ApupExitOnRootFailure(hr, "Overflow while calculating memory allocation size"); - - pDest->rgEnclosures = static_cast(MemAlloc(cbAllocSize, TRUE)); - ApupExitOnNull(pDest->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate copy of enclosures."); - - pDest->cEnclosures = pSrc->cEnclosures; - - for (DWORD i = 0; i < pDest->cEnclosures; ++i) - { - hr = CopyEnclosure(pSrc->rgEnclosures + i, pDest->rgEnclosures + i); - ApupExitOnFailure(hr, "Failed to copy enclosure."); - } - -LExit: - if (FAILED(hr)) - { - FreeEntry(pDest); - } - - return hr; -} - - -static HRESULT CopyEnclosure( - __in const APPLICATION_UPDATE_ENCLOSURE* pSrc, - __in APPLICATION_UPDATE_ENCLOSURE* pDest - ) -{ - HRESULT hr = S_OK; - - memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENCLOSURE)); - - if (pSrc->wzUrl) - { - hr = StrAllocString(&pDest->wzUrl, pSrc->wzUrl, 0); - ApupExitOnFailure(hr, "Failed copy url."); - } - - if (pSrc->wzLocalName) - { - hr = StrAllocString(&pDest->wzLocalName, pSrc->wzLocalName, 0); - ApupExitOnFailure(hr, "Failed copy url."); - } - - pDest->rgbDigest = static_cast(MemAlloc(sizeof(BYTE) * pSrc->cbDigest, FALSE)); - ApupExitOnNull(pDest->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for copy of digest."); - - pDest->cbDigest = pSrc->cbDigest; - - memcpy_s(pDest->rgbDigest, sizeof(BYTE) * pDest->cbDigest, pSrc->rgbDigest, sizeof(BYTE) * pSrc->cbDigest); - - pDest->digestAlgorithm = pSrc->digestAlgorithm; - - pDest->dw64Size = pSrc->dw64Size; - pDest->fInstaller = pSrc->fInstaller; - -LExit: - if (FAILED(hr)) - { - FreeEnclosure(pDest); - } - - return hr; -} - - -static void FreeEntry( - __in APPLICATION_UPDATE_ENTRY* pEntry - ) -{ - if (pEntry) - { - for (DWORD i = 0; i < pEntry->cEnclosures; ++i) - { - FreeEnclosure(pEntry->rgEnclosures + i); - } - - ReleaseStr(pEntry->wzUpgradeId); - ReleaseStr(pEntry->wzApplicationType); - ReleaseStr(pEntry->wzApplicationId); - ReleaseStr(pEntry->wzTitle); - ReleaseStr(pEntry->wzSummary); - ReleaseStr(pEntry->wzContentType); - ReleaseStr(pEntry->wzContent); - ReleaseVerutilVersion(pEntry->pVersion); - ReleaseVerutilVersion(pEntry->pUpgradeVersion); - } -} - - -static void FreeEnclosure( - __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure - ) -{ - if (pEnclosure) - { - ReleaseMem(pEnclosure->rgbDigest); - ReleaseStr(pEnclosure->wzLocalName); - ReleaseStr(pEnclosure->wzUrl); - } -} diff --git a/src/dutil/atomutil.cpp b/src/dutil/atomutil.cpp deleted file mode 100644 index c7c7975a..00000000 --- a/src/dutil/atomutil.cpp +++ /dev/null @@ -1,1297 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define AtomExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) -#define AtomExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) -#define AtomExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) -#define AtomExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) -#define AtomExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) -#define AtomExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) -#define AtomExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__) -#define AtomExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__) -#define AtomExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__) -#define AtomExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__) -#define AtomExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ATOMUTIL, e, x, s, __VA_ARGS__) -#define AtomExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ATOMUTIL, g, x, s, __VA_ARGS__) - -static HRESULT ParseAtomDocument( - __in IXMLDOMDocument *pixd, - __out ATOM_FEED **ppFeed - ); -static HRESULT ParseAtomFeed( - __in IXMLDOMNode *pixnFeed, - __out ATOM_FEED **ppFeed - ); -static HRESULT ParseAtomAuthor( - __in IXMLDOMNode* pixnAuthor, - __in ATOM_AUTHOR* pAuthor - ); -static HRESULT ParseAtomCategory( - __in IXMLDOMNode* pixnCategory, - __in ATOM_CATEGORY* pCategory - ); -static HRESULT ParseAtomEntry( - __in IXMLDOMNode* pixnEntry, - __in ATOM_ENTRY* pEntry - ); -static HRESULT ParseAtomLink( - __in IXMLDOMNode* pixnLink, - __in ATOM_LINK* pLink - ); -static HRESULT ParseAtomUnknownElement( - __in IXMLDOMNode *pNode, - __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement - ); -static HRESULT ParseAtomUnknownAttribute( - __in IXMLDOMNode *pNode, - __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute - ); -static HRESULT AssignDateTime( - __in FILETIME* pft, - __in IXMLDOMNode* pNode - ); -static HRESULT AssignString( - __out_z LPWSTR* pwzValue, - __in IXMLDOMNode* pNode - ); -static void FreeAtomAuthor( - __in_opt ATOM_AUTHOR* pAuthor - ); -static void FreeAtomContent( - __in_opt ATOM_CONTENT* pContent - ); -static void FreeAtomCategory( - __in_opt ATOM_CATEGORY* pCategory - ); -static void FreeAtomEntry( - __in_opt ATOM_ENTRY* pEntry - ); -static void FreeAtomLink( - __in_opt ATOM_LINK* pLink - ); -static void FreeAtomUnknownElementList( - __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement - ); -static void FreeAtomUnknownAttributeList( - __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute - ); - -template static HRESULT AllocateAtomType( - __in IXMLDOMNode* pixnParent, - __in LPCWSTR wzT, - __out T** pprgT, - __out DWORD* pcT - ); - - -/******************************************************************** - AtomInitialize - Initialize ATOM utilities. - -*********************************************************************/ -extern "C" HRESULT DAPI AtomInitialize() -{ - return XmlInitialize(); -} - - -/******************************************************************** - AtomUninitialize - Uninitialize ATOM utilities. - -*********************************************************************/ -extern "C" void DAPI AtomUninitialize() -{ - XmlUninitialize(); -} - - -/******************************************************************** - AtomParseFromString - parses out an ATOM feed from a string. - -*********************************************************************/ -extern "C" HRESULT DAPI AtomParseFromString( - __in_z LPCWSTR wzAtomString, - __out ATOM_FEED **ppFeed - ) -{ - Assert(wzAtomString); - Assert(ppFeed); - - HRESULT hr = S_OK; - ATOM_FEED *pNewFeed = NULL; - IXMLDOMDocument *pixdAtom = NULL; - - hr = XmlLoadDocument(wzAtomString, &pixdAtom); - AtomExitOnFailure(hr, "Failed to load ATOM string as XML document."); - - hr = ParseAtomDocument(pixdAtom, &pNewFeed); - AtomExitOnFailure(hr, "Failed to parse ATOM document."); - - *ppFeed = pNewFeed; - pNewFeed = NULL; - -LExit: - ReleaseAtomFeed(pNewFeed); - ReleaseObject(pixdAtom); - - return hr; -} - - -/******************************************************************** - AtomParseFromFile - parses out an ATOM feed from a file path. - -*********************************************************************/ -extern "C" HRESULT DAPI AtomParseFromFile( - __in_z LPCWSTR wzAtomFile, - __out ATOM_FEED **ppFeed - ) -{ - Assert(wzAtomFile); - Assert(ppFeed); - - HRESULT hr = S_OK; - ATOM_FEED *pNewFeed = NULL; - IXMLDOMDocument *pixdAtom = NULL; - - hr = XmlLoadDocumentFromFile(wzAtomFile, &pixdAtom); - AtomExitOnFailure(hr, "Failed to load ATOM string as XML document."); - - hr = ParseAtomDocument(pixdAtom, &pNewFeed); - AtomExitOnFailure(hr, "Failed to parse ATOM document."); - - *ppFeed = pNewFeed; - pNewFeed = NULL; - -LExit: - ReleaseAtomFeed(pNewFeed); - ReleaseObject(pixdAtom); - - return hr; -} - - -/******************************************************************** - AtomParseFromDocument - parses out an ATOM feed from an XML document. - -*********************************************************************/ -extern "C" HRESULT DAPI AtomParseFromDocument( - __in IXMLDOMDocument* pixdDocument, - __out ATOM_FEED **ppFeed - ) -{ - Assert(pixdDocument); - Assert(ppFeed); - - HRESULT hr = S_OK; - ATOM_FEED *pNewFeed = NULL; - - hr = ParseAtomDocument(pixdDocument, &pNewFeed); - AtomExitOnFailure(hr, "Failed to parse ATOM document."); - - *ppFeed = pNewFeed; - pNewFeed = NULL; - -LExit: - ReleaseAtomFeed(pNewFeed); - - return hr; -} - - -/******************************************************************** - AtomFreeFeed - parses out an ATOM feed from a string. - -*********************************************************************/ -extern "C" void DAPI AtomFreeFeed( - __in_xcount(pFeed->cItems) ATOM_FEED* pFeed - ) -{ - if (pFeed) - { - FreeAtomUnknownElementList(pFeed->pUnknownElements); - ReleaseObject(pFeed->pixn); - - for (DWORD i = 0; i < pFeed->cLinks; ++i) - { - FreeAtomLink(pFeed->rgLinks + i); - } - ReleaseMem(pFeed->rgLinks); - - for (DWORD i = 0; i < pFeed->cEntries; ++i) - { - FreeAtomEntry(pFeed->rgEntries + i); - } - ReleaseMem(pFeed->rgEntries); - - for (DWORD i = 0; i < pFeed->cCategories; ++i) - { - FreeAtomCategory(pFeed->rgCategories + i); - } - ReleaseMem(pFeed->rgCategories); - - for (DWORD i = 0; i < pFeed->cAuthors; ++i) - { - FreeAtomAuthor(pFeed->rgAuthors + i); - } - ReleaseMem(pFeed->rgAuthors); - - ReleaseStr(pFeed->wzGenerator); - ReleaseStr(pFeed->wzIcon); - ReleaseStr(pFeed->wzId); - ReleaseStr(pFeed->wzLogo); - ReleaseStr(pFeed->wzSubtitle); - ReleaseStr(pFeed->wzTitle); - - MemFree(pFeed); - } -} - - -/******************************************************************** - ParseAtomDocument - parses out an ATOM feed from a loaded XML DOM document. - -*********************************************************************/ -static HRESULT ParseAtomDocument( - __in IXMLDOMDocument *pixd, - __out ATOM_FEED **ppFeed - ) -{ - Assert(pixd); - Assert(ppFeed); - - HRESULT hr = S_OK; - IXMLDOMElement *pFeedElement = NULL; - - ATOM_FEED *pNewFeed = NULL; - - // - // Get the document element and start processing feeds. - // - hr = pixd->get_documentElement(&pFeedElement); - AtomExitOnFailure(hr, "failed get_documentElement in ParseAtomDocument"); - - hr = ParseAtomFeed(pFeedElement, &pNewFeed); - AtomExitOnFailure(hr, "Failed to parse ATOM feed."); - - if (S_FALSE == hr) - { - hr = S_OK; - } - - *ppFeed = pNewFeed; - pNewFeed = NULL; - -LExit: - ReleaseObject(pFeedElement); - - ReleaseAtomFeed(pNewFeed); - - return hr; -} - - -/******************************************************************** - ParseAtomFeed - parses out an ATOM feed from a loaded XML DOM element. - -*********************************************************************/ -static HRESULT ParseAtomFeed( - __in IXMLDOMNode *pixnFeed, - __out ATOM_FEED **ppFeed - ) -{ - Assert(pixnFeed); - Assert(ppFeed); - - HRESULT hr = S_OK; - IXMLDOMNodeList *pNodeList = NULL; - - ATOM_FEED *pNewFeed = NULL; - DWORD cAuthors = 0; - DWORD cCategories = 0; - DWORD cEntries = 0; - DWORD cLinks = 0; - - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - // First, allocate the new feed and all the possible sub elements. - pNewFeed = (ATOM_FEED*)MemAlloc(sizeof(ATOM_FEED), TRUE); - AtomExitOnNull(pNewFeed, hr, E_OUTOFMEMORY, "Failed to allocate ATOM feed structure."); - - pNewFeed->pixn = pixnFeed; - pNewFeed->pixn->AddRef(); - - hr = AllocateAtomType(pixnFeed, L"author", &pNewFeed->rgAuthors, &pNewFeed->cAuthors); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed authors."); - - hr = AllocateAtomType(pixnFeed, L"category", &pNewFeed->rgCategories, &pNewFeed->cCategories); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed categories."); - - hr = AllocateAtomType(pixnFeed, L"entry", &pNewFeed->rgEntries, &pNewFeed->cEntries); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed entries."); - - hr = AllocateAtomType(pixnFeed, L"link", &pNewFeed->rgLinks, &pNewFeed->cLinks); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed links."); - - // Second, process the elements under a feed. - hr = pixnFeed->get_childNodes(&pNodeList); - AtomExitOnFailure(hr, "Failed to get child nodes of ATOM feed element."); - - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"generator", -1)) - { - hr = AssignString(&pNewFeed->wzGenerator, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed generator."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"icon", -1)) - { - hr = AssignString(&pNewFeed->wzIcon, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed icon."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) - { - hr = AssignString(&pNewFeed->wzId, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed id."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"logo", -1)) - { - hr = AssignString(&pNewFeed->wzLogo, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed logo."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"subtitle", -1)) - { - hr = AssignString(&pNewFeed->wzSubtitle, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed subtitle."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) - { - hr = AssignString(&pNewFeed->wzTitle, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed title."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) - { - hr = AssignDateTime(&pNewFeed->ftUpdated, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM feed updated."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) - { - hr = ParseAtomAuthor(pNode, &pNewFeed->rgAuthors[cAuthors]); - AtomExitOnFailure(hr, "Failed to parse ATOM author."); - - ++cAuthors; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) - { - hr = ParseAtomCategory(pNode, &pNewFeed->rgCategories[cCategories]); - AtomExitOnFailure(hr, "Failed to parse ATOM category."); - - ++cCategories; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"entry", -1)) - { - hr = ParseAtomEntry(pNode, &pNewFeed->rgEntries[cEntries]); - AtomExitOnFailure(hr, "Failed to parse ATOM entry."); - - ++cEntries; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) - { - hr = ParseAtomLink(pNode, &pNewFeed->rgLinks[cLinks]); - AtomExitOnFailure(hr, "Failed to parse ATOM link."); - - ++cLinks; - } - else - { - hr = ParseAtomUnknownElement(pNode, &pNewFeed->pUnknownElements); - AtomExitOnFailure(hr, "Failed to parse unknown ATOM feed element: %ls", bstrNodeName); - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - - if (!pNewFeed->wzId || !*pNewFeed->wzId) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Failed to find required feed/id element."); - } - else if (!pNewFeed->wzTitle || !*pNewFeed->wzTitle) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Failed to find required feed/title element."); - } - else if (0 == pNewFeed->ftUpdated.dwHighDateTime && 0 == pNewFeed->ftUpdated.dwLowDateTime) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Failed to find required feed/updated element."); - } - - *ppFeed = pNewFeed; - pNewFeed = NULL; - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - - ReleaseAtomFeed(pNewFeed); - - return hr; -} - - -/******************************************************************** - AllocateAtomType - allocates enough space for all of the ATOM elements - of a particular type under a particular node. - -*********************************************************************/ -template static HRESULT AllocateAtomType( - __in IXMLDOMNode* pixnParent, - __in LPCWSTR wzT, - __out T** pprgT, - __out DWORD* pcT - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList *pNodeList = NULL; - - long cT = 0; - T* prgT = NULL; - - hr = XmlSelectNodes(pixnParent, wzT, &pNodeList); - AtomExitOnFailure(hr, "Failed to select all ATOM %ls.", wzT); - - if (S_OK == hr) - { - hr = pNodeList->get_length(&cT); - AtomExitOnFailure(hr, "Failed to count the number of ATOM %ls.", wzT); - - if (cT == 0) - { - ExitFunction(); - } - - prgT = static_cast(MemAlloc(sizeof(T) * cT, TRUE)); - AtomExitOnNull(prgT, hr, E_OUTOFMEMORY, "Failed to allocate ATOM."); - - *pcT = cT; - *pprgT = prgT; - prgT = NULL; - } - else - { - *pprgT = NULL; - *pcT = 0; - } - -LExit: - ReleaseMem(prgT); - ReleaseObject(pNodeList); - - return hr; -} - - -/******************************************************************** - ParseAtomAuthor - parses out an ATOM author from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseAtomAuthor( - __in IXMLDOMNode* pixnAuthor, - __in ATOM_AUTHOR* pAuthor - ) -{ - HRESULT hr = S_OK; - - IXMLDOMNodeList *pNodeList = NULL; - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - hr = pixnAuthor->get_childNodes(&pNodeList); - AtomExitOnFailure(hr, "Failed to get child nodes of ATOM author element."); - - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"name", -1)) - { - hr = AssignString(&pAuthor->wzName, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM author name."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"email", -1)) - { - hr = AssignString(&pAuthor->wzEmail, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM author email."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"uri", -1)) - { - hr = AssignString(&pAuthor->wzUrl, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM author uri."); - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM author elements."); - - hr = S_OK; - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - - return hr; -} - - -/******************************************************************** - ParseAtomCategory - parses out an ATOM category from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseAtomCategory( - __in IXMLDOMNode* pixnCategory, - __in ATOM_CATEGORY* pCategory - ) -{ - HRESULT hr = S_OK; - - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNodeList *pNodeList = NULL; - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - // Process attributes first. - hr = pixnCategory->get_attributes(&pixnnmAttributes); - AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); - - while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"label", -1)) - { - hr = AssignString(&pCategory->wzLabel, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM category label."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"scheme", -1)) - { - hr = AssignString(&pCategory->wzScheme, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM category scheme."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"term", -1)) - { - hr = AssignString(&pCategory->wzTerm, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM category term."); - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM category attributes."); - - // Process elements second. - hr = pixnCategory->get_childNodes(&pNodeList); - AtomExitOnFailure(hr, "Failed to get child nodes of ATOM category element."); - - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - hr = ParseAtomUnknownElement(pNode, &pCategory->pUnknownElements); - AtomExitOnFailure(hr, "Failed to parse unknown ATOM category element: %ls", bstrNodeName); - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM category elements."); - - hr = S_OK; - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - ReleaseObject(pixnnmAttributes); - - return hr; -} - - -/******************************************************************** - ParseAtomContent - parses out an ATOM content from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseAtomContent( - __in IXMLDOMNode* pixnContent, - __in ATOM_CONTENT* pContent - ) -{ - HRESULT hr = S_OK; - - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNodeList *pNodeList = NULL; - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - // Process attributes first. - hr = pixnContent->get_attributes(&pixnnmAttributes); - AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); - - while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) - { - hr = AssignString(&pContent->wzType, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM content type."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"url", -1)) - { - hr = AssignString(&pContent->wzUrl, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM content scheme."); - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM content attributes."); - - // Process elements second. - hr = pixnContent->get_childNodes(&pNodeList); - AtomExitOnFailure(hr, "Failed to get child nodes of ATOM content element."); - - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - hr = ParseAtomUnknownElement(pNode, &pContent->pUnknownElements); - AtomExitOnFailure(hr, "Failed to parse unknown ATOM content element: %ls", bstrNodeName); - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM content elements."); - - hr = AssignString(&pContent->wzValue, pixnContent); - AtomExitOnFailure(hr, "Failed to allocate ATOM content value."); - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - ReleaseObject(pixnnmAttributes); - - return hr; -} - - -/******************************************************************** - ParseAtomEntry - parses out an ATOM entry from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseAtomEntry( - __in IXMLDOMNode* pixnEntry, - __in ATOM_ENTRY* pEntry - ) -{ - HRESULT hr = S_OK; - - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNodeList *pNodeList = NULL; - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - DWORD cAuthors = 0; - DWORD cCategories = 0; - DWORD cLinks = 0; - - pEntry->pixn = pixnEntry; - pEntry->pixn->AddRef(); - - // First, allocate all the possible sub elements. - hr = AllocateAtomType(pixnEntry, L"author", &pEntry->rgAuthors, &pEntry->cAuthors); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry authors."); - - hr = AllocateAtomType(pixnEntry, L"category", &pEntry->rgCategories, &pEntry->cCategories); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry categories."); - - hr = AllocateAtomType(pixnEntry, L"link", &pEntry->rgLinks, &pEntry->cLinks); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry links."); - - // Second, process elements. - hr = pixnEntry->get_childNodes(&pNodeList); - AtomExitOnFailure(hr, "Failed to get child nodes of ATOM entry element."); - - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) - { - hr = AssignString(&pEntry->wzId, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry id."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"summary", -1)) - { - hr = AssignString(&pEntry->wzSummary, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry summary."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) - { - hr = AssignString(&pEntry->wzTitle, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry title."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"published", -1)) - { - hr = AssignDateTime(&pEntry->ftPublished, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry published."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) - { - hr = AssignDateTime(&pEntry->ftUpdated, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM entry updated."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) - { - hr = ParseAtomAuthor(pNode, &pEntry->rgAuthors[cAuthors]); - AtomExitOnFailure(hr, "Failed to parse ATOM entry author."); - - ++cAuthors; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) - { - hr = ParseAtomCategory(pNode, &pEntry->rgCategories[cCategories]); - AtomExitOnFailure(hr, "Failed to parse ATOM entry category."); - - ++cCategories; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"content", -1)) - { - if (NULL != pEntry->pContent) - { - hr = E_UNEXPECTED; - AtomExitOnFailure(hr, "Cannot have two content elements in ATOM entry."); - } - - pEntry->pContent = static_cast(MemAlloc(sizeof(ATOM_CONTENT), TRUE)); - AtomExitOnNull(pEntry->pContent, hr, E_OUTOFMEMORY, "Failed to allocate ATOM entry content."); - - hr = ParseAtomContent(pNode, pEntry->pContent); - AtomExitOnFailure(hr, "Failed to parse ATOM entry content."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) - { - hr = ParseAtomLink(pNode, &pEntry->rgLinks[cLinks]); - AtomExitOnFailure(hr, "Failed to parse ATOM entry link."); - - ++cLinks; - } - else - { - hr = ParseAtomUnknownElement(pNode, &pEntry->pUnknownElements); - AtomExitOnFailure(hr, "Failed to parse unknown ATOM entry element: %ls", bstrNodeName); - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM entry elements."); - - if (!pEntry->wzId || !*pEntry->wzId) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Failed to find required feed/entry/id element."); - } - else if (!pEntry->wzTitle || !*pEntry->wzTitle) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Failed to find required feed/entry/title element."); - } - else if (0 == pEntry->ftUpdated.dwHighDateTime && 0 == pEntry->ftUpdated.dwLowDateTime) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Failed to find required feed/entry/updated element."); - } - - hr = S_OK; - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - ReleaseObject(pixnnmAttributes); - - return hr; -} - - -/******************************************************************** - ParseAtomLink - parses out an ATOM link from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseAtomLink( - __in IXMLDOMNode* pixnLink, - __in ATOM_LINK* pLink - ) -{ - HRESULT hr = S_OK; - - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNodeList *pNodeList = NULL; - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - // Process attributes first. - hr = pixnLink->get_attributes(&pixnnmAttributes); - AtomExitOnFailure(hr, "Failed get attributes for ATOM link."); - - while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"rel", -1)) - { - hr = AssignString(&pLink->wzRel, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM link rel."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"href", -1)) - { - hr = AssignString(&pLink->wzUrl, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM link href."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"length", -1)) - { - hr = XmlGetAttributeLargeNumber(pixnLink, bstrNodeName, &pLink->dw64Length); - if (E_INVALIDARG == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - AtomExitOnFailure(hr, "Failed to parse ATOM link length."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) - { - hr = AssignString(&pLink->wzTitle, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM link title."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) - { - hr = AssignString(&pLink->wzType, pNode); - AtomExitOnFailure(hr, "Failed to allocate ATOM link type."); - } - else - { - hr = ParseAtomUnknownAttribute(pNode, &pLink->pUnknownAttributes); - AtomExitOnFailure(hr, "Failed to parse unknown ATOM link attribute: %ls", bstrNodeName); - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM link attributes."); - - // Process elements second. - hr = pixnLink->get_childNodes(&pNodeList); - AtomExitOnFailure(hr, "Failed to get child nodes of ATOM link element."); - - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - hr = ParseAtomUnknownElement(pNode, &pLink->pUnknownElements); - AtomExitOnFailure(hr, "Failed to parse unknown ATOM link element: %ls", bstrNodeName); - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - AtomExitOnFailure(hr, "Failed to process all ATOM link elements."); - - hr = AssignString(&pLink->wzValue, pixnLink); - AtomExitOnFailure(hr, "Failed to allocate ATOM link value."); - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - ReleaseObject(pixnnmAttributes); - - return hr; -} - - -/******************************************************************** - ParseAtomUnknownElement - parses out an unknown item from the ATOM feed from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseAtomUnknownElement( - __in IXMLDOMNode *pNode, - __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement - ) -{ - Assert(ppUnknownElement); - - HRESULT hr = S_OK; - BSTR bstrNodeNamespace = NULL; - BSTR bstrNodeName = NULL; - BSTR bstrNodeValue = NULL; - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNode* pixnAttribute = NULL; - ATOM_UNKNOWN_ELEMENT* pNewUnknownElement; - - pNewUnknownElement = (ATOM_UNKNOWN_ELEMENT*)MemAlloc(sizeof(ATOM_UNKNOWN_ELEMENT), TRUE); - AtomExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); - - hr = pNode->get_namespaceURI(&bstrNodeNamespace); - if (S_OK == hr) - { - hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); - AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element namespace."); - } - else if (S_FALSE == hr) - { - hr = S_OK; - } - AtomExitOnFailure(hr, "Failed to get unknown element namespace."); - - hr = pNode->get_baseName(&bstrNodeName); - AtomExitOnFailure(hr, "Failed to get unknown element name."); - - hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); - AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element name."); - - hr = XmlGetText(pNode, &bstrNodeValue); - AtomExitOnFailure(hr, "Failed to get unknown element value."); - - hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); - AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element value."); - - hr = pNode->get_attributes(&pixnnmAttributes); - AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); - - while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) - { - hr = ParseAtomUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); - AtomExitOnFailure(hr, "Failed to parse attribute on ATOM unknown element."); - - ReleaseNullObject(pixnAttribute); - } - - if (S_FALSE == hr) - { - hr = S_OK; - } - AtomExitOnFailure(hr, "Failed to enumerate all attributes on ATOM unknown element."); - - ATOM_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; - while (*ppTail) - { - ppTail = &(*ppTail)->pNext; - } - - *ppTail = pNewUnknownElement; - pNewUnknownElement = NULL; - -LExit: - FreeAtomUnknownElementList(pNewUnknownElement); - - ReleaseBSTR(bstrNodeNamespace); - ReleaseBSTR(bstrNodeName); - ReleaseBSTR(bstrNodeValue); - ReleaseObject(pixnnmAttributes); - ReleaseObject(pixnAttribute); - - return hr; -} - - -/******************************************************************** - ParseAtomUnknownAttribute - parses out attribute from an unknown element - -*********************************************************************/ -static HRESULT ParseAtomUnknownAttribute( - __in IXMLDOMNode *pNode, - __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute - ) -{ - Assert(ppUnknownAttribute); - - HRESULT hr = S_OK; - BSTR bstrNodeNamespace = NULL; - BSTR bstrNodeName = NULL; - BSTR bstrNodeValue = NULL; - ATOM_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; - - pNewUnknownAttribute = (ATOM_UNKNOWN_ATTRIBUTE*)MemAlloc(sizeof(ATOM_UNKNOWN_ATTRIBUTE), TRUE); - AtomExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); - - hr = pNode->get_namespaceURI(&bstrNodeNamespace); - if (S_OK == hr) - { - hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); - AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute namespace."); - } - else if (S_FALSE == hr) - { - hr = S_OK; - } - AtomExitOnFailure(hr, "Failed to get unknown attribute namespace."); - - hr = pNode->get_baseName(&bstrNodeName); - AtomExitOnFailure(hr, "Failed to get unknown attribute name."); - - hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); - AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute name."); - - hr = XmlGetText(pNode, &bstrNodeValue); - AtomExitOnFailure(hr, "Failed to get unknown attribute value."); - - hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); - AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute value."); - - ATOM_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; - while (*ppTail) - { - ppTail = &(*ppTail)->pNext; - } - - *ppTail = pNewUnknownAttribute; - pNewUnknownAttribute = NULL; - -LExit: - FreeAtomUnknownAttributeList(pNewUnknownAttribute); - - ReleaseBSTR(bstrNodeNamespace); - ReleaseBSTR(bstrNodeName); - ReleaseBSTR(bstrNodeValue); - - return hr; -} - - -/******************************************************************** - AssignDateTime - assigns the value of a node to a FILETIME struct. - -*********************************************************************/ -static HRESULT AssignDateTime( - __in FILETIME* pft, - __in IXMLDOMNode* pNode - ) -{ - HRESULT hr = S_OK; - BSTR bstrValue = NULL; - - if (0 != pft->dwHighDateTime || 0 != pft->dwLowDateTime) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Already process this datetime value."); - } - - hr = XmlGetText(pNode, &bstrValue); - AtomExitOnFailure(hr, "Failed to get value."); - - if (S_OK == hr) - { - hr = TimeFromString3339(bstrValue, pft); - AtomExitOnFailure(hr, "Failed to convert value to time."); - } - else - { - ZeroMemory(pft, sizeof(FILETIME)); - hr = S_OK; - } - -LExit: - ReleaseBSTR(bstrValue); - - return hr; -} - - -/******************************************************************** - AssignString - assigns the value of a node to a dynamic string. - -*********************************************************************/ -static HRESULT AssignString( - __out_z LPWSTR* pwzValue, - __in IXMLDOMNode* pNode - ) -{ - HRESULT hr = S_OK; - BSTR bstrValue = NULL; - - if (pwzValue && *pwzValue) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - AtomExitOnRootFailure(hr, "Already processed this value."); - } - - hr = XmlGetText(pNode, &bstrValue); - AtomExitOnFailure(hr, "Failed to get value."); - - if (S_OK == hr) - { - hr = StrAllocString(pwzValue, bstrValue, 0); - AtomExitOnFailure(hr, "Failed to allocate value."); - } - else - { - ReleaseNullStr(pwzValue); - hr = S_OK; - } - -LExit: - ReleaseBSTR(bstrValue); - - return hr; -} - - -/******************************************************************** - FreeAtomAuthor - releases all of the memory used by an ATOM author. - -*********************************************************************/ -static void FreeAtomAuthor( - __in_opt ATOM_AUTHOR* pAuthor - ) -{ - if (pAuthor) - { - ReleaseStr(pAuthor->wzUrl); - ReleaseStr(pAuthor->wzEmail); - ReleaseStr(pAuthor->wzName); - } -} - - -/******************************************************************** - FreeAtomCategory - releases all of the memory used by an ATOM category. - -*********************************************************************/ -static void FreeAtomCategory( - __in_opt ATOM_CATEGORY* pCategory - ) -{ - if (pCategory) - { - FreeAtomUnknownElementList(pCategory->pUnknownElements); - - ReleaseStr(pCategory->wzTerm); - ReleaseStr(pCategory->wzScheme); - ReleaseStr(pCategory->wzLabel); - } -} - - -/******************************************************************** - FreeAtomContent - releases all of the memory used by an ATOM content. - -*********************************************************************/ -static void FreeAtomContent( - __in_opt ATOM_CONTENT* pContent - ) -{ - if (pContent) - { - FreeAtomUnknownElementList(pContent->pUnknownElements); - - ReleaseStr(pContent->wzValue); - ReleaseStr(pContent->wzUrl); - ReleaseStr(pContent->wzType); - } -} - - -/******************************************************************** - FreeAtomEntry - releases all of the memory used by an ATOM entry. - -*********************************************************************/ -static void FreeAtomEntry( - __in_opt ATOM_ENTRY* pEntry - ) -{ - if (pEntry) - { - FreeAtomUnknownElementList(pEntry->pUnknownElements); - ReleaseObject(pEntry->pixn); - - for (DWORD i = 0; i < pEntry->cLinks; ++i) - { - FreeAtomLink(pEntry->rgLinks + i); - } - ReleaseMem(pEntry->rgLinks); - - for (DWORD i = 0; i < pEntry->cCategories; ++i) - { - FreeAtomCategory(pEntry->rgCategories + i); - } - ReleaseMem(pEntry->rgCategories); - - for (DWORD i = 0; i < pEntry->cAuthors; ++i) - { - FreeAtomAuthor(pEntry->rgAuthors + i); - } - ReleaseMem(pEntry->rgAuthors); - - FreeAtomContent(pEntry->pContent); - ReleaseMem(pEntry->pContent); - - ReleaseStr(pEntry->wzTitle); - ReleaseStr(pEntry->wzSummary); - ReleaseStr(pEntry->wzId); - } -} - - -/******************************************************************** - FreeAtomLink - releases all of the memory used by an ATOM link. - -*********************************************************************/ -static void FreeAtomLink( - __in_opt ATOM_LINK* pLink - ) -{ - if (pLink) - { - FreeAtomUnknownElementList(pLink->pUnknownElements); - FreeAtomUnknownAttributeList(pLink->pUnknownAttributes); - - ReleaseStr(pLink->wzValue); - ReleaseStr(pLink->wzUrl); - ReleaseStr(pLink->wzType); - ReleaseStr(pLink->wzTitle); - ReleaseStr(pLink->wzRel); - } -} - - -/******************************************************************** - FreeAtomUnknownElement - releases all of the memory used by a list of unknown elements - -*********************************************************************/ -static void FreeAtomUnknownElementList( - __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement - ) -{ - while (pUnknownElement) - { - ATOM_UNKNOWN_ELEMENT* pFree = pUnknownElement; - pUnknownElement = pUnknownElement->pNext; - - FreeAtomUnknownAttributeList(pFree->pAttributes); - ReleaseStr(pFree->wzNamespace); - ReleaseStr(pFree->wzElement); - ReleaseStr(pFree->wzValue); - MemFree(pFree); - } -} - - -/******************************************************************** - FreeAtomUnknownAttribute - releases all of the memory used by a list of unknown attributes - -*********************************************************************/ -static void FreeAtomUnknownAttributeList( - __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute - ) -{ - while (pUnknownAttribute) - { - ATOM_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute; - pUnknownAttribute = pUnknownAttribute->pNext; - - ReleaseStr(pFree->wzNamespace); - ReleaseStr(pFree->wzAttribute); - ReleaseStr(pFree->wzValue); - MemFree(pFree); - } -} diff --git a/src/dutil/buffutil.cpp b/src/dutil/buffutil.cpp deleted file mode 100644 index b6d58cc0..00000000 --- a/src/dutil/buffutil.cpp +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define BuffExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) -#define BuffExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) -#define BuffExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) -#define BuffExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) -#define BuffExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) -#define BuffExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) -#define BuffExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUFFUTIL, p, x, e, s, __VA_ARGS__) -#define BuffExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, p, x, s, __VA_ARGS__) -#define BuffExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, p, x, e, s, __VA_ARGS__) -#define BuffExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, p, x, s, __VA_ARGS__) -#define BuffExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUFFUTIL, e, x, s, __VA_ARGS__) -#define BuffExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_BUFFUTIL, g, x, s, __VA_ARGS__) - - -// constants - -#define BUFFER_INCREMENT 128 - - -// helper function declarations - -static HRESULT EnsureBufferSize( - __deref_inout_bcount(cbSize) BYTE** ppbBuffer, - __in SIZE_T cbSize - ); - - -// functions - -extern "C" HRESULT BuffReadNumber( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __out DWORD* pdw - ) -{ - Assert(pbBuffer); - Assert(piBuffer); - Assert(pdw); - - HRESULT hr = S_OK; - SIZE_T cbAvailable = 0; - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size."); - - // verify buffer size - if (sizeof(DWORD) > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small."); - } - - *pdw = *(const DWORD*)(pbBuffer + *piBuffer); - *piBuffer += sizeof(DWORD); - -LExit: - return hr; -} - -extern "C" HRESULT BuffReadNumber64( - __in_bcount(cbBuffer) const BYTE * pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __out DWORD64* pdw64 - ) -{ - Assert(pbBuffer); - Assert(piBuffer); - Assert(pdw64); - - HRESULT hr = S_OK; - SIZE_T cbAvailable = 0; - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size."); - - // verify buffer size - if (sizeof(DWORD64) > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small."); - } - - *pdw64 = *(const DWORD64*)(pbBuffer + *piBuffer); - *piBuffer += sizeof(DWORD64); - -LExit: - return hr; -} - -extern "C" HRESULT BuffReadPointer( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __out DWORD_PTR* pdw64 - ) -{ - Assert(pbBuffer); - Assert(piBuffer); - Assert(pdw64); - - HRESULT hr = S_OK; - SIZE_T cbAvailable = 0; - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size."); - - // verify buffer size - if (sizeof(DWORD_PTR) > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small."); - } - - *pdw64 = *(const DWORD_PTR*)(pbBuffer + *piBuffer); - *piBuffer += sizeof(DWORD_PTR); - -LExit: - return hr; -} - -extern "C" HRESULT BuffReadString( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __deref_out_z LPWSTR* pscz - ) -{ - Assert(pbBuffer); - Assert(piBuffer); - Assert(pscz); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cb = 0; - SIZE_T cbAvailable = 0; - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count."); - - // verify buffer size - if (sizeof(SIZE_T) > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small."); - } - - // read character count - cch = *(const SIZE_T*)(pbBuffer + *piBuffer); - - hr = ::SIZETMult(cch, sizeof(WCHAR), &cb); - BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); - - hr = ::SIZETAdd(*piBuffer, sizeof(SIZE_T), piBuffer); - BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); - - // verify buffer size - if (cb > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small to hold character data."); - } - - // copy character data - hr = StrAllocString(pscz, cch ? (LPCWSTR)(pbBuffer + *piBuffer) : L"", cch); - BuffExitOnFailure(hr, "Failed to copy character data."); - - *piBuffer += cb; - -LExit: - return hr; -} - -extern "C" HRESULT BuffReadStringAnsi( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __deref_out_z LPSTR* pscz - ) -{ - Assert(pbBuffer); - Assert(piBuffer); - Assert(pscz); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cb = 0; - SIZE_T cbAvailable = 0; - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count."); - - // verify buffer size - if (sizeof(SIZE_T) > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small."); - } - - // read character count - cch = *(const SIZE_T*)(pbBuffer + *piBuffer); - - hr = ::SIZETMult(cch, sizeof(CHAR), &cb); - BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); - - hr = ::SIZETAdd(*piBuffer, sizeof(SIZE_T), piBuffer); - BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); - - // verify buffer size - if (cb > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small to hold character count."); - } - - // copy character data - hr = StrAnsiAllocStringAnsi(pscz, cch ? (LPCSTR)(pbBuffer + *piBuffer) : "", cch); - BuffExitOnFailure(hr, "Failed to copy character data."); - - *piBuffer += cb; - -LExit: - return hr; -} - -extern "C" HRESULT BuffReadStream( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __deref_inout_bcount(*pcbStream) BYTE** ppbStream, - __out SIZE_T* pcbStream - ) -{ - Assert(pbBuffer); - Assert(piBuffer); - Assert(ppbStream); - Assert(pcbStream); - - HRESULT hr = S_OK; - SIZE_T cb = 0; - SIZE_T cbAvailable = 0; - errno_t err = 0; - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size for stream size."); - - // verify buffer size - if (sizeof(SIZE_T) > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small."); - } - - // read stream size - cb = *(const SIZE_T*)(pbBuffer + *piBuffer); - *piBuffer += sizeof(SIZE_T); - - // get availiable data size - hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); - BuffExitOnRootFailure(hr, "Failed to calculate available data size for stream buffer."); - - // verify buffer size - if (cb > cbAvailable) - { - hr = E_INVALIDARG; - BuffExitOnRootFailure(hr, "Buffer too small to hold byte count."); - } - - // allocate buffer - *ppbStream = (BYTE*)MemAlloc(cb, TRUE); - BuffExitOnNull(*ppbStream, hr, E_OUTOFMEMORY, "Failed to allocate stream."); - - // read stream data - err = memcpy_s(*ppbStream, cbBuffer - *piBuffer, pbBuffer + *piBuffer, cb); - if (err) - { - BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to read stream from buffer, error: %d", err); - } - - *piBuffer += cb; - - // return stream size - *pcbStream = cb; - -LExit: - return hr; -} - -extern "C" HRESULT BuffWriteNumber( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in DWORD dw - ) -{ - Assert(ppbBuffer); - Assert(piBuffer); - - HRESULT hr = S_OK; - - // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD)); - BuffExitOnFailure(hr, "Failed to ensure buffer size."); - - // copy data to buffer - *(DWORD*)(*ppbBuffer + *piBuffer) = dw; - *piBuffer += sizeof(DWORD); - -LExit: - return hr; -} - -extern "C" HRESULT BuffWriteNumber64( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in DWORD64 dw64 - ) -{ - Assert(ppbBuffer); - Assert(piBuffer); - - HRESULT hr = S_OK; - - // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD64)); - BuffExitOnFailure(hr, "Failed to ensure buffer size."); - - // copy data to buffer - *(DWORD64*)(*ppbBuffer + *piBuffer) = dw64; - *piBuffer += sizeof(DWORD64); - -LExit: - return hr; -} - -extern "C" HRESULT BuffWritePointer( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in DWORD_PTR dw - ) -{ - Assert(ppbBuffer); - Assert(piBuffer); - - HRESULT hr = S_OK; - - // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD_PTR)); - BuffExitOnFailure(hr, "Failed to ensure buffer size."); - - // copy data to buffer - *(DWORD_PTR*)(*ppbBuffer + *piBuffer) = dw; - *piBuffer += sizeof(DWORD_PTR); - -LExit: - return hr; -} - -extern "C" HRESULT BuffWriteString( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in_z_opt LPCWSTR scz - ) -{ - Assert(ppbBuffer); - Assert(piBuffer); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cb = 0; - errno_t err = 0; - - if (scz) - { - hr = ::StringCchLengthW(scz, STRSAFE_MAX_CCH, reinterpret_cast(&cch)); - BuffExitOnRootFailure(hr, "Failed to get string size.") - } - - cb = cch * sizeof(WCHAR); - - // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(SIZE_T) + cb)); - BuffExitOnFailure(hr, "Failed to ensure buffer size."); - - // copy character count to buffer - *(SIZE_T*)(*ppbBuffer + *piBuffer) = cch; - *piBuffer += sizeof(SIZE_T); - - // copy data to buffer - err = memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); - if (err) - { - BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write string to buffer: '%ls', error: %d", scz, err); - } - - *piBuffer += cb; - -LExit: - return hr; -} - -extern "C" HRESULT BuffWriteStringAnsi( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in_z_opt LPCSTR scz - ) -{ - Assert(ppbBuffer); - Assert(piBuffer); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cb = 0; - errno_t err = 0; - - if (scz) - { - hr = ::StringCchLengthA(scz, STRSAFE_MAX_CCH, reinterpret_cast(&cch)); - BuffExitOnRootFailure(hr, "Failed to get string size.") - } - - cb = cch * sizeof(CHAR); - - // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(SIZE_T) + cb)); - BuffExitOnFailure(hr, "Failed to ensure buffer size."); - - // copy character count to buffer - *(SIZE_T*)(*ppbBuffer + *piBuffer) = cch; - *piBuffer += sizeof(SIZE_T); - - // copy data to buffer - err = memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); - if (err) - { - BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write string to buffer: '%hs', error: %d", scz, err); - } - - *piBuffer += cb; - -LExit: - return hr; -} - -extern "C" HRESULT BuffWriteStream( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in_bcount(cbStream) const BYTE* pbStream, - __in SIZE_T cbStream - ) -{ - Assert(ppbBuffer); - Assert(piBuffer); - Assert(pbStream); - - HRESULT hr = S_OK; - SIZE_T cb = cbStream; - errno_t err = 0; - - // make sure we have a buffer with sufficient space - hr = EnsureBufferSize(ppbBuffer, *piBuffer + cbStream + sizeof(SIZE_T)); - BuffExitOnFailure(hr, "Failed to ensure buffer size."); - - // copy byte count to buffer - *(SIZE_T*)(*ppbBuffer + *piBuffer) = cb; - *piBuffer += sizeof(SIZE_T); - - // copy data to buffer - err = memcpy_s(*ppbBuffer + *piBuffer, cbStream, pbStream, cbStream); - if (err) - { - BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write stream to buffer, error: %d", err); - } - - *piBuffer += cbStream; - -LExit: - return hr; -} - - -// helper functions - -static HRESULT EnsureBufferSize( - __deref_inout_bcount(cbSize) BYTE** ppbBuffer, - __in SIZE_T cbSize - ) -{ - HRESULT hr = S_OK; - SIZE_T cbTarget = ((cbSize / BUFFER_INCREMENT) + 1) * BUFFER_INCREMENT; - - if (*ppbBuffer) - { - if (MemSize(*ppbBuffer) < cbTarget) - { - LPVOID pv = MemReAlloc(*ppbBuffer, cbTarget, TRUE); - BuffExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate buffer."); - *ppbBuffer = (BYTE*)pv; - } - } - else - { - *ppbBuffer = (BYTE*)MemAlloc(cbTarget, TRUE); - BuffExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer."); - } - -LExit: - return hr; -} diff --git a/src/dutil/build/WixToolset.DUtil.props b/src/dutil/build/WixToolset.DUtil.props deleted file mode 100644 index 35749be8..00000000 --- a/src/dutil/build/WixToolset.DUtil.props +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) - - - $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) - - - - - $(MSBuildThisFileDirectory)native\v140\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) - - - - - $(MSBuildThisFileDirectory)native\v141\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) - - - - - $(MSBuildThisFileDirectory)native\v142\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) - - - diff --git a/src/dutil/butil.cpp b/src/dutil/butil.cpp deleted file mode 100644 index e04b52e9..00000000 --- a/src/dutil/butil.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define ButilExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) -#define ButilExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) -#define ButilExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) -#define ButilExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) -#define ButilExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) -#define ButilExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) -#define ButilExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__) -#define ButilExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__) -#define ButilExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__) -#define ButilExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__) -#define ButilExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUTIL, e, x, s, __VA_ARGS__) - -// constants -// From engine/registration.h -const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; -const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; -const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; - -// Forward declarations. -/******************************************************************** -OpenBundleKey - Opens the bundle uninstallation key for a given bundle - -NOTE: caller is responsible for closing key -********************************************************************/ -static HRESULT OpenBundleKey( - __in_z LPCWSTR wzBundleId, - __in BUNDLE_INSTALL_CONTEXT context, - __inout HKEY *key); - - -extern "C" HRESULT DAPI BundleGetBundleInfo( - __in_z LPCWSTR wzBundleId, - __in_z LPCWSTR wzAttribute, - __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, - __inout_opt LPDWORD pcchValueBuf - ) -{ - Assert(wzBundleId && wzAttribute); - - HRESULT hr = S_OK; - BUNDLE_INSTALL_CONTEXT context = BUNDLE_INSTALL_CONTEXT_MACHINE; - LPWSTR sczValue = NULL; - HKEY hkBundle = NULL; - DWORD cchSource = 0; - DWORD dwType = 0; - DWORD dwValue = 0; - - if ((lpValueBuf && !pcchValueBuf) || !wzBundleId || !wzAttribute) - { - ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); - } - - if (FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_MACHINE, &hkBundle)) && - FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_USER, &hkBundle))) - { - ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path."); - } - - // If the bundle doesn't have the property defined, return ERROR_UNKNOWN_PROPERTY - hr = RegGetType(hkBundle, wzAttribute, &dwType); - ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) : hr, "Failed to locate bundle property."); - - switch (dwType) - { - case REG_SZ: - hr = RegReadString(hkBundle, wzAttribute, &sczValue); - ButilExitOnFailure(hr, "Failed to read string property."); - break; - case REG_DWORD: - hr = RegReadNumber(hkBundle, wzAttribute, &dwValue); - ButilExitOnFailure(hr, "Failed to read dword property."); - - hr = StrAllocFormatted(&sczValue, L"%d", dwValue); - ButilExitOnFailure(hr, "Failed to format dword property as string."); - break; - default: - ButilExitOnFailure(hr = E_NOTIMPL, "Reading bundle info of type 0x%x not implemented.", dwType); - - } - - hr = ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); - ButilExitOnFailure(hr, "Failed to calculate length of string"); - - if (lpValueBuf) - { - // cchSource is the length of the string not including the terminating null character - if (*pcchValueBuf <= cchSource) - { - *pcchValueBuf = ++cchSource; - ButilExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA), "A buffer is too small to hold the requested data."); - } - - hr = ::StringCchCatNExW(lpValueBuf, *pcchValueBuf, sczValue, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer."); - - *pcchValueBuf = cchSource++; - } - -LExit: - ReleaseRegKey(hkBundle); - ReleaseStr(sczValue); - - return hr; -} - -HRESULT DAPI BundleEnumRelatedBundle( - __in_z LPCWSTR wzUpgradeCode, - __in BUNDLE_INSTALL_CONTEXT context, - __inout PDWORD pdwStartIndex, - __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf - ) -{ - HRESULT hr = S_OK; - HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; - HKEY hkUninstall = NULL; - HKEY hkBundle = NULL; - LPWSTR sczUninstallSubKey = NULL; - DWORD cchUninstallSubKey = 0; - LPWSTR sczUninstallSubKeyPath = NULL; - LPWSTR sczValue = NULL; - DWORD dwType = 0; - - LPWSTR* rgsczBundleUpgradeCodes = NULL; - DWORD cBundleUpgradeCodes = 0; - BOOL fUpgradeCodeFound = FALSE; - - if (!wzUpgradeCode || !lpBundleIdBuf || !pdwStartIndex) - { - ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); - } - - hr = RegOpen(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstall); - ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); - - for (DWORD dwIndex = *pdwStartIndex; !fUpgradeCodeFound; dwIndex++) - { - hr = RegKeyEnum(hkUninstall, dwIndex, &sczUninstallSubKey); - ButilExitOnFailure(hr, "Failed to enumerate bundle uninstall key path."); - - hr = StrAllocFormatted(&sczUninstallSubKeyPath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, sczUninstallSubKey); - ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); - - hr = RegOpen(hkRoot, sczUninstallSubKeyPath, KEY_READ, &hkBundle); - ButilExitOnFailure(hr, "Failed to open uninstall key path."); - - // If it's a bundle, it should have a BundleUpgradeCode value of type REG_SZ (old) or REG_MULTI_SZ - hr = RegGetType(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &dwType); - if (FAILED(hr)) - { - ReleaseRegKey(hkBundle); - ReleaseNullStr(sczUninstallSubKey); - ReleaseNullStr(sczUninstallSubKeyPath); - // Not a bundle - continue; - } - - switch (dwType) - { - case REG_SZ: - hr = RegReadString(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &sczValue); - ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode string property."); - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, sczValue, -1, wzUpgradeCode, -1)) - { - *pdwStartIndex = dwIndex; - fUpgradeCodeFound = TRUE; - break; - } - - ReleaseNullStr(sczValue); - - break; - case REG_MULTI_SZ: - hr = RegReadStringArray(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczBundleUpgradeCodes, &cBundleUpgradeCodes); - ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode multi-string property."); - - for (DWORD i = 0; i < cBundleUpgradeCodes; i++) - { - LPWSTR wzBundleUpgradeCode = rgsczBundleUpgradeCodes[i]; - if (wzBundleUpgradeCode && *wzBundleUpgradeCode) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzBundleUpgradeCode, -1, wzUpgradeCode, -1)) - { - *pdwStartIndex = dwIndex; - fUpgradeCodeFound = TRUE; - break; - } - } - } - ReleaseNullStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes); - - break; - - default: - ButilExitOnFailure(hr = E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType); - - } - - if (fUpgradeCodeFound) - { - if (lpBundleIdBuf) - { - hr = ::StringCchLengthW(sczUninstallSubKey, STRSAFE_MAX_CCH, reinterpret_cast(&cchUninstallSubKey)); - ButilExitOnFailure(hr, "Failed to calculate length of string"); - - hr = ::StringCchCopyNExW(lpBundleIdBuf, MAX_GUID_CHARS + 1, sczUninstallSubKey, cchUninstallSubKey, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer."); - } - - break; - } - - // Cleanup before next iteration - ReleaseRegKey(hkBundle); - ReleaseNullStr(sczUninstallSubKey); - ReleaseNullStr(sczUninstallSubKeyPath); - } - -LExit: - ReleaseStr(sczValue); - ReleaseStr(sczUninstallSubKey); - ReleaseStr(sczUninstallSubKeyPath); - ReleaseRegKey(hkBundle); - ReleaseRegKey(hkUninstall); - ReleaseStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes); - - return hr; -} - - -HRESULT OpenBundleKey( - __in_z LPCWSTR wzBundleId, - __in BUNDLE_INSTALL_CONTEXT context, - __inout HKEY *key) -{ - Assert(key && wzBundleId); - AssertSz(NULL == *key, "*key should be null"); - - HRESULT hr = S_OK; - HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; - LPWSTR sczKeypath = NULL; - - hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId); - ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); - - hr = RegOpen(hkRoot, sczKeypath, KEY_READ, key); - ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); - -LExit: - ReleaseStr(sczKeypath); - - return hr; -} diff --git a/src/dutil/cabcutil.cpp b/src/dutil/cabcutil.cpp deleted file mode 100644 index 93a9b7e1..00000000 --- a/src/dutil/cabcutil.cpp +++ /dev/null @@ -1,1539 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define CabcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) -#define CabcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) -#define CabcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) -#define CabcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) -#define CabcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) -#define CabcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) -#define CabcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__) -#define CabcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__) -#define CabcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__) -#define CabcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__) -#define CabcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABCUTIL, e, x, s, __VA_ARGS__) -#define CabcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABCUTIL, g, x, s, __VA_ARGS__) - - -static const WCHAR CABC_MAGIC_UNICODE_STRING_MARKER = '?'; -static const DWORD MAX_CABINET_HEADER_SIZE = 16 * 1024 * 1024; - -// The minimum number of uncompressed bytes between FciFlushFolder() calls - if we call FciFlushFolder() -// too often (because of duplicates too close together) we theoretically ruin our compression ratio - -// left at zero to maximize install-time performance, because even a small minimum threshhold seems to -// have a high install-time performance cost for little or no size benefit. The value is left here for -// tweaking though - possible suggested values are 524288 for 512K, or 2097152 for 2MB. -static const DWORD MINFLUSHTHRESHHOLD = 0; - -// structs -struct MS_CABINET_HEADER -{ - DWORD sig; - DWORD csumHeader; - DWORD cbCabinet; - DWORD csumFolders; - DWORD coffFiles; - DWORD csumFiles; - WORD version; - WORD cFolders; - WORD cFiles; - WORD flags; - WORD setID; - WORD iCabinet; -}; - - -struct MS_CABINET_ITEM -{ - DWORD cbFile; - DWORD uoffFolderStart; - WORD iFolder; - WORD date; - WORD time; - WORD attribs; -}; - -struct CABC_INTERNAL_ADDFILEINFO -{ - LPCWSTR wzSourcePath; - LPCWSTR wzEmptyPath; -}; - -struct CABC_DUPLICATEFILE -{ - DWORD dwFileArrayIndex; - DWORD dwDuplicateCabFileIndex; - LPWSTR pwzSourcePath; - LPWSTR pwzToken; -}; - - -struct CABC_FILE -{ - DWORD dwCabFileIndex; - LPWSTR pwzSourcePath; - LPWSTR pwzToken; - PMSIFILEHASHINFO pmfHash; - LONGLONG llFileSize; - BOOL fHasDuplicates; -}; - - -struct CABC_DATA -{ - LONGLONG llBytesSinceLastFlush; - LONGLONG llFlushThreshhold; - - STRINGDICT_HANDLE shDictHandle; - - WCHAR wzCabinetPath[MAX_PATH]; - WCHAR wzEmptyFile[MAX_PATH]; - HANDLE hEmptyFile; - DWORD dwLastFileIndex; - - DWORD cFilePaths; - DWORD cMaxFilePaths; - CABC_FILE *prgFiles; - - DWORD cDuplicates; - DWORD cMaxDuplicates; - CABC_DUPLICATEFILE *prgDuplicates; - - HRESULT hrLastError; - BOOL fGoodCab; - - HFCI hfci; - ERF erf; - CCAB ccab; - TCOMP tc; - - // Below Field are used for Cabinet Splitting - BOOL fCabinetSplittingEnabled; - FileSplitCabNamesCallback fileSplitCabNamesCallback; - WCHAR wzFirstCabinetName[MAX_PATH]; // Stores Name of First Cabinet excluding ".cab" extention to help generate other names by Splitting -}; - -const int CABC_HANDLE_BYTES = sizeof(CABC_DATA); - -// -// prototypes -// -static void FreeCabCData( - __in CABC_DATA* pcd - ); -static HRESULT CheckForDuplicateFile( - __in CABC_DATA *pcd, - __out CABC_FILE **ppcf, - __in LPCWSTR wzFileName, - __in PMSIFILEHASHINFO *ppmfHash, - __in LONGLONG llFileSize - ); -static HRESULT AddDuplicateFile( - __in CABC_DATA *pcd, - __in DWORD dwFileArrayIndex, - __in_z LPCWSTR wzSourcePath, - __in_opt LPCWSTR wzToken, - __in DWORD dwDuplicateCabFileIndex - ); -static HRESULT AddNonDuplicateFile( - __in CABC_DATA *pcd, - __in LPCWSTR wzFile, - __in_opt LPCWSTR wzToken, - __in_opt const MSIFILEHASHINFO* pmfHash, - __in LONGLONG llFileSize, - __in DWORD dwCabFileIndex - ); -static HRESULT UpdateDuplicateFiles( - __in const CABC_DATA *pcd - ); -static HRESULT DuplicateFile( - __in MS_CABINET_HEADER *pHeader, - __in const CABC_DATA *pcd, - __in const CABC_DUPLICATEFILE *pDuplicate - ); -static HRESULT UtcFileTimeToLocalDosDateTime( - __in const FILETIME* pFileTime, - __out USHORT* pDate, - __out USHORT* pTime - ); - -static __callback int DIAMONDAPI CabCFilePlaced(__in PCCAB pccab, __in_z PSTR szFile, __in long cbFile, __in BOOL fContinuation, __inout_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback void * DIAMONDAPI CabCAlloc(__in ULONG cb); -static __callback void DIAMONDAPI CabCFree(__out_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback INT_PTR DIAMONDAPI CabCOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback UINT FAR DIAMONDAPI CabCRead(__in INT_PTR hf, __out_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback UINT FAR DIAMONDAPI CabCWrite(__in INT_PTR hf, __in_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback long FAR DIAMONDAPI CabCSeek(__in INT_PTR hf, __in long dist, __in int seektype, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback int FAR DIAMONDAPI CabCClose(__in INT_PTR hf, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback int DIAMONDAPI CabCDelete(__in_z PSTR szFile, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); -__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetTempFile(__out_bcount_z(cbFile) char *szFile, __in int cbFile, __inout_bcount(CABC_HANDLE_BYTES) void *pv); -__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetNextCabinet(__in PCCAB pccab, __in ULONG ul, __out_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback INT_PTR DIAMONDAPI CabCGetOpenInfo(__in_z PSTR pszName, __out USHORT *pdate, __out USHORT *ptime, __out USHORT *pattribs, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); -static __callback long DIAMONDAPI CabCStatus(__in UINT uiTypeStatus, __in ULONG cb1, __in ULONG cb2, __inout_bcount(CABC_HANDLE_BYTES) void *pv); - - -/******************************************************************** -CabcBegin - begins creating a cabinet - -NOTE: phContext must be the same handle used in AddFile and Finish. - wzCabDir can be L"", but not NULL. - dwMaxSize and dwMaxThresh can be 0. A large default value will be used in that case. - -********************************************************************/ -extern "C" HRESULT DAPI CabCBegin( - __in_z LPCWSTR wzCab, - __in_z LPCWSTR wzCabDir, - __in DWORD dwMaxFiles, - __in DWORD dwMaxSize, - __in DWORD dwMaxThresh, - __in COMPRESSION_TYPE ct, - __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext - ) -{ - Assert(wzCab && *wzCab && phContext); - - HRESULT hr = S_OK; - CABC_DATA *pcd = NULL; - WCHAR wzTempPath[MAX_PATH] = { }; - - C_ASSERT(sizeof(MSIFILEHASHINFO) == 20); - - WCHAR wzPathBuffer [MAX_PATH] = L""; - size_t cchPathBuffer; - if (wzCabDir) - { - hr = ::StringCchLengthW(wzCabDir, MAX_PATH, &cchPathBuffer); - CabcExitOnFailure(hr, "Failed to get length of cab directory"); - - // Need room to terminate with L'\\' and L'\0' - if((MAX_PATH - 1) <= cchPathBuffer || 0 == cchPathBuffer) - { - hr = E_INVALIDARG; - CabcExitOnFailure(hr, "Cab directory had invalid length: %u", cchPathBuffer); - } - - hr = ::StringCchCopyW(wzPathBuffer, countof(wzPathBuffer), wzCabDir); - CabcExitOnFailure(hr, "Failed to copy cab directory to buffer"); - - if (L'\\' != wzPathBuffer[cchPathBuffer - 1]) - { - hr = ::StringCchCatW(wzPathBuffer, countof(wzPathBuffer), L"\\"); - CabcExitOnFailure(hr, "Failed to cat \\ to end of buffer"); - ++cchPathBuffer; - } - } - - pcd = static_cast(MemAlloc(sizeof(CABC_DATA), TRUE)); - CabcExitOnNull(pcd, hr, E_OUTOFMEMORY, "failed to allocate cab creation data structure"); - - pcd->hrLastError = S_OK; - pcd->fGoodCab = TRUE; - pcd->llFlushThreshhold = MINFLUSHTHRESHHOLD; - - pcd->hEmptyFile = INVALID_HANDLE_VALUE; - - pcd->fileSplitCabNamesCallback = NULL; - - if (NULL == dwMaxSize) - { - pcd->ccab.cb = CAB_MAX_SIZE; - pcd->fCabinetSplittingEnabled = FALSE; // If no max cab size is supplied, cabinet splitting is not desired - } - else - { - pcd->ccab.cb = dwMaxSize * 1024 * 1024; - pcd->fCabinetSplittingEnabled = TRUE; - } - - if (0 == dwMaxThresh) - { - // Subtract 16 to magically make cabbing of uncompressed data larger than 2GB work. - pcd->ccab.cbFolderThresh = CAB_MAX_SIZE - 16; - } - else - { - pcd->ccab.cbFolderThresh = dwMaxThresh; - } - - // Translate the compression type - if (COMPRESSION_TYPE_NONE == ct) - { - pcd->tc = tcompTYPE_NONE; - } - else if (COMPRESSION_TYPE_LOW == ct) - { - pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_LO; - } - else if (COMPRESSION_TYPE_MEDIUM == ct) - { - pcd->tc = TCOMPfromLZXWindow(18); - } - else if (COMPRESSION_TYPE_HIGH == ct) - { - pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_HI; - } - else if (COMPRESSION_TYPE_MSZIP == ct) - { - pcd->tc = tcompTYPE_MSZIP; - } - else - { - hr = E_INVALIDARG; - CabcExitOnFailure(hr, "Invalid compression type specified."); - } - - if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzCab, -1, pcd->ccab.szCab, sizeof(pcd->ccab.szCab), NULL, NULL)) - { - CabcExitWithLastError(hr, "failed to convert cab name to multi-byte"); - } - - if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzPathBuffer, -1, pcd->ccab.szCabPath, sizeof(pcd->ccab.szCab), NULL, NULL)) - { - CabcExitWithLastError(hr, "failed to convert cab dir to multi-byte"); - } - - // Remember the path to the cabinet. - hr= ::StringCchCopyW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzPathBuffer); - CabcExitOnFailure(hr, "Failed to copy cabinet path from path: %ls", wzPathBuffer); - - hr = ::StringCchCatW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzCab); - CabcExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab); - - // Get the empty file to use as the blank marker for duplicates. - if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) - { - CabcExitWithLastError(hr, "Failed to get temp path."); - } - - if (!::GetTempFileNameW(wzTempPath, L"WSC", 0, pcd->wzEmptyFile)) - { - CabcExitWithLastError(hr, "Failed to create a temp file name."); - } - - // Try to open the newly created empty file (remember, GetTempFileName() is kind enough to create a file for us) - // with a handle to automatically delete the file on close. Ignore any failure that might happen, since the worst - // case is we'll leave a zero byte file behind in the temp folder. - pcd->hEmptyFile = ::CreateFileW(pcd->wzEmptyFile, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); - - hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_CASEINSENSITIVE); - CabcExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files"); - - // Make sure to allocate at least some space, or we won't be able to realloc later if they "lied" about having zero files - if (1 > dwMaxFiles) - { - dwMaxFiles = 1; - } - - pcd->cMaxFilePaths = dwMaxFiles; - size_t cbFileAllocSize = 0; - - hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &(cbFileAllocSize)); - CabcExitOnFailure(hr, "Maximum allocation exceeded on initialization."); - - pcd->prgFiles = static_cast(MemAlloc(cbFileAllocSize, TRUE)); - CabcExitOnNull(pcd->prgFiles, hr, E_OUTOFMEMORY, "Failed to allocate memory for files."); - - // Tell cabinet API about our configuration. - pcd->hfci = ::FCICreate(&(pcd->erf), CabCFilePlaced, CabCAlloc, CabCFree, CabCOpen, CabCRead, CabCWrite, CabCClose, CabCSeek, CabCDelete, CabCGetTempFile, &(pcd->ccab), pcd); - if (NULL == pcd->hfci || pcd->erf.fError) - { - // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error - if (FAILED(pcd->hrLastError)) - { - hr = pcd->hrLastError; - } - else - { - CabcExitWithLastError(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); - } - - pcd->fGoodCab = FALSE; - - CabcExitOnFailure(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); // TODO: can these be converted to HRESULTS? - } - - *phContext = pcd; - -LExit: - if (FAILED(hr) && pcd && pcd->hfci) - { - ::FCIDestroy(pcd->hfci); - } - - return hr; -} - - -/******************************************************************** -CabCNextCab - This will be useful when creating multiple cabs. -Haven't needed it yet. -********************************************************************/ -extern "C" HRESULT DAPI CabCNextCab( - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext - ) -{ - UNREFERENCED_PARAMETER(hContext); - // TODO: Make the appropriate FCIFlushCabinet and FCIFlushFolder calls - return E_NOTIMPL; -} - - -/******************************************************************** -CabcAddFile - adds a file to a cabinet - -NOTE: hContext must be the same used in Begin and Finish -if wzToken is null, the file's original name is used within the cab -********************************************************************/ -extern "C" HRESULT DAPI CabCAddFile( - __in_z LPCWSTR wzFile, - __in_z_opt LPCWSTR wzToken, - __in_opt PMSIFILEHASHINFO pmfHash, - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext - ) -{ - Assert(wzFile && *wzFile && hContext); - - HRESULT hr = S_OK; - CABC_DATA *pcd = reinterpret_cast(hContext); - CABC_FILE *pcfDuplicate = NULL; - LONGLONG llFileSize = 0; - PMSIFILEHASHINFO pmfLocalHash = pmfHash; - - // Use Smart Cabbing if there are duplicates and if Cabinet Splitting is not desired - // For Cabinet Spliting avoid hashing as Smart Cabbing is disabled - if(!pcd->fCabinetSplittingEnabled) - { - // Store file size, primarily used to determine which files to hash for duplicates - hr = FileSize(wzFile, &llFileSize); - CabcExitOnFailure(hr, "Failed to check size of file %ls", wzFile); - - hr = CheckForDuplicateFile(pcd, &pcfDuplicate, wzFile, &pmfLocalHash, llFileSize); - CabcExitOnFailure(hr, "Failed while checking for duplicate of file: %ls", wzFile); - } - - if (pcfDuplicate) // This will be null for smart cabbing case - { - DWORD index; - hr = ::PtrdiffTToDWord(pcfDuplicate - pcd->prgFiles, &index); - CabcExitOnFailure(hr, "Failed to calculate index of file name: %ls", pcfDuplicate->pwzSourcePath); - - hr = AddDuplicateFile(pcd, index, wzFile, wzToken, pcd->dwLastFileIndex); - CabcExitOnFailure(hr, "Failed to add duplicate of file name: %ls", pcfDuplicate->pwzSourcePath); - } - else - { - hr = AddNonDuplicateFile(pcd, wzFile, wzToken, pmfLocalHash, llFileSize, pcd->dwLastFileIndex); - CabcExitOnFailure(hr, "Failed to add non-duplicated file: %ls", wzFile); - } - - ++pcd->dwLastFileIndex; - -LExit: - // If we allocated a hash struct ourselves, free it - if (pmfHash != pmfLocalHash) - { - ReleaseMem(pmfLocalHash); - } - - return hr; -} - - -/******************************************************************** -CabcFinish - finishes making a cabinet - -NOTE: hContext must be the same used in Begin and AddFile -*********************************************************************/ -extern "C" HRESULT DAPI CabCFinish( - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext, - __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback - ) -{ - Assert(hContext); - - HRESULT hr = S_OK; - CABC_DATA *pcd = reinterpret_cast(hContext); - CABC_INTERNAL_ADDFILEINFO fileInfo = { }; - DWORD dwCabFileIndex; // Total file index, counts up to pcd->dwLastFileIndex - DWORD dwArrayFileIndex = 0; // Index into pcd->prgFiles[] array - DWORD dwDupeArrayFileIndex = 0; // Index into pcd->prgDuplicates[] array - LPSTR pszFileToken = NULL; - LONGLONG llFileSize = 0; - - pcd->fileSplitCabNamesCallback = fileSplitCabNamesCallback; - - // These are used to determine whether to call FciFlushFolder() before or after the next call to FciAddFile() - // doing so at appropriate times results in install-time performance benefits in the case of duplicate files. - // Basically, when MSI has to extract files out of order (as it does due to our smart cabbing), it can't just jump - // exactly to the out of order file, it must begin extracting all over again, starting from that file's CAB folder - // (this is not the same as a regular folder, and is a concept unique to CABs). - - // This means MSI spends a lot of time extracting the same files twice, especially if the duplicate file has many files - // before it in the CAB folder. To avoid this, we want to make sure whenever MSI jumps to another file in the CAB, that - // file is at the beginning of its own folder, so no extra files need to be extracted. FciFlushFolder() causes the CAB - // to close the current folder, and create a new folder for the next file to be added. - - // So to maximize our performance benefit, we must call FciFlushFolder() at every place MSI will jump "to" in the CAB sequence. - // So, we call FciFlushFolder() before adding the original version of a duplicated file (as this will be jumped "to") - // And we call FciFlushFolder() after adding the duplicate versions of files (as this will be jumped back "to" to get back in the regular sequence) - BOOL fFlushBefore = FALSE; - BOOL fFlushAfter = FALSE; - - ReleaseDict(pcd->shDictHandle); - - // We need to go through all the files, duplicates and non-duplicates, sequentially in the order they were added - for (dwCabFileIndex = 0; dwCabFileIndex < pcd->dwLastFileIndex; ++dwCabFileIndex) - { - if (dwArrayFileIndex < pcd->cMaxFilePaths && pcd->prgFiles[dwArrayFileIndex].dwCabFileIndex == dwCabFileIndex) // If it's a non-duplicate file - { - // Just a normal, non-duplicated file. We'll add it to the list for later checking of - // duplicates. - fileInfo.wzSourcePath = pcd->prgFiles[dwArrayFileIndex].pwzSourcePath; - fileInfo.wzEmptyPath = NULL; - - // Use the provided token, otherwise default to the source file name. - if (pcd->prgFiles[dwArrayFileIndex].pwzToken) - { - LPCWSTR pwzTemp = pcd->prgFiles[dwArrayFileIndex].pwzToken; - hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); - CabcExitOnFailure(hr, "failed to convert file token to ANSI: %ls", pwzTemp); - } - else - { - LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath); - hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); - CabcExitOnFailure(hr, "failed to convert file name to ANSI: %ls", pwzTemp); - } - - if (pcd->prgFiles[dwArrayFileIndex].fHasDuplicates) - { - fFlushBefore = TRUE; - } - - llFileSize = pcd->prgFiles[dwArrayFileIndex].llFileSize; - - ++dwArrayFileIndex; // Increment into the non-duplicate array - } - else if (dwDupeArrayFileIndex < pcd->cMaxDuplicates && pcd->prgDuplicates[dwDupeArrayFileIndex].dwDuplicateCabFileIndex == dwCabFileIndex) // If it's a duplicate file - { - // For duplicate files, we point them at our empty (zero-byte) file so it takes up no space - // in the resultant cabinet. Later on (CabCFinish) we'll go through and change all the zero - // byte files to point at their duplicated file index. - // - // Notice that duplicate files are not added to the list of file paths because all duplicate - // files point at the same path (the empty file) so there is no point in tracking them with - // their path. - fileInfo.wzSourcePath = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzSourcePath; - fileInfo.wzEmptyPath = pcd->wzEmptyFile; - - // Use the provided token, otherwise default to the source file name. - if (pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken) - { - LPCWSTR pwzTemp = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken; - hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); - CabcExitOnFailure(hr, "failed to convert duplicate file token to ANSI: %ls", pwzTemp); - } - else - { - LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath); - hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); - CabcExitOnFailure(hr, "failed to convert duplicate file name to ANSI: %ls", pwzTemp); - } - - // Flush afterward only if this isn't a duplicate of the previous file, and at least one non-duplicate file remains to be added to the cab - if (!(dwCabFileIndex - 1 == pcd->prgFiles[pcd->prgDuplicates[dwDupeArrayFileIndex].dwFileArrayIndex].dwCabFileIndex) && - !(dwDupeArrayFileIndex > 0 && dwCabFileIndex - 1 == pcd->prgDuplicates[dwDupeArrayFileIndex - 1].dwDuplicateCabFileIndex) && - dwArrayFileIndex < pcd->cFilePaths) - { - fFlushAfter = TRUE; - } - - // We're just adding a 0-byte file, so set it appropriately - llFileSize = 0; - - ++dwDupeArrayFileIndex; // Increment into the duplicate array - } - else // If it's neither duplicate nor non-duplicate, throw an error - { - hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT); - CabcExitOnRootFailure(hr, "Internal inconsistency in data structures while creating CAB file - a non-standard, non-duplicate file was encountered"); - } - - if (fFlushBefore && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold) - { - if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus)) - { - CabcExitWithLastError(hr, "failed to flush FCI folder before adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); - } - pcd->llBytesSinceLastFlush = 0; - } - - pcd->llBytesSinceLastFlush += llFileSize; - - // Add the file to the cab. Notice that we are passing our CABC_INTERNAL_ADDFILEINFO struct - // through the pointer to an ANSI string. This is neccessary so we can smuggle through the - // path to the empty file (should this be a duplicate file). -#pragma prefast(push) -#pragma prefast(disable:6387) // OACR is silly, pszFileToken can't be false here - if (!::FCIAddFile(pcd->hfci, reinterpret_cast(&fileInfo), pszFileToken, FALSE, CabCGetNextCabinet, CabCStatus, CabCGetOpenInfo, pcd->tc)) -#pragma prefast(pop) - { - pcd->fGoodCab = FALSE; - - // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error - if (FAILED(pcd->hrLastError)) - { - hr = pcd->hrLastError; - } - else - { - CabcExitWithLastError(hr, "failed to add file to FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); - } - - CabcExitOnFailure(hr, "failed to add file to FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); // TODO: can these be converted to HRESULTS? - } - - // For Cabinet Splitting case, check for pcd->hrLastError that may be set as result of Error in CabCGetNextCabinet - // This is required as returning False in CabCGetNextCabinet is not aborting cabinet creation and is reporting success instead - if (pcd->fCabinetSplittingEnabled && FAILED(pcd->hrLastError)) - { - hr = pcd->hrLastError; - CabcExitOnFailure(hr, "Failed to create next cabinet name while splitting cabinet."); - } - - if (fFlushAfter && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold) - { - if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus)) - { - CabcExitWithLastError(hr, "failed to flush FCI folder after adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); - } - pcd->llBytesSinceLastFlush = 0; - } - - fFlushAfter = FALSE; - fFlushBefore = FALSE; - } - - if (!pcd->fGoodCab) - { - // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error - if (FAILED(pcd->hrLastError)) - { - hr = pcd->hrLastError; - } - else - { - CabcExitWithLastError(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); - } - - CabcExitOnFailure(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); // TODO: can these be converted to HRESULTS? - } - - // Only flush the cabinet if we actually succeeded in previous calls - otherwise we just waste time (a lot on big cabs) - if (!::FCIFlushCabinet(pcd->hfci, FALSE, CabCGetNextCabinet, CabCStatus)) - { - // If we have a last error, use that, otherwise return the useless error - hr = FAILED(pcd->hrLastError) ? pcd->hrLastError : E_FAIL; - CabcExitOnFailure(hr, "failed to flush FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); // TODO: can these be converted to HRESULTS? - } - - if (pcd->fGoodCab && pcd->cDuplicates) - { - hr = UpdateDuplicateFiles(pcd); - CabcExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->wzCabinetPath); - } - -LExit: - ::FCIDestroy(pcd->hfci); - FreeCabCData(pcd); - ReleaseNullStr(pszFileToken); - - return hr; -} - - -/******************************************************************** -CabCCancel - cancels making a cabinet - -NOTE: hContext must be the same used in Begin and AddFile -*********************************************************************/ -extern "C" void DAPI CabCCancel( - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext - ) -{ - Assert(hContext); - - CABC_DATA* pcd = reinterpret_cast(hContext); - ::FCIDestroy(pcd->hfci); - FreeCabCData(pcd); -} - - -// -// private -// - -static void FreeCabCData( - __in CABC_DATA* pcd - ) -{ - if (pcd) - { - ReleaseFileHandle(pcd->hEmptyFile); - - for (DWORD i = 0; i < pcd->cFilePaths; ++i) - { - ReleaseStr(pcd->prgFiles[i].pwzSourcePath); - ReleaseMem(pcd->prgFiles[i].pmfHash); - } - ReleaseMem(pcd->prgFiles); - ReleaseMem(pcd->prgDuplicates); - - ReleaseMem(pcd); - } -} - -/******************************************************************** - SmartCab functions - -********************************************************************/ - -static HRESULT CheckForDuplicateFile( - __in CABC_DATA *pcd, - __out CABC_FILE **ppcf, - __in LPCWSTR wzFileName, - __in PMSIFILEHASHINFO *ppmfHash, - __in LONGLONG llFileSize - ) -{ - DWORD i; - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - - CabcExitOnNull(ppcf, hr, E_INVALIDARG, "No file structure sent while checking for duplicate file"); - CabcExitOnNull(ppmfHash, hr, E_INVALIDARG, "No file hash structure pointer sent while checking for duplicate file"); - - *ppcf = NULL; // By default, we'll set our output to NULL - - hr = DictGetValue(pcd->shDictHandle, wzFileName, reinterpret_cast(ppcf)); - // If we found it in the hash of previously added source paths, return our match immediately - if (SUCCEEDED(hr)) - { - ExitFunction1(hr = S_OK); - } - else if (E_NOTFOUND == hr) - { - hr = S_OK; - } - CabcExitOnFailure(hr, "Failed while searching for file in dictionary of previously added files"); - - for (i = 0; i < pcd->cFilePaths; ++i) - { - // If two files have the same size, use hashing to check if they're a match - if (llFileSize == pcd->prgFiles[i].llFileSize) - { - // If pcd->prgFiles[i], our potential match, hasn't been hashed yet, hash it - if (pcd->prgFiles[i].pmfHash == NULL) - { - pcd->prgFiles[i].pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); - CabcExitOnNull(pcd->prgFiles[i].pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for candidate duplicate file's MSI file hash"); - - pcd->prgFiles[i].pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); - er = ::MsiGetFileHashW(pcd->prgFiles[i].pwzSourcePath, 0, pcd->prgFiles[i].pmfHash); - CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of candidate duplicate file: %ls", pcd->prgFiles[i].pwzSourcePath); - } - - // If our own file hasn't yet been hashed, hash it - if (NULL == *ppmfHash) - { - *ppmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); - CabcExitOnNull(*ppmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for file's MSI file hash"); - - (*ppmfHash)->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); - er = ::MsiGetFileHashW(wzFileName, 0, *ppmfHash); - CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of file: %ls", pcd->prgFiles[i].pwzSourcePath); - } - - // If the two file hashes are both of the expected size, and they match, we've got a match, so return it! - if (pcd->prgFiles[i].pmfHash->dwFileHashInfoSize == (*ppmfHash)->dwFileHashInfoSize && - sizeof(MSIFILEHASHINFO) == (*ppmfHash)->dwFileHashInfoSize && - pcd->prgFiles[i].pmfHash->dwData[0] == (*ppmfHash)->dwData[0] && - pcd->prgFiles[i].pmfHash->dwData[1] == (*ppmfHash)->dwData[1] && - pcd->prgFiles[i].pmfHash->dwData[2] == (*ppmfHash)->dwData[2] && - pcd->prgFiles[i].pmfHash->dwData[3] == (*ppmfHash)->dwData[3]) - { - *ppcf = pcd->prgFiles + i; - ExitFunction1(hr = S_OK); - } - } - } - -LExit: - - return hr; -} - - -static HRESULT AddDuplicateFile( - __in CABC_DATA *pcd, - __in DWORD dwFileArrayIndex, - __in_z LPCWSTR wzSourcePath, - __in_opt LPCWSTR wzToken, - __in DWORD dwDuplicateCabFileIndex - ) -{ - HRESULT hr = S_OK; - LPVOID pv = NULL; - - // Ensure there is enough memory to store this duplicate file index. - if (pcd->cDuplicates == pcd->cMaxDuplicates) - { - pcd->cMaxDuplicates += 20; // grow by a reasonable number (20 is reasonable, right?) - size_t cbDuplicates = 0; - - hr = ::SizeTMult(pcd->cMaxDuplicates, sizeof(CABC_DUPLICATEFILE), &cbDuplicates); - CabcExitOnFailure(hr, "Maximum allocation exceeded."); - - if (pcd->cDuplicates) - { - pv = MemReAlloc(pcd->prgDuplicates, cbDuplicates, FALSE); - CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for duplicate file."); - } - else - { - pv = MemAlloc(cbDuplicates, FALSE); - CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for duplicate file."); - } - - ZeroMemory(reinterpret_cast(pv) + (pcd->cDuplicates * sizeof(CABC_DUPLICATEFILE)), (pcd->cMaxDuplicates - pcd->cDuplicates) * sizeof(CABC_DUPLICATEFILE)); - - pcd->prgDuplicates = static_cast(pv); - pv = NULL; - } - - // Store the duplicate file index. - pcd->prgDuplicates[pcd->cDuplicates].dwFileArrayIndex = dwFileArrayIndex; - pcd->prgDuplicates[pcd->cDuplicates].dwDuplicateCabFileIndex = dwDuplicateCabFileIndex; - pcd->prgFiles[dwFileArrayIndex].fHasDuplicates = TRUE; // Mark original file as having duplicates - - hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzSourcePath, wzSourcePath, 0); - CabcExitOnFailure(hr, "Failed to copy duplicate file path: %ls", wzSourcePath); - - if (wzToken && *wzToken) - { - hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzToken, wzToken, 0); - CabcExitOnFailure(hr, "Failed to copy duplicate file token: %ls", wzToken); - } - - ++pcd->cDuplicates; - -LExit: - ReleaseMem(pv); - return hr; -} - - -static HRESULT AddNonDuplicateFile( - __in CABC_DATA *pcd, - __in LPCWSTR wzFile, - __in_opt LPCWSTR wzToken, - __in_opt const MSIFILEHASHINFO* pmfHash, - __in LONGLONG llFileSize, - __in DWORD dwCabFileIndex - ) -{ - HRESULT hr = S_OK; - LPVOID pv = NULL; - - // Ensure there is enough memory to store this file index. - if (pcd->cFilePaths == pcd->cMaxFilePaths) - { - pcd->cMaxFilePaths += 100; // grow by a reasonable number (100 is reasonable, right?) - size_t cbFilePaths = 0; - - hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &cbFilePaths); - CabcExitOnFailure(hr, "Maximum allocation exceeded."); - - pv = MemReAlloc(pcd->prgFiles, cbFilePaths, FALSE); - CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for file."); - - ZeroMemory(reinterpret_cast(pv) + (pcd->cFilePaths * sizeof(CABC_FILE)), (pcd->cMaxFilePaths - pcd->cFilePaths) * sizeof(CABC_FILE)); - - pcd->prgFiles = static_cast(pv); - pv = NULL; - } - - // Store the file index information. - // TODO: add this to a sorted list so we can do a binary search later. - CABC_FILE *pcf = pcd->prgFiles + pcd->cFilePaths; - pcf->dwCabFileIndex = dwCabFileIndex; - pcf->llFileSize = llFileSize; - - if (pmfHash && sizeof(MSIFILEHASHINFO) == pmfHash->dwFileHashInfoSize) - { - pcf->pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); - CabcExitOnNull(pcf->pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for individual file's MSI file hash"); - - pcf->pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); - pcf->pmfHash->dwData[0] = pmfHash->dwData[0]; - pcf->pmfHash->dwData[1] = pmfHash->dwData[1]; - pcf->pmfHash->dwData[2] = pmfHash->dwData[2]; - pcf->pmfHash->dwData[3] = pmfHash->dwData[3]; - } - - hr = StrAllocString(&pcf->pwzSourcePath, wzFile, 0); - CabcExitOnFailure(hr, "Failed to copy file path: %ls", wzFile); - - if (wzToken && *wzToken) - { - hr = StrAllocString(&pcf->pwzToken, wzToken, 0); - CabcExitOnFailure(hr, "Failed to copy file token: %ls", wzToken); - } - - ++pcd->cFilePaths; - - hr = DictAddValue(pcd->shDictHandle, pcf); - CabcExitOnFailure(hr, "Failed to add file to dictionary of added files"); - -LExit: - ReleaseMem(pv); - return hr; -} - - -static HRESULT UpdateDuplicateFiles( - __in const CABC_DATA *pcd - ) -{ - HRESULT hr = S_OK; - DWORD cbCabinet = 0; - LARGE_INTEGER liCabinetSize = { }; - HANDLE hCabinet = INVALID_HANDLE_VALUE; - HANDLE hCabinetMapping = NULL; - LPVOID pv = NULL; - MS_CABINET_HEADER *pCabinetHeader = NULL; - - hCabinet = ::CreateFileW(pcd->wzCabinetPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hCabinet) - { - CabcExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->wzCabinetPath); - } - - // Shouldn't need more than 16 MB to get the whole cabinet header into memory so use that as - // the upper bound for the memory map. - if (!::GetFileSizeEx(hCabinet, &liCabinetSize)) - { - CabcExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->wzCabinetPath); - } - - if (0 == liCabinetSize.HighPart && liCabinetSize.LowPart < MAX_CABINET_HEADER_SIZE) - { - cbCabinet = liCabinetSize.LowPart; - } - else - { - cbCabinet = MAX_CABINET_HEADER_SIZE; - } - - // CreateFileMapping() returns NULL on failure, not INVALID_HANDLE_VALUE - hCabinetMapping = ::CreateFileMappingW(hCabinet, NULL, PAGE_READWRITE | SEC_COMMIT, 0, cbCabinet, NULL); - if (NULL == hCabinetMapping || INVALID_HANDLE_VALUE == hCabinetMapping) - { - CabcExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->wzCabinetPath); - } - - pv = ::MapViewOfFile(hCabinetMapping, FILE_MAP_WRITE, 0, 0, 0); - CabcExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->wzCabinetPath); - - pCabinetHeader = static_cast(pv); - - for (DWORD i = 0; i < pcd->cDuplicates; ++i) - { - const CABC_DUPLICATEFILE *pDuplicateFile = pcd->prgDuplicates + i; - - hr = DuplicateFile(pCabinetHeader, pcd, pDuplicateFile); - CabcExitOnFailure(hr, "Failed to find cabinet file items at index: %d and %d", pDuplicateFile->dwFileArrayIndex, pDuplicateFile->dwDuplicateCabFileIndex); - } - -LExit: - if (pv) - { - ::UnmapViewOfFile(pv); - } - if (hCabinetMapping) - { - ::CloseHandle(hCabinetMapping); - } - ReleaseFileHandle(hCabinet); - - return hr; -} - - -static HRESULT DuplicateFile( - __in MS_CABINET_HEADER *pHeader, - __in const CABC_DATA *pcd, - __in const CABC_DUPLICATEFILE *pDuplicate - ) -{ - HRESULT hr = S_OK; - BYTE *pbHeader = reinterpret_cast(pHeader); - BYTE* pbItem = pbHeader + pHeader->coffFiles; - const MS_CABINET_ITEM *pOriginalItem = NULL; - MS_CABINET_ITEM *pDuplicateItem = NULL; - - if (pHeader->cFiles <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex || - pHeader->cFiles <= pDuplicate->dwDuplicateCabFileIndex || - pDuplicate->dwDuplicateCabFileIndex <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex) - { - hr = E_UNEXPECTED; - CabcExitOnFailure(hr, "Unexpected duplicate file indices, header cFiles: %d, file index: %d, duplicate index: %d", pHeader->cFiles, pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex, pDuplicate->dwDuplicateCabFileIndex); - } - - // Step through each cabinet items until we get to the original - // file's index. Notice that the name of the cabinet item is - // appended to the end of the MS_CABINET_INFO, that's why we can't - // index straight to the data we want. - for (DWORD i = 0; i < pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; ++i) - { - LPCSTR szItemName = reinterpret_cast(pbItem + sizeof(MS_CABINET_ITEM)); - pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1; - } - - pOriginalItem = reinterpret_cast(pbItem); - - // Now pick up where we left off after the original file's index - // was found and loop until we find the duplicate file's index. - for (DWORD i = pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; i < pDuplicate->dwDuplicateCabFileIndex; ++i) - { - LPCSTR szItemName = reinterpret_cast(pbItem + sizeof(MS_CABINET_ITEM)); - pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1; - } - - pDuplicateItem = reinterpret_cast(pbItem); - - if (0 != pDuplicateItem->cbFile) - { - hr = E_UNEXPECTED; - CabcExitOnFailure(hr, "Failed because duplicate file does not have a file size of zero: %d", pDuplicateItem->cbFile); - } - - pDuplicateItem->cbFile = pOriginalItem->cbFile; - pDuplicateItem->uoffFolderStart = pOriginalItem->uoffFolderStart; - pDuplicateItem->iFolder = pOriginalItem->iFolder; - // Note: we do *not* duplicate the date/time and attributes metadata from - // the original item to the duplicate. The following lines are commented - // so people are not tempted to put them back. - //pDuplicateItem->date = pOriginalItem->date; - //pDuplicateItem->time = pOriginalItem->time; - //pDuplicateItem->attribs = pOriginalItem->attribs; - -LExit: - return hr; -} - - -static HRESULT UtcFileTimeToLocalDosDateTime( - __in const FILETIME* pFileTime, - __out USHORT* pDate, - __out USHORT* pTime - ) -{ - HRESULT hr = S_OK; - FILETIME ftLocal = { }; - - if (!::FileTimeToLocalFileTime(pFileTime, &ftLocal)) - { - CabcExitWithLastError(hr, "Filed to convert file time to local file time."); - } - - if (!::FileTimeToDosDateTime(&ftLocal, pDate, pTime)) - { - CabcExitWithLastError(hr, "Filed to convert file time to DOS date time."); - } - -LExit: - return hr; -} - - -/******************************************************************** - FCI callback functions - -*********************************************************************/ -static __callback int DIAMONDAPI CabCFilePlaced( - __in PCCAB pccab, - __in_z PSTR szFile, - __in long cbFile, - __in BOOL fContinuation, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - UNREFERENCED_PARAMETER(pccab); - UNREFERENCED_PARAMETER(szFile); - UNREFERENCED_PARAMETER(cbFile); - UNREFERENCED_PARAMETER(fContinuation); - UNREFERENCED_PARAMETER(pv); - return 0; -} - - -static __callback void * DIAMONDAPI CabCAlloc( - __in ULONG cb - ) -{ - return MemAlloc(cb, FALSE); -} - - -static __callback void DIAMONDAPI CabCFree( - __out_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - MemFree(pv); -} - -static __callback INT_PTR DIAMONDAPI CabCOpen( - __in_z PSTR pszFile, - __in int oflag, - __in int pmode, - __out int *err, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - CABC_DATA *pcd = reinterpret_cast(pv); - HRESULT hr = S_OK; - INT_PTR pFile = -1; - DWORD dwAccess = 0; - DWORD dwDisposition = 0; - DWORD dwAttributes = 0; - - // - // Translate flags for CreateFile - // - if (oflag & _O_CREAT) - { - if (pmode == _S_IREAD) - dwAccess |= GENERIC_READ; - else if (pmode == _S_IWRITE) - dwAccess |= GENERIC_WRITE; - else if (pmode == (_S_IWRITE | _S_IREAD)) - dwAccess |= GENERIC_READ | GENERIC_WRITE; - - if (oflag & _O_SHORT_LIVED) - dwDisposition = FILE_ATTRIBUTE_TEMPORARY; - else if (oflag & _O_TEMPORARY) - dwAttributes |= FILE_FLAG_DELETE_ON_CLOSE; - else if (oflag & _O_EXCL) - dwDisposition = CREATE_NEW; - } - if (oflag & _O_TRUNC) - dwDisposition = CREATE_ALWAYS; - - if (!dwAccess) - dwAccess = GENERIC_READ; - if (!dwDisposition) - dwDisposition = OPEN_EXISTING; - if (!dwAttributes) - dwAttributes = FILE_ATTRIBUTE_NORMAL; - - // Check to see if we were passed the magic character that says 'Unicode string follows'. - if (pszFile && CABC_MAGIC_UNICODE_STRING_MARKER == *pszFile) - { - pFile = reinterpret_cast(::CreateFileW(reinterpret_cast(pszFile + 1), dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL)); - } - else - { -#pragma prefast(push) -#pragma prefast(disable:25068) // We intentionally don't use the unicode API here - pFile = reinterpret_cast(::CreateFileA(pszFile, dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL)); -#pragma prefast(pop) - } - - if (INVALID_HANDLE_VALUE == reinterpret_cast(pFile)) - { - CabcExitOnLastError(hr, "failed to open file: %s", pszFile); - } - -LExit: - if (FAILED(hr)) - pcd->hrLastError = *err = hr; - - return FAILED(hr) ? -1 : pFile; -} - - -static __callback UINT FAR DIAMONDAPI CabCRead( - __in INT_PTR hf, - __out_bcount(cb) void FAR *memory, - __in UINT cb, - __out int *err, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - CABC_DATA *pcd = reinterpret_cast(pv); - HRESULT hr = S_OK; - DWORD cbRead = 0; - - CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to read during cabinet extraction because no file handle was provided"); - if (!::ReadFile(reinterpret_cast(hf), memory, cb, &cbRead, NULL)) - { - *err = ::GetLastError(); - CabcExitOnLastError(hr, "failed to read during cabinet extraction"); - } - -LExit: - if (FAILED(hr)) - { - pcd->hrLastError = *err = hr; - } - - return FAILED(hr) ? -1 : cbRead; -} - - -static __callback UINT FAR DIAMONDAPI CabCWrite( - __in INT_PTR hf, - __in_bcount(cb) void FAR *memory, - __in UINT cb, - __out int *err, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - CABC_DATA *pcd = reinterpret_cast(pv); - HRESULT hr = S_OK; - DWORD cbWrite = 0; - - CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to write during cabinet extraction because no file handle was provided"); - if (!::WriteFile(reinterpret_cast(hf), memory, cb, &cbWrite, NULL)) - { - *err = ::GetLastError(); - CabcExitOnLastError(hr, "failed to write during cabinet extraction"); - } - -LExit: - if (FAILED(hr)) - pcd->hrLastError = *err = hr; - - return FAILED(hr) ? -1 : cbWrite; -} - - -static __callback long FAR DIAMONDAPI CabCSeek( - __in INT_PTR hf, - __in long dist, - __in int seektype, - __out int *err, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - CABC_DATA *pcd = reinterpret_cast(pv); - HRESULT hr = S_OK; - DWORD dwMoveMethod; - LONG lMove = 0; - - switch (seektype) - { - case 0: // SEEK_SET - dwMoveMethod = FILE_BEGIN; - break; - case 1: /// SEEK_CUR - dwMoveMethod = FILE_CURRENT; - break; - case 2: // SEEK_END - dwMoveMethod = FILE_END; - break; - default : - dwMoveMethod = 0; - hr = E_UNEXPECTED; - CabcExitOnFailure(hr, "unexpected seektype in FCISeek(): %d", seektype); - } - - // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. - // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) - // Todo: update these comments for FCI (are they accurate for FCI as well?) - lMove = ::SetFilePointer(reinterpret_cast(hf), dist, NULL, dwMoveMethod); - if (DWORD_MAX == lMove) - { - *err = ::GetLastError(); - CabcExitOnLastError(hr, "failed to move file pointer %d bytes", dist); - } - -LExit: - if (FAILED(hr)) - { - pcd->hrLastError = *err = hr; - } - - return FAILED(hr) ? -1 : lMove; -} - - -static __callback int FAR DIAMONDAPI CabCClose( - __in INT_PTR hf, - __out int *err, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - CABC_DATA *pcd = reinterpret_cast(pv); - HRESULT hr = S_OK; - - if (!::CloseHandle(reinterpret_cast(hf))) - { - *err = ::GetLastError(); - CabcExitOnLastError(hr, "failed to close file during cabinet extraction"); - } - -LExit: - if (FAILED(hr)) - { - pcd->hrLastError = *err = hr; - } - - return FAILED(hr) ? -1 : 0; -} - -static __callback int DIAMONDAPI CabCDelete( - __in_z PSTR szFile, - __out int *err, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - UNREFERENCED_PARAMETER(err); - UNREFERENCED_PARAMETER(pv); - -#pragma prefast(push) -#pragma prefast(disable:25068) // We intentionally don't use the unicode API here - ::DeleteFileA(szFile); -#pragma prefast(pop) - - return 0; -} - - -__success(return != FALSE) -static __callback BOOL DIAMONDAPI CabCGetTempFile( - __out_bcount_z(cbFile) char *szFile, - __in int cbFile, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - CABC_DATA *pcd = reinterpret_cast(pv); - static volatile DWORD dwIndex = 0; - - HRESULT hr = S_OK; - char szTempPath[MAX_PATH] = { }; - DWORD cchTempPath = MAX_PATH; - DWORD dwProcessId = ::GetCurrentProcessId(); - HANDLE hTempFile = INVALID_HANDLE_VALUE; - - if (MAX_PATH < ::GetTempPathA(cchTempPath, szTempPath)) - { - CabcExitWithLastError(hr, "Failed to get temp path during cabinet creation."); - } - - for (DWORD i = 0; i < DWORD_MAX; ++i) - { - LONG dwTempIndex = ::InterlockedIncrement(reinterpret_cast(&dwIndex)); - - hr = ::StringCbPrintfA(szFile, cbFile, "%s\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId); - CabcExitOnFailure(hr, "failed to format log file path."); - - hTempFile = ::CreateFileA(szFile, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); - if (INVALID_HANDLE_VALUE != hTempFile) - { - // we found one that doesn't exist - hr = S_OK; - break; - } - else - { - hr = E_FAIL; // this file was taken so be pessimistic and assume we're not going to find one. - } - } - CabcExitOnFailure(hr, "failed to find temporary file."); - -LExit: - ReleaseFileHandle(hTempFile); - - if (FAILED(hr)) - { - pcd->hrLastError = hr; - } - - return FAILED(hr)? FALSE : TRUE; -} - - -__success(return != FALSE) -static __callback BOOL DIAMONDAPI CabCGetNextCabinet( - __in PCCAB pccab, - __in ULONG ul, - __out_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - UNREFERENCED_PARAMETER(ul); - - // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........ - CABC_DATA *pcd = reinterpret_cast(pv); - HRESULT hr = S_OK; - LPWSTR pwzFileToken = NULL; - WCHAR wzNewCabName[MAX_PATH] = L""; - - if (pccab->iCab == 1) - { - pcd->wzFirstCabinetName[0] = '\0'; - LPCWSTR pwzCabinetName = FileFromPath(pcd->wzCabinetPath); - size_t len = wcsnlen(pwzCabinetName, sizeof(pwzCabinetName)); - if (len > 4) - { - len -= 4; // remove Extention ".cab" of 8.3 Format - } - hr = ::StringCchCatNW(pcd->wzFirstCabinetName, countof(pcd->wzFirstCabinetName), pwzCabinetName, len); - CabcExitOnFailure(hr, "Failed to remove extension to create next Cabinet File Name"); - } - - const int nAlphabets = 26; // Number of Alphabets from a to z - if (pccab->iCab <= nAlphabets) - { - // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........ - hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + pccab->iCab)); - CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); - hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + pccab->iCab)); - CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); - } - else if (pccab->iCab <= nAlphabets*nAlphabets) - { - // Construct next cab names like cab1aa.cab, cab1ab.cab, cab1ac.cab, ......, cabaz.cab, cabaa.cab, cabab.cab, cabac.cab, ...... - int char2 = (pccab->iCab) % nAlphabets; - int char1 = (pccab->iCab - char2)/nAlphabets; - if (char2 == 0) - { - // e.g. when iCab = 52, we want az - char2 = nAlphabets; // Second char must be 'z' in this case - char1--; // First Char must be decremented by 1 - } - hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + char1), char(((int)('a') - 1) + char2)); - CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); - hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + char1), WCHAR(((int)('a') - 1) + char2)); - CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); - } - else - { - hr = DISP_E_BADINDEX; // Value 0x8002000B stands for Invalid index. - CabcExitOnFailure(hr, "Cannot Split Cabinet more than 26*26 = 676 times. Failed to create next Cabinet File Name"); - } - - // Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method - if(pcd->fileSplitCabNamesCallback != 0) - { - // In following if/else block, getting the Token for the First File in the Cabinets that are getting Split - // This code will need updation if we need to send all file tokens for the splitting Cabinets - if (pcd->prgFiles[0].pwzToken) - { - pwzFileToken = pcd->prgFiles[0].pwzToken; - } - else - { - LPCWSTR wzSourcePath = pcd->prgFiles[0].pwzSourcePath; - pwzFileToken = FileFromPath(wzSourcePath); - } - - // The call back to Binder to Add File Transfer for new Cab and add new Cab to Media table - pcd->fileSplitCabNamesCallback(pcd->wzFirstCabinetName, wzNewCabName, pwzFileToken); - } - -LExit: - if (FAILED(hr)) - { - // Returning False in case of error here as stated by Documentation, However It fails to Abort Cab Creation!!! - // So Using separate check for pcd->hrLastError after ::FCIAddFile for Cabinet Splitting - pcd->hrLastError = hr; - return FALSE; - } - else - { - return TRUE; - } -} - - -static __callback INT_PTR DIAMONDAPI CabCGetOpenInfo( - __in_z PSTR pszName, - __out USHORT *pdate, - __out USHORT *ptime, - __out USHORT *pattribs, - __out int *err, - __out_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - HRESULT hr = S_OK; - CABC_INTERNAL_ADDFILEINFO* pFileInfo = reinterpret_cast(pszName); - LPCWSTR wzFile = NULL; - DWORD cbFile = 0; - LPSTR pszFilePlusMagic = NULL; - DWORD cbFilePlusMagic = 0; - WIN32_FILE_ATTRIBUTE_DATA fad = { }; - INT_PTR iResult = -1; - - // If there is an empty file provided, use that as the source path to cab (since we - // must be dealing with a duplicate file). Otherwise, use the source path you'd expect. - wzFile = pFileInfo->wzEmptyPath ? pFileInfo->wzEmptyPath : pFileInfo->wzSourcePath; - cbFile = (lstrlenW(wzFile) + 1) * sizeof(WCHAR); - - // Convert the source file path into an Ansi string that our APIs will recognize as - // a Unicode string (due to the magic character). - cbFilePlusMagic = cbFile + 1; // add one for the magic. - pszFilePlusMagic = reinterpret_cast(MemAlloc(cbFilePlusMagic, TRUE)); - - *pszFilePlusMagic = CABC_MAGIC_UNICODE_STRING_MARKER; - memcpy_s(pszFilePlusMagic + 1, cbFilePlusMagic - 1, wzFile, cbFile); - - if (!::GetFileAttributesExW(pFileInfo->wzSourcePath, GetFileExInfoStandard, &fad)) - { - CabcExitWithLastError(hr, "Failed to get file attributes on '%ls'.", pFileInfo->wzSourcePath); - } - - // Set the attributes but only allow the few attributes that CAB supports. - *pattribs = static_cast(fad.dwFileAttributes) & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE); - - hr = UtcFileTimeToLocalDosDateTime(&fad.ftLastWriteTime, pdate, ptime); - if (FAILED(hr)) - { - // NOTE: Changed this from ftLastWriteTime to ftCreationTime because of issues around how different OSs were - // handling the access of the FILETIME structure and how it would fail conversion to DOS time if it wasn't - // found. This would create further problems if the file was written to the CAB without this value. Windows - // Installer would then fail to extract the file. - hr = UtcFileTimeToLocalDosDateTime(&fad.ftCreationTime, pdate, ptime); - CabcExitOnFailure(hr, "Filed to read a valid file time stucture on file '%s'.", pszName); - } - - iResult = CabCOpen(pszFilePlusMagic, _O_BINARY|_O_RDONLY, 0, err, pv); - -LExit: - ReleaseMem(pszFilePlusMagic); - if (FAILED(hr)) - { - *err = (int)hr; - } - - return FAILED(hr) ? -1 : iResult; -} - - -static __callback long DIAMONDAPI CabCStatus( - __in UINT ui, - __in ULONG cb1, - __in ULONG cb2, - __inout_bcount(CABC_HANDLE_BYTES) void *pv - ) -{ - UNREFERENCED_PARAMETER(ui); - UNREFERENCED_PARAMETER(cb1); - UNREFERENCED_PARAMETER(cb2); - UNREFERENCED_PARAMETER(pv); - return 0; -} diff --git a/src/dutil/cabutil.cpp b/src/dutil/cabutil.cpp deleted file mode 100644 index 5d77e483..00000000 --- a/src/dutil/cabutil.cpp +++ /dev/null @@ -1,617 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define CabExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) -#define CabExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) -#define CabExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) -#define CabExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) -#define CabExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) -#define CabExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) -#define CabExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__) -#define CabExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__) -#define CabExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__) -#define CabExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__) -#define CabExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABUTIL, e, x, s, __VA_ARGS__) -#define CabExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABUTIL, g, x, s, __VA_ARGS__) - - -// external prototypes -typedef BOOL (FAR DIAMONDAPI *PFNFDIDESTROY)(VOID*); -typedef HFDI (FAR DIAMONDAPI *PFNFDICREATE)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF); -typedef BOOL (FAR DIAMONDAPI *PFNFDIISCABINET)(HFDI, INT_PTR, PFDICABINETINFO); -typedef BOOL (FAR DIAMONDAPI *PFNFDICOPY)(HFDI, char *, char *, int, PFNFDINOTIFY, PFNFDIDECRYPT, void *); - - -// -// static globals -// -static HMODULE vhCabinetDll = NULL; - -static HFDI vhfdi = NULL; -static PFNFDICREATE vpfnFDICreate = NULL; -static PFNFDICOPY vpfnFDICopy = NULL; -static PFNFDIISCABINET vpfnFDIIsCabinet = NULL; -static PFNFDIDESTROY vpfnFDIDestroy = NULL; -static ERF verf; - -static DWORD64 vdw64EmbeddedOffset = 0; - -// -// structs -// -struct CAB_CALLBACK_STRUCT -{ - BOOL fStopExtracting; // flag set when no more files are needed - LPCWSTR pwzExtract; // file to extract ("*" means extract all) - LPCWSTR pwzExtractDir; // directory to extract files to - - // possible user data - CAB_CALLBACK_PROGRESS pfnProgress; - LPVOID pvContext; -}; - -// -// prototypes -// -static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize); -static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData); -static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode); -static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb); -static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb); -static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf); -static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype); -static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify); -static HRESULT DAPI CabOperation(__in LPCWSTR wzCabinet, __in LPCWSTR wzExtractFile, __in_opt LPCWSTR wzExtractDir, __in_opt CAB_CALLBACK_PROGRESS pfnProgress, __in_opt LPVOID pvContext, __in_opt STDCALL_PFNFDINOTIFY pfnNotify, __in DWORD64 dw64EmbeddedOffset); - -static STDCALL_PFNFDINOTIFY v_pfnNetFx11Notify = NULL; - - -inline HRESULT LoadCabinetDll() -{ - HRESULT hr = S_OK; - if (!vhCabinetDll) - { - hr = LoadSystemLibrary(L"cabinet.dll", &vhCabinetDll); - CabExitOnFailure(hr, "failed to load cabinet.dll"); - - // retrieve all address functions - vpfnFDICreate = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDICreate")); - CabExitOnNullWithLastError(vpfnFDICreate, hr, "failed to import FDICreate from CABINET.DLL"); - vpfnFDICopy = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDICopy")); - CabExitOnNullWithLastError(vpfnFDICopy, hr, "failed to import FDICopy from CABINET.DLL"); - vpfnFDIIsCabinet = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDIIsCabinet")); - CabExitOnNullWithLastError(vpfnFDIIsCabinet, hr, "failed to import FDIIsCabinetfrom CABINET.DLL"); - vpfnFDIDestroy = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDIDestroy")); - CabExitOnNullWithLastError(vpfnFDIDestroy, hr, "failed to import FDIDestroyfrom CABINET.DLL"); - - vhfdi = vpfnFDICreate(CabExtractAlloc, CabExtractFree, CabExtractOpen, CabExtractRead, CabExtractWrite, CabExtractClose, CabExtractSeek, cpuUNKNOWN, &verf); - CabExitOnNull(vhfdi, hr, E_FAIL, "failed to initialize cabinet.dll"); - } - -LExit: - if (FAILED(hr) && vhCabinetDll) - { - ::FreeLibrary(vhCabinetDll); - vhCabinetDll = NULL; - } - - return hr; -} - - -static HANDLE OpenFileWithRetry( - __in LPCWSTR wzPath, - __in DWORD dwDesiredAccess, - __in DWORD dwCreationDisposition -) -{ - HANDLE hFile = INVALID_HANDLE_VALUE; - - for (DWORD i = 0; i < 30; ++i) - { - hFile = ::CreateFileW(wzPath, dwDesiredAccess, FILE_SHARE_READ, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE != hFile) - { - break; - } - - ::Sleep(100); - } - - return hFile; -} - - -/******************************************************************** - CabInitialize - initializes internal static variables - -********************************************************************/ -extern "C" HRESULT DAPI CabInitialize( - __in BOOL fDelayLoad - ) -{ - HRESULT hr = S_OK; - - if (!fDelayLoad) - { - hr = LoadCabinetDll(); - CabExitOnFailure(hr, "failed to load CABINET.DLL"); - } - -LExit: - return hr; -} - - -/******************************************************************** - CabUninitialize - initializes internal static variables - -********************************************************************/ -extern "C" void DAPI CabUninitialize( - ) -{ - if (vhfdi) - { - if (vpfnFDIDestroy) - { - vpfnFDIDestroy(vhfdi); - } - vhfdi = NULL; - } - - vpfnFDICreate = NULL; - vpfnFDICopy =NULL; - vpfnFDIIsCabinet = NULL; - vpfnFDIDestroy = NULL; - - if (vhCabinetDll) - { - ::FreeLibrary(vhCabinetDll); - vhCabinetDll = NULL; - } -} - -/******************************************************************** - CabEnumerate - list files inside cabinet - - NOTE: wzCabinet must be full path to cabinet file - pfnNotify is callback function to get notified for each file - in the cabinet -********************************************************************/ -extern "C" HRESULT DAPI CabEnumerate( - __in_z LPCWSTR wzCabinet, - __in_z LPCWSTR wzEnumerateFile, - __in STDCALL_PFNFDINOTIFY pfnNotify, - __in DWORD64 dw64EmbeddedOffset - ) -{ - return CabOperation(wzCabinet, wzEnumerateFile, NULL, NULL, NULL, pfnNotify, dw64EmbeddedOffset); -} - -/******************************************************************** - CabExtract - extracts one or all files from a cabinet - - NOTE: wzCabinet must be full path to cabinet file - wzExtractFile can be a single file id or "*" to extract all files - wzExttractDir must be normalized (end in a "\") - if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa -********************************************************************/ -extern "C" HRESULT DAPI CabExtract( - __in_z LPCWSTR wzCabinet, - __in_z LPCWSTR wzExtractFile, - __in_z LPCWSTR wzExtractDir, - __in_opt CAB_CALLBACK_PROGRESS pfnProgress, - __in_opt LPVOID pvContext, - __in DWORD64 dw64EmbeddedOffset - ) -{ - return CabOperation(wzCabinet, wzExtractFile, wzExtractDir, pfnProgress, pvContext, NULL, dw64EmbeddedOffset); -} - -// -// private -// -/******************************************************************** - FDINotify -- wrapper that converts call convention from __cdecl to __stdcall. - - NOTE: Since netfx 1.1 supports only function pointers (delegates) - with __stdcall calling convention and cabinet api uses - __cdecl calling convention, we need this wrapper function. - netfx 2.0 will work with [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute on the delegate. - TODO: remove this when upgrading to netfx 2.0. -********************************************************************/ -static __callback INT_PTR DIAMONDAPI FDINotify( - __in FDINOTIFICATIONTYPE iNotification, - __inout FDINOTIFICATION *pFDINotify - ) -{ - if (NULL != v_pfnNetFx11Notify) - { - return v_pfnNetFx11Notify(iNotification, pFDINotify); - } - else - { - return (INT_PTR)0; - } -} - - -/******************************************************************** - CabOperation - helper function that enumerates or extracts files - from cabinet - - NOTE: wzCabinet must be full path to cabinet file - wzExtractFile can be a single file id or "*" to extract all files - wzExttractDir must be normalized (end in a "\") - if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa - pfnNotify is callback function to get notified for each file - in the cabinet. If it's NULL, files will be extracted. -********************************************************************/ -static HRESULT DAPI CabOperation( - __in LPCWSTR wzCabinet, - __in LPCWSTR wzExtractFile, - __in_opt LPCWSTR wzExtractDir, - __in_opt CAB_CALLBACK_PROGRESS pfnProgress, - __in_opt LPVOID pvContext, - __in_opt STDCALL_PFNFDINOTIFY pfnNotify, - __in DWORD64 dw64EmbeddedOffset - ) -{ - HRESULT hr = S_OK; - BOOL fResult; - - LPWSTR sczCabinet = NULL; - LPWSTR pwz = NULL; - CHAR szCabDirectory[MAX_PATH * 4]; // Make sure these are big enough for UTF-8 strings - CHAR szCabFile[MAX_PATH * 4]; - - CAB_CALLBACK_STRUCT ccs; - PFNFDINOTIFY pfnFdiNotify; - - // - // ensure the cabinet.dll is loaded - // - if (!vhfdi) - { - hr = LoadCabinetDll(); - CabExitOnFailure(hr, "failed to load CABINET.DLL"); - } - - hr = StrAllocString(&sczCabinet, wzCabinet, 0); - CabExitOnFailure(hr, "Failed to make copy of cabinet name:%ls", wzCabinet); - - // - // split the cabinet full path into directory and filename and convert to multi-byte (ick!) - // - pwz = FileFromPath(sczCabinet); - CabExitOnNull(pwz, hr, E_INVALIDARG, "failed to process cabinet path: %ls", wzCabinet); - - if (!::WideCharToMultiByte(CP_UTF8, 0, pwz, -1, szCabFile, countof(szCabFile), NULL, NULL)) - { - CabExitWithLastError(hr, "failed to convert cabinet filename to ASCII: %ls", pwz); - } - - *pwz = '\0'; - - // If a full path was not provided, use the relative current directory. - if (wzCabinet == pwz) - { - hr = ::StringCchCopyA(szCabDirectory, countof(szCabDirectory), ".\\"); - CabExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory."); - } - else - { - if (!::WideCharToMultiByte(CP_UTF8, 0, sczCabinet, -1, szCabDirectory, countof(szCabDirectory), NULL, NULL)) - { - CabExitWithLastError(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet); - } - } - - // - // iterate through files in cabinet extracting them to the callback function - // - ccs.fStopExtracting = FALSE; - ccs.pwzExtract = wzExtractFile; - ccs.pwzExtractDir = wzExtractDir; - ccs.pfnProgress = pfnProgress; - ccs.pvContext = pvContext; - - vdw64EmbeddedOffset = dw64EmbeddedOffset; - - // if pfnNotify is given, use it, otherwise use default callback - if (NULL == pfnNotify) - { - pfnFdiNotify = CabExtractCallback; - } - else - { - v_pfnNetFx11Notify = pfnNotify; - pfnFdiNotify = FDINotify; - } - fResult = vpfnFDICopy(vhfdi, szCabFile, szCabDirectory, 0, pfnFdiNotify, NULL, static_cast(&ccs)); - if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure - { - CabExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet); - } - -LExit: - ReleaseStr(sczCabinet); - v_pfnNetFx11Notify = NULL; - - return hr; -} - -/**************************************************************************** - default extract routines - -****************************************************************************/ -static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize) -{ - return MemAlloc(dwSize, FALSE); -} - - -static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData) -{ - MemFree(pvData); -} - - -static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - INT_PTR pFile = -1; - LPWSTR sczCabFile = NULL; - - // if FDI asks for some unusual mode (in low memory situation it could ask for a scratch file) fail - if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) - { - hr = E_OUTOFMEMORY; - CabExitOnFailure(hr, "FDI asked for a scratch file to be created, which is unsupported"); - } - - hr = StrAllocStringAnsi(&sczCabFile, pszFile, 0, CP_UTF8); - CabExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string"); - - hFile = OpenFileWithRetry(sczCabFile, GENERIC_READ, OPEN_EXISTING); - if (INVALID_HANDLE_VALUE == hFile) - { - CabExitWithLastError(hr, "failed to open file: %ls", sczCabFile); - } - - pFile = reinterpret_cast(hFile); - - if (vdw64EmbeddedOffset) - { - hr = CabExtractSeek(pFile, 0, 0); - CabExitOnFailure(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset); - } - - hFile = INVALID_HANDLE_VALUE; - -LExit: - ReleaseFileHandle(hFile); - ReleaseStr(sczCabFile); - - return FAILED(hr) ? -1 : pFile; -} - - -static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb) -{ - HRESULT hr = S_OK; - DWORD cbRead = 0; - - CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to read file during cabinet extraction - no file given to read"); - if (!::ReadFile(reinterpret_cast(hf), pv, cb, &cbRead, NULL)) - { - CabExitWithLastError(hr, "failed to read during cabinet extraction"); - } - -LExit: - return FAILED(hr) ? -1 : cbRead; -} - - -static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb) -{ - HRESULT hr = S_OK; - DWORD cbWrite = 0; - - CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to write file during cabinet extraction - no file given to write"); - if (!::WriteFile(reinterpret_cast(hf), pv, cb, &cbWrite, NULL)) - { - CabExitWithLastError(hr, "failed to write during cabinet extraction"); - } - -LExit: - return FAILED(hr) ? -1 : cbWrite; -} - - -static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype) -{ - HRESULT hr = S_OK; - DWORD dwMoveMethod; - LONG lMove = 0; - - switch (seektype) - { - case 0: // SEEK_SET - dwMoveMethod = FILE_BEGIN; - dist += static_cast(vdw64EmbeddedOffset); - break; - case 1: /// SEEK_CUR - dwMoveMethod = FILE_CURRENT; - break; - case 2: // SEEK_END - dwMoveMethod = FILE_END; - break; - default : - dwMoveMethod = 0; - hr = E_UNEXPECTED; - CabExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); - } - - // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. - // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) - lMove = ::SetFilePointer(reinterpret_cast(hf), dist, NULL, dwMoveMethod); - if (0xFFFFFFFF == lMove) - { - CabExitWithLastError(hr, "failed to move file pointer %d bytes", dist); - } - -LExit: - return FAILED(hr) ? -1 : lMove - static_cast(vdw64EmbeddedOffset); -} - - -static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf) -{ - HRESULT hr = S_OK; - - if (!::CloseHandle(reinterpret_cast(hf))) - { - CabExitWithLastError(hr, "failed to close file during cabinet extraction"); - } - -LExit: - return FAILED(hr) ? -1 : 0; -} - - -static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify) -{ - Assert(pFDINotify->pv); - - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - INT_PTR ipResult = 0; // result to return on success - - CAB_CALLBACK_STRUCT* pccs = static_cast(pFDINotify->pv); - LPCSTR sz; - WCHAR wz[MAX_PATH]; - FILETIME ft; - - switch (iNotification) - { - case fdintCOPY_FILE: // begin extracting a resource from cabinet - CabExitOnNull(pFDINotify->psz1, hr, E_INVALIDARG, "No cabinet file ID given to convert"); - CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); - - if (pccs->fStopExtracting) - { - ExitFunction1(hr = S_FALSE); // no more extracting - } - - // convert params to useful variables - sz = static_cast(pFDINotify->psz1); - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) - { - CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); - } - - if (pccs->pfnProgress) - { - hr = pccs->pfnProgress(TRUE, wz, pccs->pvContext); - if (S_OK != hr) - { - ExitFunction(); - } - } - - if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, wz)) - { - // get the created date for the resource in the cabinet - FILETIME ftLocal; - if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) - { - CabExitWithLastError(hr, "failed to get time for resource: %ls", wz); - } - ::LocalFileTimeToFileTime(&ftLocal, &ft); - - WCHAR wzPath[MAX_PATH]; - hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir); - CabExitOnFailure(hr, "failed to copy in extract directory: %ls for file: %ls", pccs->pwzExtractDir, wz); - hr = ::StringCchCatW(wzPath, countof(wzPath), wz); - CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); - - hFile = OpenFileWithRetry(wzPath, GENERIC_WRITE, CREATE_ALWAYS); - if (INVALID_HANDLE_VALUE == hFile) - { - CabExitWithLastError(hr, "failed to create file: %ls", wzPath); - } - - ::SetFileTime(hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails) - - if (::SetFilePointer(hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) - { - if (::SetEndOfFile(hFile)) - { - ::SetFilePointer(hFile, 0, NULL, FILE_BEGIN); // reset the file pointer - } - } - - ipResult = reinterpret_cast(hFile); - hFile = INVALID_HANDLE_VALUE; - } - else // resource wasn't requested, skip it - { - hr = S_OK; - ipResult = 0; - } - - break; - case fdintCLOSE_FILE_INFO: // resource extraction complete - Assert(pFDINotify->hf && pFDINotify->psz1); - CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); - - // convert params to useful variables - sz = static_cast(pFDINotify->psz1); - CabExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided"); - - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) - { - CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); - } - - if (NULL != pFDINotify->hf) // just close the file - { - ::CloseHandle(reinterpret_cast(pFDINotify->hf)); - } - - if (pccs->pfnProgress) - { - hr = pccs->pfnProgress(FALSE, wz, pccs->pvContext); - } - - if (S_OK == hr && L'*' == *pccs->pwzExtract) // if everything is okay and we're extracting all files, keep going - { - ipResult = TRUE; - } - else // something went wrong or we only needed to extract one file - { - hr = S_OK; - ipResult = FALSE; - pccs->fStopExtracting = TRUE; - } - - break; - case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through - case fdintNEXT_CABINET: __fallthrough; - case fdintENUMERATE: __fallthrough; - case fdintCABINET_INFO: - break; - default: - AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command"); - }; - -LExit: - ReleaseFileHandle(hFile); - - return (S_OK == hr) ? ipResult : -1; -} diff --git a/src/dutil/certutil.cpp b/src/dutil/certutil.cpp deleted file mode 100644 index 69897b9e..00000000 --- a/src/dutil/certutil.cpp +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define CertExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) -#define CertExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) -#define CertExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) -#define CertExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) -#define CertExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) -#define CertExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) -#define CertExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CERTUTIL, p, x, e, s, __VA_ARGS__) -#define CertExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, p, x, s, __VA_ARGS__) -#define CertExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CERTUTIL, p, x, e, s, __VA_ARGS__) -#define CertExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, p, x, s, __VA_ARGS__) -#define CertExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CERTUTIL, e, x, s, __VA_ARGS__) -#define CertExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CERTUTIL, g, x, s, __VA_ARGS__) - -/******************************************************************** -CertReadProperty - reads a property from the certificate. - -NOTE: call MemFree() on the returned pvValue. -********************************************************************/ -extern "C" HRESULT DAPI CertReadProperty( - __in PCCERT_CONTEXT pCertContext, - __in DWORD dwProperty, - __deref_out_bound LPVOID* ppvValue, - __out_opt DWORD* pcbValue - ) -{ - HRESULT hr = S_OK; - LPVOID pv = NULL; - DWORD cb = 0; - - if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, NULL, &cb)) - { - CertExitWithLastError(hr, "Failed to get size of certificate property."); - } - - pv = MemAlloc(cb, TRUE); - CertExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for certificate property."); - - if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, pv, &cb)) - { - CertExitWithLastError(hr, "Failed to get certificate property."); - } - - *ppvValue = pv; - pv = NULL; - - if (pcbValue) - { - *pcbValue = cb; - } - -LExit: - ReleaseMem(pv); - return hr; -} - - -extern "C" HRESULT DAPI CertGetAuthenticodeSigningTimestamp( - __in CMSG_SIGNER_INFO* pSignerInfo, - __out FILETIME* pftSigningTimestamp - ) -{ - HRESULT hr = S_OK; - CRYPT_INTEGER_BLOB* pBlob = NULL; - PCMSG_SIGNER_INFO pCounterSignerInfo = NULL; - DWORD cbSigningTimestamp = sizeof(FILETIME); - - // Find the countersigner blob. The countersigner in Authenticode contains the time - // that signing took place. It's a "countersigner" because the signing time was sent - // off to the certificate authority in the sky to return the verified time signed. - for (DWORD i = 0; i < pSignerInfo->UnauthAttrs.cAttr; ++i) - { - if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_counterSign, -1, pSignerInfo->UnauthAttrs.rgAttr[i].pszObjId, -1)) - { - pBlob = pSignerInfo->UnauthAttrs.rgAttr[i].rgValue; - break; - } - } - - if (!pBlob) - { - hr = TRUST_E_FAIL; - CertExitOnFailure(hr, "Failed to find countersigner in signer information."); - } - - hr = CrypDecodeObject(PKCS7_SIGNER_INFO, pBlob->pbData, pBlob->cbData, 0, reinterpret_cast(&pCounterSignerInfo), NULL); - CertExitOnFailure(hr, "Failed to decode countersigner information."); - - pBlob = NULL; // reset the blob before searching for the signing time. - - // Find the signing time blob in the countersigner. - for (DWORD i = 0; i < pCounterSignerInfo->AuthAttrs.cAttr; ++i) - { - if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_signingTime, -1, pCounterSignerInfo->AuthAttrs.rgAttr[i].pszObjId, -1)) - { - pBlob = pCounterSignerInfo->AuthAttrs.rgAttr[i].rgValue; - break; - } - } - - if (!pBlob) - { - hr = TRUST_E_FAIL; - CertExitOnFailure(hr, "Failed to find signing time in countersigner information."); - } - - if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_RSA_signingTime, pBlob->pbData, pBlob->cbData, 0, pftSigningTimestamp, &cbSigningTimestamp)) - { - CertExitWithLastError(hr, "Failed to decode countersigner signing timestamp."); - } - -LExit: - ReleaseMem(pCounterSignerInfo); - - return hr; -} - - -extern "C" HRESULT DAPI GetCryptProvFromCert( - __in_opt HWND hwnd, - __in PCCERT_CONTEXT pCert, - __out HCRYPTPROV *phCryptProv, - __out DWORD *pdwKeySpec, - __in BOOL *pfDidCryptAcquire, - __deref_opt_out LPWSTR *ppwszTmpContainer, - __deref_opt_out LPWSTR *ppwszProviderName, - __out DWORD *pdwProviderType - ) -{ - HRESULT hr = S_OK; - HMODULE hMsSign32 = NULL; - - typedef BOOL (WINAPI *GETCRYPTPROVFROMCERTPTR)(HWND, PCCERT_CONTEXT, HCRYPTPROV*, DWORD*,BOOL*,LPWSTR*,LPWSTR*,DWORD*); - GETCRYPTPROVFROMCERTPTR pGetCryptProvFromCert = NULL; - - hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32); - CertExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); - - pGetCryptProvFromCert = (GETCRYPTPROVFROMCERTPTR)::GetProcAddress(hMsSign32, "GetCryptProvFromCert"); - CertExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); - - if (!pGetCryptProvFromCert(hwnd, - pCert, - phCryptProv, - pdwKeySpec, - pfDidCryptAcquire, - ppwszTmpContainer, - ppwszProviderName, - pdwProviderType)) - { - CertExitWithLastError(hr, "Failed to get CSP from cert."); - } -LExit: - return hr; -} - -extern "C" HRESULT DAPI FreeCryptProvFromCert( - __in BOOL fAcquired, - __in HCRYPTPROV hProv, - __in_opt LPWSTR pwszCapiProvider, - __in DWORD dwProviderType, - __in_opt LPWSTR pwszTmpContainer - ) -{ - HRESULT hr = S_OK; - HMODULE hMsSign32 = NULL; - - typedef void (WINAPI *FREECRYPTPROVFROMCERT)(BOOL, HCRYPTPROV, LPWSTR, DWORD, LPWSTR); - FREECRYPTPROVFROMCERT pFreeCryptProvFromCert = NULL; - - hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32); - CertExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); - - pFreeCryptProvFromCert = (FREECRYPTPROVFROMCERT)::GetProcAddress(hMsSign32, "FreeCryptProvFromCert"); - CertExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); - - pFreeCryptProvFromCert(fAcquired, hProv, pwszCapiProvider, dwProviderType, pwszTmpContainer); -LExit: - return hr; -} - -extern "C" HRESULT DAPI GetProvSecurityDesc( - __in HCRYPTPROV hProv, - __deref_out SECURITY_DESCRIPTOR** ppSecurity) -{ - HRESULT hr = S_OK; - ULONG ulSize = 0; - SECURITY_DESCRIPTOR* pSecurity = NULL; - - // Get the size of the security descriptor. - if (!::CryptGetProvParam( - hProv, - PP_KEYSET_SEC_DESCR, - NULL, - &ulSize, - DACL_SECURITY_INFORMATION)) - { - CertExitWithLastError(hr, "Error getting security descriptor size for CSP."); - } - - // Allocate the memory for the security descriptor. - pSecurity = static_cast(MemAlloc(ulSize, TRUE)); - CertExitOnNullWithLastError(pSecurity, hr, "Error allocating memory for CSP DACL"); - - // Get the security descriptor. - if (!::CryptGetProvParam( - hProv, - PP_KEYSET_SEC_DESCR, - (BYTE*)pSecurity, - &ulSize, - DACL_SECURITY_INFORMATION)) - { - MemFree(pSecurity); - CertExitWithLastError(hr, "Error getting security descriptor for CSP."); - } - *ppSecurity = pSecurity; - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI SetProvSecurityDesc( - __in HCRYPTPROV hProv, - __in SECURITY_DESCRIPTOR* pSecurity) -{ - HRESULT hr = S_OK; - - // Set the new security descriptor. - if (!::CryptSetProvParam( - hProv, - PP_KEYSET_SEC_DESCR, - (BYTE*)pSecurity, - DACL_SECURITY_INFORMATION)) - { - CertExitWithLastError(hr, "Error setting security descriptor for CSP."); - } -LExit: - return hr; -} - -extern "C" BOOL DAPI CertHasPrivateKey( - __in PCCERT_CONTEXT pCertContext, - __out_opt DWORD* pdwKeySpec) -{ - HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hPrivateKey = NULL; - DWORD dwKeySpec = 0; - // set CRYPT_ACQUIRE_CACHE_FLAG so that we don't have to release the private key handle - BOOL fResult = ::CryptAcquireCertificatePrivateKey( - pCertContext, - CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_CACHE_FLAG, - 0, //pvReserved - &hPrivateKey, - &dwKeySpec, - NULL - ); - if (pdwKeySpec) - { - *pdwKeySpec = dwKeySpec; - } - return fResult; -} - - -extern "C" HRESULT DAPI CertInstallSingleCertificate( - __in HCERTSTORE hStore, - __in PCCERT_CONTEXT pCertContext, - __in LPCWSTR wzName - ) -{ - HRESULT hr = S_OK; - CERT_BLOB blob = { }; - - DWORD dwKeySpec = 0; - - HCRYPTPROV hCsp = NULL; - LPWSTR pwszTmpContainer = NULL; - LPWSTR pwszProviderName = NULL; - DWORD dwProviderType = 0; - BOOL fAcquired = TRUE; - - SECURITY_DESCRIPTOR* pSecurity = NULL; - SECURITY_DESCRIPTOR* pSecurityNew = NULL; - - // Update the friendly name of the certificate to be configured. - blob.pbData = (BYTE*)wzName; - blob.cbData = (lstrlenW(wzName) + 1) * sizeof(WCHAR); // including terminating null - - if (!::CertSetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, 0, &blob)) - { - CertExitWithLastError(hr, "Failed to set the friendly name of the certificate: %ls", wzName); - } - - if (!::CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) - { - CertExitWithLastError(hr, "Failed to add certificate to the store."); - } - - // if the certificate has a private key, grant Administrators access - if (CertHasPrivateKey(pCertContext, &dwKeySpec)) - { - if (AT_KEYEXCHANGE == dwKeySpec || AT_SIGNATURE == dwKeySpec) - { - // We added a CSP key - hr = GetCryptProvFromCert(NULL, pCertContext, &hCsp, &dwKeySpec, &fAcquired, &pwszTmpContainer, &pwszProviderName, &dwProviderType); - CertExitOnFailure(hr, "Failed to get handle to CSP"); - - hr = GetProvSecurityDesc(hCsp, &pSecurity); - CertExitOnFailure(hr, "Failed to get security descriptor of CSP"); - - hr = AclAddAdminToSecurityDescriptor(pSecurity, &pSecurityNew); - CertExitOnFailure(hr, "Failed to create new security descriptor"); - - hr = SetProvSecurityDesc(hCsp, pSecurityNew); - CertExitOnFailure(hr, "Failed to set Admin ACL on CSP"); - } - - if (CERT_NCRYPT_KEY_SPEC == dwKeySpec) - { - // We added a CNG key - // TODO change ACL on CNG key - } - } -LExit: - if (hCsp) - { - FreeCryptProvFromCert(fAcquired, hCsp, NULL, dwProviderType, NULL); - } - - ReleaseMem(pSecurity); - - if (pSecurityNew) - { - AclFreeSecurityDescriptor(pSecurityNew); - } - return hr; -} diff --git a/src/dutil/conutil.cpp b/src/dutil/conutil.cpp deleted file mode 100644 index 33e1b59a..00000000 --- a/src/dutil/conutil.cpp +++ /dev/null @@ -1,673 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ConExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) -#define ConExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) -#define ConExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) -#define ConExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) -#define ConExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) -#define ConExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) -#define ConExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__) -#define ConExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__) -#define ConExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__) -#define ConExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__) -#define ConExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CONUTIL, e, x, s, __VA_ARGS__) -#define ConExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CONUTIL, g, x, s, __VA_ARGS__) - - -static HANDLE vhStdIn = INVALID_HANDLE_VALUE; -static HANDLE vhStdOut = INVALID_HANDLE_VALUE; -static BOOL vfConsoleIn = FALSE; -static BOOL vfConsoleOut = FALSE; -static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo; - - -extern "C" HRESULT DAPI ConsoleInitialize() -{ - Assert(INVALID_HANDLE_VALUE == vhStdOut); - HRESULT hr = S_OK; - UINT er; - - vhStdIn = ::GetStdHandle(STD_INPUT_HANDLE); - if (INVALID_HANDLE_VALUE == vhStdIn) - { - ConExitOnLastError(hr, "failed to open stdin"); - } - - vhStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); - if (INVALID_HANDLE_VALUE == vhStdOut) - { - ConExitOnLastError(hr, "failed to open stdout"); - } - - // check if we have a std in on the console - if (::GetConsoleScreenBufferInfo(vhStdIn, &vcsbiInfo)) - { - vfConsoleIn = TRUE; - } - else - { - er = ::GetLastError(); - if (ERROR_INVALID_HANDLE == er) - { - vfConsoleIn= FALSE; - hr = S_OK; - } - else - { - ConExitOnWin32Error(er, hr, "failed to get input console screen buffer info"); - } - } - - if (::GetConsoleScreenBufferInfo(vhStdOut, &vcsbiInfo)) - { - vfConsoleOut = TRUE; - } - else // no console - { - memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); - er = ::GetLastError(); - if (ERROR_INVALID_HANDLE == er) - { - vfConsoleOut = FALSE; - hr = S_OK; - } - else - { - ConExitOnWin32Error(er, hr, "failed to get output console screen buffer info"); - } - } - -LExit: - if (FAILED(hr)) - { - if (INVALID_HANDLE_VALUE != vhStdOut) - { - ::CloseHandle(vhStdOut); - } - - if (INVALID_HANDLE_VALUE != vhStdIn && vhStdOut != vhStdIn) - { - ::CloseHandle(vhStdIn); - } - - vhStdOut = INVALID_HANDLE_VALUE; - vhStdIn = INVALID_HANDLE_VALUE; - } - - return hr; -} - - -extern "C" void DAPI ConsoleUninitialize() -{ - BOOL fOutEqualsIn = vhStdOut == vhStdIn; - - memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); - - if (INVALID_HANDLE_VALUE != vhStdOut) - { - ::CloseHandle(vhStdOut); - } - - if (INVALID_HANDLE_VALUE != vhStdIn && !fOutEqualsIn) - { - ::CloseHandle(vhStdIn); - } - - vhStdOut = INVALID_HANDLE_VALUE; - vhStdIn = INVALID_HANDLE_VALUE; -} - - -extern "C" void DAPI ConsoleGreen() -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); - if (vfConsoleOut) - { - ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_GREEN | FOREGROUND_INTENSITY); - } -} - - -extern "C" void DAPI ConsoleRed() -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); - if (vfConsoleOut) - { - ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_INTENSITY); - } -} - - -extern "C" void DAPI ConsoleYellow() -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); - if (vfConsoleOut) - { - ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); - } -} - - -extern "C" void DAPI ConsoleNormal() -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); - if (vfConsoleOut) - { - ::SetConsoleTextAttribute(vhStdOut, vcsbiInfo.wAttributes); - } -} - - -/******************************************************************** - ConsoleWrite - full color printfA without libc - - NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d") - assumes already in normal color and resets the screen to normal color -********************************************************************/ -extern "C" HRESULT DAPI ConsoleWrite( - CONSOLE_COLOR cc, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); - HRESULT hr = S_OK; - LPSTR pszOutput = NULL; - DWORD cchOutput = 0; - DWORD cbWrote = 0; - DWORD cbTotal = 0; - - // set the color - switch (cc) - { - case CONSOLE_COLOR_NORMAL: break; // do nothing - case CONSOLE_COLOR_RED: ConsoleRed(); break; - case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break; - case CONSOLE_COLOR_GREEN: ConsoleGreen(); break; - } - - va_list args; - va_start(args, szFormat); - hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); - va_end(args); - ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); - - cchOutput = lstrlenA(pszOutput); - while (cbTotal < (sizeof(*pszOutput) * cchOutput)) - { - if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) - { - ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput); - } - - cbTotal += cbWrote; - } - - // reset the color to normal - if (CONSOLE_COLOR_NORMAL != cc) - { - ConsoleNormal(); - } - -LExit: - ReleaseStr(pszOutput); - return hr; -} - - -/******************************************************************** - ConsoleWriteLine - full color printfA plus newline without libc - - NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d") - assumes already in normal color and resets the screen to normal color -********************************************************************/ -extern "C" HRESULT DAPI ConsoleWriteLine( - CONSOLE_COLOR cc, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); - HRESULT hr = S_OK; - LPSTR pszOutput = NULL; - DWORD cchOutput = 0; - DWORD cbWrote = 0; - DWORD cbTotal = 0; - LPCSTR szNewLine = "\r\n"; - - // set the color - switch (cc) - { - case CONSOLE_COLOR_NORMAL: break; // do nothing - case CONSOLE_COLOR_RED: ConsoleRed(); break; - case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break; - case CONSOLE_COLOR_GREEN: ConsoleGreen(); break; - } - - va_list args; - va_start(args, szFormat); - hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); - va_end(args); - ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); - - // - // write the string - // - cchOutput = lstrlenA(pszOutput); - while (cbTotal < (sizeof(*pszOutput) * cchOutput)) - { - if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) - ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput); - - cbTotal += cbWrote; - } - - // - // write the newline - // - if (!::WriteFile(vhStdOut, reinterpret_cast(szNewLine), 2, &cbWrote, NULL)) - { - ConExitOnLastError(hr, "failed to write newline to console"); - } - - // reset the color to normal - if (CONSOLE_COLOR_NORMAL != cc) - { - ConsoleNormal(); - } - -LExit: - ReleaseStr(pszOutput); - return hr; -} - - -/******************************************************************** - ConsoleWriteError - display an error to the screen - - NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%s" or "%d") -********************************************************************/ -HRESULT ConsoleWriteError( - HRESULT hrError, - CONSOLE_COLOR cc, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - LPSTR pszMessage = NULL; - - va_list args; - va_start(args, szFormat); - hr = StrAnsiAllocFormattedArgs(&pszMessage, szFormat, args); - va_end(args); - ConExitOnFailure(hr, "failed to format error message: \"%s\"", szFormat); - - if (FAILED(hrError)) - { - hr = ConsoleWriteLine(cc, "Error 0x%x: %s", hrError, pszMessage); - } - else - { - hr = ConsoleWriteLine(cc, "Error: %s", pszMessage); - } - -LExit: - ReleaseStr(pszMessage); - return hr; -} - - -/******************************************************************** - ConsoleReadW - get console input without libc - - NOTE: only supports reading ANSI characters -********************************************************************/ -extern "C" HRESULT DAPI ConsoleReadW( - __deref_out_z LPWSTR* ppwzBuffer - ) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); - Assert(ppwzBuffer); - - HRESULT hr = S_OK; - LPSTR psz = NULL; - DWORD cch = 0; - DWORD cchRead = 0; - DWORD cchTotalRead = 0; - - cch = 64; - hr = StrAnsiAlloc(&psz, cch); - ConExitOnFailure(hr, "failed to allocate memory to read from console"); - - // loop until we read the \r\n from the console - for (;;) - { - // read one character at a time, since that seems to be the only way to make this work - if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) - ConExitOnLastError(hr, "failed to read string from console"); - - cchTotalRead += cchRead; - if (1 < cchTotalRead && '\r' == psz[cchTotalRead - 2] || '\n' == psz[cchTotalRead - 1]) - { - psz[cchTotalRead - 2] = '\0'; // chop off the \r\n - break; - } - else if (0 == cchRead) // nothing more was read - { - psz[cchTotalRead] = '\0'; // null termintate and bail - break; - } - - if (cchTotalRead == cch) - { - cch *= 2; // double everytime we run out of space - hr = StrAnsiAlloc(&psz, cch); - ConExitOnFailure(hr, "failed to allocate memory to read from console"); - } - } - - hr = StrAllocStringAnsi(ppwzBuffer, psz, 0, CP_ACP); - -LExit: - ReleaseStr(psz); - return hr; -} - - -/******************************************************************** - ConsoleReadNonBlockingW - Read from the console without blocking - Won't work for redirected files (exe < txtfile), but will work for stdin redirected to - an anonymous or named pipe - - if (fReadLine), stop reading immediately when \r\n is found -*********************************************************************/ -extern "C" HRESULT DAPI ConsoleReadNonBlockingW( - __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, - __out DWORD* pcchSize, - BOOL fReadLine - ) -{ - Assert(INVALID_HANDLE_VALUE != vhStdIn && pcchSize); - HRESULT hr = S_OK; - - LPSTR psz = NULL; - - ConExitOnNull(ppwzBuffer, hr, E_INVALIDARG, "Failed to read from console because buffer was not provided"); - - DWORD dwRead; - DWORD dwNumInput; - - DWORD cchTotal = 0; - DWORD cch = 8; - - DWORD cchRead = 0; - DWORD cchTotalRead = 0; - - DWORD dwIndex = 0; - DWORD er; - - INPUT_RECORD ir; - WCHAR chIn; - - *ppwzBuffer = NULL; - *pcchSize = 0; - - // If we really have a handle to stdin, and not the end of a pipe - if (!PeekNamedPipe(vhStdIn, NULL, 0, NULL, &dwRead, NULL)) - { - er = ::GetLastError(); - if (ERROR_INVALID_HANDLE != er) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(er)); - } - - if (!GetNumberOfConsoleInputEvents(vhStdIn, &dwRead)) - { - ConExitOnLastError(hr, "failed to peek at console input"); - } - - if (0 == dwRead) - { - ExitFunction1(hr = S_FALSE); - } - - for (/* dwRead from num of input events */; dwRead > 0; dwRead--) - { - if (!ReadConsoleInputW(vhStdIn, &ir, 1, &dwNumInput)) - { - ConExitOnLastError(hr, "Failed to read input from console"); - } - - // If what we have is a KEY_EVENT, and that event signifies keyUp, we're interested - if (KEY_EVENT == ir.EventType && FALSE == ir.Event.KeyEvent.bKeyDown) - { - chIn = ir.Event.KeyEvent.uChar.UnicodeChar; - - if (0 == cchTotal) - { - cchTotal = cch; - cch *= 2; - StrAlloc(ppwzBuffer, cch); - } - - (*ppwzBuffer)[dwIndex] = chIn; - - if (fReadLine && (L'\r' == (*ppwzBuffer)[dwIndex - 1] && L'\n' == (*ppwzBuffer)[dwIndex])) - { - *ppwzBuffer[dwIndex - 1] = L'\0'; - dwIndex -= 1; - break; - } - - ++dwIndex; - cchTotal--; - } - } - - *pcchSize = dwIndex; - } - else - { - // otherwise, the peek worked, and we have the end of a pipe - if (0 == dwRead) - ExitFunction1(hr = S_FALSE); - - cch = 8; - hr = StrAnsiAlloc(&psz, cch); - ConExitOnFailure(hr, "failed to allocate memory to read from console"); - - for (/*dwRead from PeekNamedPipe*/; dwRead > 0; dwRead--) - { - // read one character at a time, since that seems to be the only way to make this work - if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) - { - ConExitOnLastError(hr, "failed to read string from console"); - } - - cchTotalRead += cchRead; - if (fReadLine && '\r' == psz[cchTotalRead - 1] && '\n' == psz[cchTotalRead]) - { - psz[cchTotalRead - 1] = '\0'; // chop off the \r\n - cchTotalRead -= 1; - break; - } - else if (0 == cchRead) // nothing more was read - { - psz[cchTotalRead] = '\0'; // null termintate and bail - break; - } - - if (cchTotalRead == cch) - { - cch *= 2; // double everytime we run out of space - hr = StrAnsiAlloc(&psz, cch); - ConExitOnFailure(hr, "failed to allocate memory to read from console"); - } - } - - *pcchSize = cchTotalRead; - hr = StrAllocStringAnsi(ppwzBuffer, psz, cchTotalRead, CP_ACP); - } - -LExit: - ReleaseStr(psz); - - return hr; -} - - -/******************************************************************** - ConsoleReadStringA - get console input without libc - -*********************************************************************/ -extern "C" HRESULT DAPI ConsoleReadStringA( - __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer, - CONST DWORD cchCharBuffer, - __out DWORD* pcchNumCharReturn - ) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); - HRESULT hr = S_OK; - if (ppszCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2)) - { - DWORD iRead = 1; - DWORD iReadCharTotal = 0; - if (ppszCharBuffer && *ppszCharBuffer == NULL) - { - do - { - hr = StrAnsiAlloc(ppszCharBuffer, cchCharBuffer * iRead); - ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); - // ReadConsoleW will not return until , the last two chars are 13 and 10. - if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) - { - ConExitOnLastError(hr, "failed to read string from console"); - } - iReadCharTotal += *pcchNumCharReturn; - iRead += 1; - } - while((*ppszCharBuffer)[iReadCharTotal - 1] != 10 || (*ppszCharBuffer)[iReadCharTotal - 2] != 13); - *pcchNumCharReturn = iReadCharTotal; - } - else - { - if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || - *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) - { - ConExitOnLastError(hr, "failed to read string from console"); - } - if ((*ppszCharBuffer)[*pcchNumCharReturn - 1] != 10 || - (*ppszCharBuffer)[*pcchNumCharReturn - 2] != 13) - { - // need read more - hr = ERROR_MORE_DATA; - } - } - } - else - { - hr = E_INVALIDARG; - } - -LExit: - return hr; -} - -/******************************************************************** - ConsoleReadStringW - get console input without libc - -*********************************************************************/ -extern "C" HRESULT DAPI ConsoleReadStringW( - __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer, - const DWORD cchCharBuffer, - __out DWORD* pcchNumCharReturn - ) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); - HRESULT hr = S_OK; - if (ppwzCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2)) - { - DWORD iRead = 1; - DWORD iReadCharTotal = 0; - if (*ppwzCharBuffer == NULL) - { - do - { - hr = StrAlloc(ppwzCharBuffer, cchCharBuffer * iRead); - ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); - // ReadConsoleW will not return until , the last two chars are 13 and 10. - if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) - { - ConExitOnLastError(hr, "failed to read string from console"); - } - iReadCharTotal += *pcchNumCharReturn; - iRead += 1; - } - while((*ppwzCharBuffer)[iReadCharTotal - 1] != 10 || (*ppwzCharBuffer)[iReadCharTotal - 2] != 13); - *pcchNumCharReturn = iReadCharTotal; - } - else - { - if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || - *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) - { - ConExitOnLastError(hr, "failed to read string from console"); - } - if ((*ppwzCharBuffer)[*pcchNumCharReturn - 1] != 10 || - (*ppwzCharBuffer)[*pcchNumCharReturn - 2] != 13) - { - // need read more - hr = ERROR_MORE_DATA; - } - } - } - else - { - hr = E_INVALIDARG; - } - -LExit: - return hr; -} - -/******************************************************************** - ConsoleSetReadHidden - set console input no echo - -*********************************************************************/ -extern "C" HRESULT DAPI ConsoleSetReadHidden(void) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); - HRESULT hr = S_OK; - ::FlushConsoleInputBuffer(vhStdIn); - if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT)) - { - ConExitOnLastError(hr, "failed to set console input mode to be hidden"); - } - -LExit: - return hr; -} - -/******************************************************************** - ConsoleSetReadNormal - reset to echo - -*********************************************************************/ -extern "C" HRESULT DAPI ConsoleSetReadNormal(void) -{ - AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); - HRESULT hr = S_OK; - if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT)) - { - ConExitOnLastError(hr, "failed to set console input mode to be normal"); - } - -LExit: - return hr; -} - diff --git a/src/dutil/cryputil.cpp b/src/dutil/cryputil.cpp deleted file mode 100644 index 24bb83f1..00000000 --- a/src/dutil/cryputil.cpp +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define CrypExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) -#define CrypExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) -#define CrypExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) -#define CrypExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) -#define CrypExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) -#define CrypExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) -#define CrypExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CRYPUTIL, p, x, e, s, __VA_ARGS__) -#define CrypExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, p, x, s, __VA_ARGS__) -#define CrypExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, p, x, e, s, __VA_ARGS__) -#define CrypExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, p, x, s, __VA_ARGS__) -#define CrypExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CRYPUTIL, e, x, s, __VA_ARGS__) -#define CrypExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CRYPUTIL, g, x, s, __VA_ARGS__) - -static PFN_RTLENCRYPTMEMORY vpfnRtlEncryptMemory = NULL; -static PFN_RTLDECRYPTMEMORY vpfnRtlDecryptMemory = NULL; -static PFN_CRYPTPROTECTMEMORY vpfnCryptProtectMemory = NULL; -static PFN_CRYPTUNPROTECTMEMORY vpfnCryptUnprotectMemory = NULL; - -static HMODULE vhAdvApi32Dll = NULL; -static HMODULE vhCrypt32Dll = NULL; -static BOOL vfCrypInitialized = FALSE; - -// function definitions - -/******************************************************************** - CrypInitialize - initializes cryputil - -*********************************************************************/ -extern "C" HRESULT DAPI CrypInitialize( - ) -{ - HRESULT hr = S_OK; - - hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); - if (SUCCEEDED(hr)) - { - // Ignore failures - if these don't exist, we'll try the Crypt methods. - vpfnRtlEncryptMemory = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "SystemFunction040")); - vpfnRtlDecryptMemory = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "SystemFunction041")); - } - if (!vpfnRtlEncryptMemory || !vpfnRtlDecryptMemory) - { - hr = LoadSystemLibrary(L"Crypt32.dll", &vhCrypt32Dll); - CrypExitOnFailure(hr, "Failed to load Crypt32.dll"); - - vpfnCryptProtectMemory = reinterpret_cast(::GetProcAddress(vhCrypt32Dll, "CryptProtectMemory")); - if (!vpfnRtlEncryptMemory && !vpfnCryptProtectMemory) - { - CrypExitWithLastError(hr, "Failed to load an encryption method"); - } - vpfnCryptUnprotectMemory = reinterpret_cast(::GetProcAddress(vhCrypt32Dll, "CryptUnprotectMemory")); - if (!vpfnRtlDecryptMemory && !vpfnCryptUnprotectMemory) - { - CrypExitWithLastError(hr, "Failed to load a decryption method"); - } - } - - vfCrypInitialized = TRUE; - -LExit: - return hr; -} - - -/******************************************************************** - CrypUninitialize - uninitializes cryputil - -*********************************************************************/ -extern "C" void DAPI CrypUninitialize( - ) -{ - if (vhAdvApi32Dll) - { - ::FreeLibrary(vhAdvApi32Dll); - vhAdvApi32Dll = NULL; - vpfnRtlEncryptMemory = NULL; - vpfnRtlDecryptMemory = NULL; - } - - if (vhCrypt32Dll) - { - ::FreeLibrary(vhCrypt32Dll); - vhCrypt32Dll = NULL; - vpfnCryptProtectMemory = NULL; - vpfnCryptUnprotectMemory = NULL; - } - - vfCrypInitialized = FALSE; -} - -extern "C" HRESULT DAPI CrypDecodeObject( - __in_z LPCSTR szStructType, - __in_ecount(cbData) const BYTE* pbData, - __in DWORD cbData, - __in DWORD dwFlags, - __out LPVOID* ppvObject, - __out_opt DWORD* pcbObject - ) -{ - HRESULT hr = S_OK; - LPVOID pvObject = NULL; - DWORD cbObject = 0; - - if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, NULL, &cbObject)) - { - CrypExitWithLastError(hr, "Failed to decode object to determine size."); - } - - pvObject = MemAlloc(cbObject, TRUE); - CrypExitOnNull(pvObject, hr, E_OUTOFMEMORY, "Failed to allocate memory for decoded object."); - - if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, pvObject, &cbObject)) - { - CrypExitWithLastError(hr, "Failed to decode object."); - } - - *ppvObject = pvObject; - pvObject = NULL; - - if (pcbObject) - { - *pcbObject = cbObject; - } - -LExit: - ReleaseMem(pvObject); - - return hr; -} - - -extern "C" HRESULT DAPI CrypMsgGetParam( - __in HCRYPTMSG hCryptMsg, - __in DWORD dwType, - __in DWORD dwIndex, - __out LPVOID* ppvData, - __out_opt DWORD* pcbData - ) -{ - HRESULT hr = S_OK; - LPVOID pv = NULL; - DWORD cb = 0; - - if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, NULL, &cb)) - { - CrypExitWithLastError(hr, "Failed to get crypt message parameter data size."); - } - - pv = MemAlloc(cb, TRUE); - CrypExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for crypt message parameter."); - - if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, pv, &cb)) - { - CrypExitWithLastError(hr, "Failed to get crypt message parameter."); - } - - *ppvData = pv; - pv = NULL; - - if (pcbData) - { - *pcbData = cb; - } - -LExit: - ReleaseMem(pv); - - return hr; -} - - -extern "C" HRESULT DAPI CrypHashFile( - __in_z LPCWSTR wzFilePath, - __in DWORD dwProvType, - __in ALG_ID algid, - __out_bcount(cbHash) BYTE* pbHash, - __in DWORD cbHash, - __out_opt DWORD64* pqwBytesHashed - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - - // open input file - hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - CrypExitWithLastError(hr, "Failed to open input file."); - } - - hr = CrypHashFileHandle(hFile, dwProvType, algid, pbHash, cbHash, pqwBytesHashed); - CrypExitOnFailure(hr, "Failed to hash file: %ls", wzFilePath); - -LExit: - ReleaseFileHandle(hFile); - - return hr; -} - - -extern "C" HRESULT DAPI CrypHashFileHandle( - __in HANDLE hFile, - __in DWORD dwProvType, - __in ALG_ID algid, - __out_bcount(cbHash) BYTE* pbHash, - __in DWORD cbHash, - __out_opt DWORD64* pqwBytesHashed - ) -{ - HRESULT hr = S_OK; - HCRYPTPROV hProv = NULL; - HCRYPTHASH hHash = NULL; - DWORD cbRead = 0; - BYTE rgbBuffer[4096] = { }; - const LARGE_INTEGER liZero = { }; - - // get handle to the crypto provider - if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - { - CrypExitWithLastError(hr, "Failed to acquire crypto context."); - } - - // initiate hash - if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash)) - { - CrypExitWithLastError(hr, "Failed to initiate hash."); - } - - for (;;) - { - // read data block - if (!::ReadFile(hFile, rgbBuffer, sizeof(rgbBuffer), &cbRead, NULL)) - { - CrypExitWithLastError(hr, "Failed to read data block."); - } - - if (!cbRead) - { - break; // end of file - } - - // hash data block - if (!::CryptHashData(hHash, rgbBuffer, cbRead, 0)) - { - CrypExitWithLastError(hr, "Failed to hash data block."); - } - } - - // get hash value - if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) - { - CrypExitWithLastError(hr, "Failed to get hash value."); - } - - if (pqwBytesHashed) - { - if (!::SetFilePointerEx(hFile, liZero, (LARGE_INTEGER*)pqwBytesHashed, FILE_CURRENT)) - { - CrypExitWithLastError(hr, "Failed to get file pointer."); - } - } - -LExit: - if (hHash) - { - ::CryptDestroyHash(hHash); - } - if (hProv) - { - ::CryptReleaseContext(hProv, 0); - } - - return hr; -} - -HRESULT DAPI CrypHashBuffer( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __in DWORD dwProvType, - __in ALG_ID algid, - __out_bcount(cbHash) BYTE* pbHash, - __in DWORD cbHash - ) -{ - HRESULT hr = S_OK; - HCRYPTPROV hProv = NULL; - HCRYPTHASH hHash = NULL; - DWORD cbDataHashed = 0; - SIZE_T cbTotal = 0; - SIZE_T cbRemaining = 0; - - // get handle to the crypto provider - if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - { - CrypExitWithLastError(hr, "Failed to acquire crypto context."); - } - - // initiate hash - if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash)) - { - CrypExitWithLastError(hr, "Failed to initiate hash."); - } - - do - { - cbRemaining = cbBuffer - cbTotal; - cbDataHashed = (DWORD)min(DWORD_MAX, cbRemaining); - if (!::CryptHashData(hHash, pbBuffer + cbTotal, cbDataHashed, 0)) - { - CrypExitWithLastError(hr, "Failed to hash data."); - } - - cbTotal += cbDataHashed; - } while (cbTotal < cbBuffer); - - // get hash value - if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) - { - CrypExitWithLastError(hr, "Failed to get hash value."); - } - -LExit: - if (hHash) - { - ::CryptDestroyHash(hHash); - } - if (hProv) - { - ::CryptReleaseContext(hProv, 0); - } - - return hr; -} - -HRESULT DAPI CrypEncryptMemory( - __inout LPVOID pData, - __in DWORD cbData, - __in DWORD dwFlags - ) -{ - HRESULT hr = E_FAIL; - - if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE) - { - hr = E_INVALIDARG; - } - else if (vpfnRtlEncryptMemory) - { - hr = static_cast(vpfnRtlEncryptMemory(pData, cbData, dwFlags)); - } - else if (vpfnCryptProtectMemory) - { - if (vpfnCryptProtectMemory(pData, cbData, dwFlags)) - { - hr = S_OK; - } - else - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - } - } - CrypExitOnFailure(hr, "Failed to encrypt memory"); -LExit: - return hr; -} - -HRESULT DAPI CrypDecryptMemory( - __inout LPVOID pData, - __in DWORD cbData, - __in DWORD dwFlags - ) -{ - HRESULT hr = E_FAIL; - - if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE) - { - hr = E_INVALIDARG; - } - else if (vpfnRtlDecryptMemory) - { - hr = static_cast(vpfnRtlDecryptMemory(pData, cbData, dwFlags)); - } - else if (vpfnCryptUnprotectMemory) - { - if (vpfnCryptUnprotectMemory(pData, cbData, dwFlags)) - { - hr = S_OK; - } - else - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - } - } - CrypExitOnFailure(hr, "Failed to decrypt memory"); -LExit: - return hr; -} - diff --git a/src/dutil/deputil.cpp b/src/dutil/deputil.cpp deleted file mode 100644 index 2e6d6a6c..00000000 --- a/src/dutil/deputil.cpp +++ /dev/null @@ -1,699 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define DepExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) -#define DepExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) -#define DepExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) -#define DepExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) -#define DepExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) -#define DepExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) -#define DepExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DEPUTIL, p, x, e, s, __VA_ARGS__) -#define DepExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, p, x, s, __VA_ARGS__) -#define DepExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DEPUTIL, p, x, e, s, __VA_ARGS__) -#define DepExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, p, x, s, __VA_ARGS__) -#define DepExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEPUTIL, e, x, s, __VA_ARGS__) -#define DepExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DEPUTIL, g, x, s, __VA_ARGS__) - -#define ARRAY_GROWTH_SIZE 5 - -static LPCWSTR vcszVersionValue = L"Version"; -static LPCWSTR vcszDisplayNameValue = L"DisplayName"; -static LPCWSTR vcszMinVersionValue = L"MinVersion"; -static LPCWSTR vcszMaxVersionValue = L"MaxVersion"; -static LPCWSTR vcszAttributesValue = L"Attributes"; - -enum eRequiresAttributes { RequiresAttributesMinVersionInclusive = 256, RequiresAttributesMaxVersionInclusive = 512 }; - -// We write to Software\Classes explicitly based on the current security context instead of HKCR. -// See http://msdn.microsoft.com/en-us/library/ms724475(VS.85).aspx for more information. -static LPCWSTR vsczRegistryRoot = L"Software\\Classes\\Installer\\Dependencies\\"; -static LPCWSTR vsczRegistryDependents = L"Dependents"; - -static HRESULT AllocDependencyKeyName( - __in_z LPCWSTR wzName, - __deref_out_z LPWSTR* psczKeyName - ); - -static HRESULT GetDependencyNameFromKey( - __in HKEY hkHive, - __in LPCWSTR wzKey, - __deref_out_z LPWSTR* psczName - ); - -DAPI_(HRESULT) DepGetProviderInformation( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __deref_out_z_opt LPWSTR* psczId, - __deref_out_z_opt LPWSTR* psczName, - __deref_out_z_opt LPWSTR* psczVersion - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - HKEY hkKey = NULL; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); - - // Try to open the dependency key. - hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); - - // Get the Id if requested and available. - if (psczId) - { - hr = RegReadString(hkKey, NULL, psczId); - if (E_FILENOTFOUND == hr) - { - hr = S_OK; - } - DepExitOnFailure(hr, "Failed to get the id for the dependency \"%ls\".", wzProviderKey); - } - - // Get the DisplayName if requested and available. - if (psczName) - { - hr = RegReadString(hkKey, vcszDisplayNameValue, psczName); - if (E_FILENOTFOUND == hr) - { - hr = S_OK; - } - DepExitOnFailure(hr, "Failed to get the name for the dependency \"%ls\".", wzProviderKey); - } - - // Get the Version if requested and available. - if (psczVersion) - { - hr = RegReadString(hkKey, vcszVersionValue, psczVersion); - if (E_FILENOTFOUND == hr) - { - hr = S_OK; - } - DepExitOnFailure(hr, "Failed to get the version for the dependency \"%ls\".", wzProviderKey); - } - -LExit: - ReleaseRegKey(hkKey); - ReleaseStr(sczKey); - - return hr; -} - -DAPI_(HRESULT) DepCheckDependency( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __in_z_opt LPCWSTR wzMinVersion, - __in_z_opt LPCWSTR wzMaxVersion, - __in int iAttributes, - __in STRINGDICT_HANDLE sdDependencies, - __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, - __inout LPUINT pcDependencies - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - HKEY hkKey = NULL; - DWORD64 dw64Version = 0; - DWORD64 dw64MinVersion = 0; - DWORD64 dw64MaxVersion = 0; - BOOL fAllowEqual = FALSE; - LPWSTR sczName = NULL; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); - - // Try to open the key. If that fails, add the missing dependency key to the dependency array if it doesn't already exist. - hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open the registry key for dependency \"%ls\".", wzProviderKey); - - // If there are no registry values, consider the key orphaned and treat it as missing. - hr = RegReadVersion(hkKey, vcszVersionValue, &dw64Version); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to read the %ls registry value for dependency \"%ls\".", vcszVersionValue, wzProviderKey); - } - } - - // If the key was not found or the Version value was not found, add the missing dependency key to the dependency array. - if (E_FILENOTFOUND == hr) - { - hr = DictKeyExists(sdDependencies, wzProviderKey); - if (E_NOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to check the dictionary for missing dependency \"%ls\".", wzProviderKey); - } - else - { - hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, NULL); - DepExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the array.", wzProviderKey); - - hr = DictAddKey(sdDependencies, wzProviderKey); - DepExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the dictionary.", wzProviderKey); - } - - // Exit since the check already failed. - ExitFunction1(hr = E_NOTFOUND); - } - - // Check MinVersion if provided. - if (wzMinVersion) - { - if (*wzMinVersion) - { - hr = FileVersionFromStringEx(wzMinVersion, 0, &dw64MinVersion); - DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMinVersion); - - fAllowEqual = iAttributes & RequiresAttributesMinVersionInclusive; - if (!(fAllowEqual && dw64MinVersion <= dw64Version || dw64MinVersion < dw64Version)) - { - hr = DictKeyExists(sdDependencies, wzProviderKey); - if (E_NOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to check the dictionary for the older dependency \"%ls\".", wzProviderKey); - } - else - { - hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName); - DepExitOnFailure(hr, "Failed to get the display name of the older dependency \"%ls\".", wzProviderKey); - - hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName); - DepExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the dependencies array.", wzProviderKey); - - hr = DictAddKey(sdDependencies, wzProviderKey); - DepExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the unique dependency string list.", wzProviderKey); - } - - // Exit since the check already failed. - ExitFunction1(hr = E_NOTFOUND); - } - } - } - - // Check MaxVersion if provided. - if (wzMaxVersion) - { - if (*wzMaxVersion) - { - hr = FileVersionFromStringEx(wzMaxVersion, 0, &dw64MaxVersion); - DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMaxVersion); - - fAllowEqual = iAttributes & RequiresAttributesMaxVersionInclusive; - if (!(fAllowEqual && dw64Version <= dw64MaxVersion || dw64Version < dw64MaxVersion)) - { - hr = DictKeyExists(sdDependencies, wzProviderKey); - if (E_NOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to check the dictionary for the newer dependency \"%ls\".", wzProviderKey); - } - else - { - hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName); - DepExitOnFailure(hr, "Failed to get the display name of the newer dependency \"%ls\".", wzProviderKey); - - hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName); - DepExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the dependencies array.", wzProviderKey); - - hr = DictAddKey(sdDependencies, wzProviderKey); - DepExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the unique dependency string list.", wzProviderKey); - } - - // Exit since the check already failed. - ExitFunction1(hr = E_NOTFOUND); - } - } - } - -LExit: - ReleaseStr(sczName); - ReleaseRegKey(hkKey); - ReleaseStr(sczKey); - - return hr; -} - -DAPI_(HRESULT) DepCheckDependents( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __reserved int /*iAttributes*/, - __in C_STRINGDICT_HANDLE sdIgnoredDependents, - __deref_inout_ecount_opt(*pcDependents) DEPENDENCY** prgDependents, - __inout LPUINT pcDependents - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - HKEY hkProviderKey = NULL; - HKEY hkDependentsKey = NULL; - LPWSTR sczDependentKey = NULL; - LPWSTR sczDependentName = NULL; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); - - // Try to open the key. If that fails, the dependency information is corrupt. - hr = RegOpen(hkHive, sczKey, KEY_READ, &hkProviderKey); - DepExitOnFailure(hr, "Failed to open the registry key \"%ls\". The dependency store is corrupt.", sczKey); - - // Try to open the dependencies key. If that does not exist, there are no dependents. - hr = RegOpen(hkProviderKey, vsczRegistryDependents, KEY_READ, &hkDependentsKey); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open the registry key for dependents of \"%ls\".", wzProviderKey); - } - else - { - ExitFunction1(hr = S_OK); - } - - // Now enumerate the dependent keys. If they are not defined in the ignored list, add them to the array. - for (DWORD dwIndex = 0; ; ++dwIndex) - { - hr = RegKeyEnum(hkDependentsKey, dwIndex, &sczDependentKey); - if (E_NOMOREITEMS != hr) - { - DepExitOnFailure(hr, "Failed to enumerate the dependents key of \"%ls\".", wzProviderKey); - } - else - { - hr = S_OK; - break; - } - - // If the key isn't ignored, add it to the dependent array. - hr = DictKeyExists(sdIgnoredDependents, sczDependentKey); - if (E_NOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); - } - else - { - // Get the name of the dependent from the key. - hr = GetDependencyNameFromKey(hkHive, sczDependentKey, &sczDependentName); - DepExitOnFailure(hr, "Failed to get the name of the dependent from the key \"%ls\".", sczDependentKey); - - hr = DepDependencyArrayAlloc(prgDependents, pcDependents, sczDependentKey, sczDependentName); - DepExitOnFailure(hr, "Failed to add the dependent key \"%ls\" to the string array.", sczDependentKey); - } - } - -LExit: - ReleaseStr(sczDependentName); - ReleaseStr(sczDependentKey); - ReleaseRegKey(hkDependentsKey); - ReleaseRegKey(hkProviderKey); - ReleaseStr(sczKey); - - return hr; -} - -DAPI_(HRESULT) DepRegisterDependency( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __in_z LPCWSTR wzVersion, - __in_z LPCWSTR wzDisplayName, - __in_z_opt LPCWSTR wzId, - __in int iAttributes - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - HKEY hkKey = NULL; - BOOL fCreated = FALSE; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); - - // Create the dependency key (or open it if it already exists). - hr = RegCreateEx(hkHive, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated); - DepExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczKey); - - // Set the id if it was provided. - if (wzId) - { - hr = RegWriteString(hkKey, NULL, wzId); - DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", L"default", wzId); - } - - // Set the version. - hr = RegWriteString(hkKey, vcszVersionValue, wzVersion); - DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszVersionValue, wzVersion); - - // Set the display name. - hr = RegWriteString(hkKey, vcszDisplayNameValue, wzDisplayName); - DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszDisplayNameValue, wzDisplayName); - - // Set the attributes if non-zero. - if (0 != iAttributes) - { - hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast(iAttributes)); - DepExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); - } - -LExit: - ReleaseRegKey(hkKey); - ReleaseStr(sczKey); - - return hr; -} - -DAPI_(HRESULT) DepDependentExists( - __in HKEY hkHive, - __in_z LPCWSTR wzDependencyProviderKey, - __in_z LPCWSTR wzProviderKey - ) -{ - HRESULT hr = S_OK; - LPWSTR sczDependentKey = NULL; - HKEY hkDependentKey = NULL; - - // Format the provider dependents registry key. - hr = StrAllocFormatted(&sczDependentKey, L"%ls%ls\\%ls\\%ls", vsczRegistryRoot, wzDependencyProviderKey, vsczRegistryDependents, wzProviderKey); - DepExitOnFailure(hr, "Failed to format registry key to dependent."); - - hr = RegOpen(hkHive, sczDependentKey, KEY_READ, &hkDependentKey); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open the dependent registry key at: \"%ls\".", sczDependentKey); - } - -LExit: - ReleaseRegKey(hkDependentKey); - ReleaseStr(sczDependentKey); - - return hr; -} - -DAPI_(HRESULT) DepRegisterDependent( - __in HKEY hkHive, - __in_z LPCWSTR wzDependencyProviderKey, - __in_z LPCWSTR wzProviderKey, - __in_z_opt LPCWSTR wzMinVersion, - __in_z_opt LPCWSTR wzMaxVersion, - __in int iAttributes - ) -{ - HRESULT hr = S_OK; - LPWSTR sczDependencyKey = NULL; - HKEY hkDependencyKey = NULL; - LPWSTR sczKey = NULL; - HKEY hkKey = NULL; - BOOL fCreated = FALSE; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzDependencyProviderKey, &sczDependencyKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzDependencyProviderKey); - - // Create the dependency key (or open it if it already exists). - hr = RegCreateEx(hkHive, sczDependencyKey, KEY_WRITE, FALSE, NULL, &hkDependencyKey, &fCreated); - DepExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczDependencyKey); - - // Create the subkey to register the dependent. - hr = StrAllocFormatted(&sczKey, L"%ls\\%ls", vsczRegistryDependents, wzProviderKey); - DepExitOnFailure(hr, "Failed to allocate dependent subkey \"%ls\" under dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); - - hr = RegCreateEx(hkDependencyKey, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated); - DepExitOnFailure(hr, "Failed to create the dependency subkey \"%ls\".", sczKey); - - // Set the minimum version if not NULL. - hr = RegWriteString(hkKey, vcszMinVersionValue, wzMinVersion); - DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMinVersionValue, wzMinVersion); - - // Set the maximum version if not NULL. - hr = RegWriteString(hkKey, vcszMaxVersionValue, wzMaxVersion); - DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMaxVersionValue, wzMaxVersion); - - // Set the attributes if non-zero. - if (0 != iAttributes) - { - hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast(iAttributes)); - DepExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); - } - -LExit: - ReleaseRegKey(hkKey); - ReleaseStr(sczKey); - ReleaseRegKey(hkDependencyKey); - ReleaseStr(sczDependencyKey); - - return hr; -} - -DAPI_(HRESULT) DepUnregisterDependency( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - HKEY hkKey = NULL; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); - - // Delete the entire key including all sub-keys. - hr = RegDelete(hkHive, sczKey, REG_KEY_DEFAULT, TRUE); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to delete the key \"%ls\".", sczKey); - } - -LExit: - ReleaseRegKey(hkKey); - ReleaseStr(sczKey); - - return hr; -} - -DAPI_(HRESULT) DepUnregisterDependent( - __in HKEY hkHive, - __in_z LPCWSTR wzDependencyProviderKey, - __in_z LPCWSTR wzProviderKey - ) -{ - HRESULT hr = S_OK; - HKEY hkRegistryRoot = NULL; - HKEY hkDependencyProviderKey = NULL; - HKEY hkRegistryDependents = NULL; - DWORD cSubKeys = 0; - DWORD cValues = 0; - - // Open the root key. We may delete the wzDependencyProviderKey during clean up. - hr = RegOpen(hkHive, vsczRegistryRoot, KEY_READ, &hkRegistryRoot); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open root registry key \"%ls\".", vsczRegistryRoot); - } - else - { - ExitFunction(); - } - - // Try to open the dependency key. If that does not exist, simply return. - hr = RegOpen(hkRegistryRoot, wzDependencyProviderKey, KEY_READ, &hkDependencyProviderKey); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzDependencyProviderKey); - } - else - { - ExitFunction(); - } - - // Try to open the dependents subkey to enumerate. - hr = RegOpen(hkDependencyProviderKey, vsczRegistryDependents, KEY_READ, &hkRegistryDependents); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); - } - else - { - ExitFunction(); - } - - // Delete the wzProviderKey dependent sub-key. - hr = RegDelete(hkRegistryDependents, wzProviderKey, REG_KEY_DEFAULT, TRUE); - DepExitOnFailure(hr, "Failed to delete the dependent \"%ls\" under the dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); - - // If there are no remaining dependents, delete the Dependents subkey. - hr = RegQueryKey(hkRegistryDependents, &cSubKeys, NULL); - DepExitOnFailure(hr, "Failed to get the number of dependent subkeys under the dependency \"%ls\".", wzDependencyProviderKey); - - if (0 < cSubKeys) - { - ExitFunction(); - } - - // Release the handle to make sure it's deleted immediately. - ReleaseRegKey(hkRegistryDependents); - - // Fail if there are any subkeys since we just checked. - hr = RegDelete(hkDependencyProviderKey, vsczRegistryDependents, REG_KEY_DEFAULT, FALSE); - DepExitOnFailure(hr, "Failed to delete the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); - - // If there are no values, delete the provider dependency key. - hr = RegQueryKey(hkDependencyProviderKey, NULL, &cValues); - DepExitOnFailure(hr, "Failed to get the number of values under the dependency \"%ls\".", wzDependencyProviderKey); - - if (0 == cValues) - { - // Release the handle to make sure it's deleted immediately. - ReleaseRegKey(hkDependencyProviderKey); - - // Fail if there are any subkeys since we just checked. - hr = RegDelete(hkRegistryRoot, wzDependencyProviderKey, REG_KEY_DEFAULT, FALSE); - DepExitOnFailure(hr, "Failed to delete the dependency \"%ls\".", wzDependencyProviderKey); - } - -LExit: - ReleaseRegKey(hkRegistryDependents); - ReleaseRegKey(hkDependencyProviderKey); - ReleaseRegKey(hkRegistryRoot); - - return hr; -} - -DAPI_(HRESULT) DepDependencyArrayAlloc( - __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, - __inout LPUINT pcDependencies, - __in_z LPCWSTR wzKey, - __in_z_opt LPCWSTR wzName - ) -{ - HRESULT hr = S_OK; - UINT cRequired = 0; - DEPENDENCY* pDependency = NULL; - - hr = ::UIntAdd(*pcDependencies, 1, &cRequired); - DepExitOnFailure(hr, "Failed to increment the number of elements required in the dependency array."); - - hr = MemEnsureArraySize(reinterpret_cast(prgDependencies), cRequired, sizeof(DEPENDENCY), ARRAY_GROWTH_SIZE); - DepExitOnFailure(hr, "Failed to allocate memory for the dependency array."); - - pDependency = static_cast(&(*prgDependencies)[*pcDependencies]); - DepExitOnNull(pDependency, hr, E_POINTER, "The dependency element in the array is invalid."); - - hr = StrAllocString(&(pDependency->sczKey), wzKey, 0); - DepExitOnFailure(hr, "Failed to allocate the string key in the dependency array."); - - if (wzName) - { - hr = StrAllocString(&(pDependency->sczName), wzName, 0); - DepExitOnFailure(hr, "Failed to allocate the string name in the dependency array."); - } - - // Update the number of current elements in the dependency array. - *pcDependencies = cRequired; - -LExit: - return hr; -} - -DAPI_(void) DepDependencyArrayFree( - __in_ecount(cDependencies) DEPENDENCY* rgDependencies, - __in UINT cDependencies - ) -{ - for (UINT i = 0; i < cDependencies; ++i) - { - ReleaseStr(rgDependencies[i].sczKey); - ReleaseStr(rgDependencies[i].sczName); - } - - ReleaseMem(rgDependencies); -} - -/*************************************************************************** - AllocDependencyKeyName - Allocates and formats the root registry key name. - -***************************************************************************/ -static HRESULT AllocDependencyKeyName( - __in_z LPCWSTR wzName, - __deref_out_z LPWSTR* psczKeyName - ) -{ - HRESULT hr = S_OK; - size_t cchName = 0; - size_t cchKeyName = 0; - - // Get the length of the static registry root once. - static size_t cchRegistryRoot = ::lstrlenW(vsczRegistryRoot); - - // Get the length of the dependency, and add to the length of the root. - hr = ::StringCchLengthW(wzName, STRSAFE_MAX_CCH, &cchName); - DepExitOnFailure(hr, "Failed to get string length of dependency name."); - - // Add the sizes together to allocate memory once (callee will add space for nul). - hr = ::SizeTAdd(cchRegistryRoot, cchName, &cchKeyName); - DepExitOnFailure(hr, "Failed to add the string lengths together."); - - // Allocate and concat the strings together. - hr = StrAllocString(psczKeyName, vsczRegistryRoot, cchKeyName); - DepExitOnFailure(hr, "Failed to allocate string for dependency registry root."); - - hr = StrAllocConcat(psczKeyName, wzName, cchName); - DepExitOnFailure(hr, "Failed to concatenate the dependency key name."); - -LExit: - return hr; -} - -/*************************************************************************** - GetDependencyNameFromKey - Attempts to name of the dependency from the key. - -***************************************************************************/ -static HRESULT GetDependencyNameFromKey( - __in HKEY hkHive, - __in LPCWSTR wzProviderKey, - __deref_out_z LPWSTR* psczName - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - HKEY hkKey = NULL; - - // Format the provider dependency registry key. - hr = AllocDependencyKeyName(wzProviderKey, &sczKey); - DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); - - // Try to open the dependency key. - hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); - } - else - { - ExitFunction1(hr = S_OK); - } - - // Get the DisplayName if available. - hr = RegReadString(hkKey, vcszDisplayNameValue, psczName); - if (E_FILENOTFOUND != hr) - { - DepExitOnFailure(hr, "Failed to get the dependency name for the dependency \"%ls\".", wzProviderKey); - } - else - { - ExitFunction1(hr = S_OK); - } - -LExit: - ReleaseRegKey(hkKey); - ReleaseStr(sczKey); - - return hr; -} diff --git a/src/dutil/dictutil.cpp b/src/dutil/dictutil.cpp deleted file mode 100644 index 0d0743eb..00000000 --- a/src/dutil/dictutil.cpp +++ /dev/null @@ -1,784 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define DictExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) -#define DictExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) -#define DictExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) -#define DictExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) -#define DictExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) -#define DictExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) -#define DictExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__) -#define DictExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__) -#define DictExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__) -#define DictExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__) -#define DictExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DICTUTIL, e, x, s, __VA_ARGS__) -#define DictExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DICTUTIL, g, x, s, __VA_ARGS__) - -// These should all be primes, and spaced reasonably apart (currently each is about 4x the last) -const DWORD MAX_BUCKET_SIZES[] = { - 503, - 2017, - 7937, - 32779, - 131111, - 524341, - 2097709, - 8390857, - 33563437, - 134253719, - 537014927, - 2148059509 - }; - -// However many items are in the cab, let's keep the buckets at least 8 times that to avoid collisions -#define MAX_BUCKETS_TO_ITEMS_RATIO 8 - -enum DICT_TYPE -{ - DICT_INVALID = 0, - DICT_EMBEDDED_KEY = 1, - DICT_STRING_LIST = 2 -}; - -struct STRINGDICT_STRUCT -{ - DICT_TYPE dtType; - - // Optional flags to control the behavior of the dictionary. - DICT_FLAG dfFlags; - - // Index into MAX_BUCKET_SIZES (array of primes), representing number of buckets we've allocated - DWORD dwBucketSizeIndex; - - // Number of items currently stored in the dict buckets - DWORD dwNumItems; - - // Byte offset of key within bucket value, for collision checking - see - // comments above DictCreateEmbeddedKey() implementation for further details - size_t cByteOffset; - - // The actual stored buckets - void **ppvBuckets; - - // The actual stored items in the order they were added (used for auto freeing or enumerating) - void **ppvItemList; - - // Pointer to the array of items, so the caller is free to resize the array of values out from under us without harm - void **ppvValueArray; -}; - -const int STRINGDICT_HANDLE_BYTES = sizeof(STRINGDICT_STRUCT); - -static HRESULT StringHash( - __in const STRINGDICT_STRUCT *psd, - __in DWORD dwNumBuckets, - __in_z LPCWSTR pszString, - __out DWORD *pdwHash - ); -static BOOL IsMatchExact( - __in const STRINGDICT_STRUCT *psd, - __in DWORD dwMatchIndex, - __in_z LPCWSTR wzOriginalString - ); -static HRESULT GetValue( - __in const STRINGDICT_STRUCT *psd, - __in_z LPCWSTR pszString, - __out_opt void **ppvValue - ); -static HRESULT GetInsertIndex( - __in const STRINGDICT_STRUCT *psd, - __in DWORD dwBucketCount, - __in void **ppvBuckets, - __in_z LPCWSTR pszString, - __out DWORD *pdwOutput - ); -static HRESULT GetIndex( - __in const STRINGDICT_STRUCT *psd, - __in_z LPCWSTR pszString, - __out DWORD *pdwOutput - ); -static LPCWSTR GetKey( - __in const STRINGDICT_STRUCT *psd, - __in void *pvValue - ); -static HRESULT GrowDictionary( - __inout STRINGDICT_STRUCT *psd - ); -// These 2 helper functions allow us to safely handle dictutil consumers resizing -// the value array by storing "offsets" instead of raw void *'s in our buckets. -static void * TranslateOffsetToValue( - __in const STRINGDICT_STRUCT *psd, - __in void *pvValue - ); -static void * TranslateValueToOffset( - __in const STRINGDICT_STRUCT *psd, - __in void *pvValue - ); - -// The dict will store a set of keys (as wide-char strings) and a set of values associated with those keys (as void *'s). -// However, to support collision checking, the key needs to be represented in the "value" object (pointed to -// by the void *). The "stByteOffset" parameter tells this dict the byte offset of the "key" string pointer -// within the "value" object. Use the offsetof() macro to fill this out. -// The "ppvArray" parameter gives dictutil the address of your value array. If you provide this parameter, -// dictutil will remember all pointer values provided as "offsets" against this array. It is only necessary to provide -// this parameter to dictutil if it is possible you will realloc the array. -// -// Use DictAddValue() and DictGetValue() with this dictionary type. -extern "C" HRESULT DAPI DictCreateWithEmbeddedKey( - __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, - __in DWORD dwNumExpectedItems, - __in_opt void **ppvArray, - __in size_t cByteOffset, - __in DICT_FLAG dfFlags - ) -{ - HRESULT hr = S_OK; - - DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); - - // Allocate the handle - *psdHandle = static_cast(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE)); - DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); - - STRINGDICT_STRUCT *psd = static_cast(*psdHandle); - - // Fill out the new handle's values - psd->dtType = DICT_EMBEDDED_KEY; - psd->dfFlags = dfFlags; - psd->cByteOffset = cByteOffset; - psd->dwBucketSizeIndex = 0; - psd->dwNumItems = 0; - psd->ppvItemList = NULL; - psd->ppvValueArray = ppvArray; - - // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime - // array based on expected number of items and items to buckets ratio - // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end - // this loop past the end of the array! - while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) && - MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO) - { - ++psd->dwBucketSizeIndex; - } - - // Finally, allocate our initial buckets - psd->ppvBuckets = static_cast(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE)); - DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); - -LExit: - return hr; -} - -// The dict will store a set of keys, with no values associated with them. Use DictAddKey() and DictKeyExists() with this dictionary type. -extern "C" HRESULT DAPI DictCreateStringList( - __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, - __in DWORD dwNumExpectedItems, - __in DICT_FLAG dfFlags - ) -{ - HRESULT hr = S_OK; - - DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); - - // Allocate the handle - *psdHandle = static_cast(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE)); - DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); - - STRINGDICT_STRUCT *psd = static_cast(*psdHandle); - - // Fill out the new handle's values - psd->dtType = DICT_STRING_LIST; - psd->dfFlags = dfFlags; - psd->cByteOffset = 0; - psd->dwBucketSizeIndex = 0; - psd->dwNumItems = 0; - psd->ppvItemList = NULL; - psd->ppvValueArray = NULL; - - // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime - // array based on expected number of items and items to buckets ratio - // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end - // this loop past the end of the array! - while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) && - MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO) - { - ++psd->dwBucketSizeIndex; - } - - // Finally, allocate our initial buckets - psd->ppvBuckets = static_cast(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE)); - DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI DictCreateStringListFromArray( - __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, - __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, - __in const DWORD cStringArray, - __in DICT_FLAG dfFlags - ) -{ - HRESULT hr = S_OK; - STRINGDICT_HANDLE sd = NULL; - - hr = DictCreateStringList(&sd, cStringArray, dfFlags); - DictExitOnFailure(hr, "Failed to create the string dictionary."); - - for (DWORD i = 0; i < cStringArray; ++i) - { - const LPCWSTR wzKey = rgwzStringArray[i]; - - hr = DictKeyExists(sd, wzKey); - if (E_NOTFOUND != hr) - { - DictExitOnFailure(hr, "Failed to check the string dictionary."); - } - else - { - hr = DictAddKey(sd, wzKey); - DictExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzKey); - } - } - - *psdHandle = sd; - sd = NULL; - -LExit: - ReleaseDict(sd); - - return hr; -} - -extern "C" HRESULT DAPI DictCompareStringListToArray( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList, - __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, - __in const DWORD cStringArray - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < cStringArray; ++i) - { - hr = DictKeyExists(sdStringList, rgwzStringArray[i]); - if (E_NOTFOUND != hr) - { - DictExitOnFailure(hr, "Failed to check the string dictionary."); - ExitFunction1(hr = S_OK); - } - } - - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_NO_MATCH)); - -LExit: - return hr; -} - -// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO) -extern "C" HRESULT DAPI DictAddKey( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, - __in_z LPCWSTR pszString - ) -{ - HRESULT hr = S_OK; - DWORD dwIndex = 0; - STRINGDICT_STRUCT *psd = static_cast(sdHandle); - - DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); - DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while adding value to dict"); - - if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); - } - - if (DICT_STRING_LIST != psd->dtType) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Tried to add key without value to wrong dictionary type! This dictionary type is: %d", psd->dtType); - } - - if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO) - { - hr = GrowDictionary(psd); - if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr) - { - // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full - if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) - { - hr = S_OK; - } - } - DictExitOnFailure(hr, "Failed to grow dictionary"); - } - - hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, pszString, &dwIndex); - DictExitOnFailure(hr, "Failed to get index to insert into"); - - hr = MemEnsureArraySize(reinterpret_cast(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000); - DictExitOnFailure(hr, "Failed to resize list of items in dictionary"); - ++psd->dwNumItems; - - hr = StrAllocString(reinterpret_cast(&(psd->ppvBuckets[dwIndex])), pszString, 0); - DictExitOnFailure(hr, "Failed to allocate copy of string"); - - psd->ppvItemList[psd->dwNumItems-1] = psd->ppvBuckets[dwIndex]; - -LExit: - return hr; -} - -// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO) -extern "C" HRESULT DAPI DictAddValue( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, - __in void *pvValue - ) -{ - HRESULT hr = S_OK; - void *pvOffset = NULL; - LPCWSTR wzKey = NULL; - DWORD dwIndex = 0; - STRINGDICT_STRUCT *psd = static_cast(sdHandle); - - DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); - DictExitOnNull(pvValue, hr, E_INVALIDARG, "Value not specified while adding value to dict"); - - if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); - } - - if (DICT_EMBEDDED_KEY != psd->dtType) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Tried to add key/value pair to wrong dictionary type! This dictionary type is: %d", psd->dtType); - } - - wzKey = GetKey(psd, pvValue); - DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified while adding value to dict"); - - if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO) - { - hr = GrowDictionary(psd); - if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr && psd->dwNumItems + 1 ) - { - // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full - if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) - { - hr = S_OK; - } - } - DictExitOnFailure(hr, "Failed to grow dictionary"); - } - - hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, wzKey, &dwIndex); - DictExitOnFailure(hr, "Failed to get index to insert into"); - - hr = MemEnsureArraySize(reinterpret_cast(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000); - DictExitOnFailure(hr, "Failed to resize list of items in dictionary"); - ++psd->dwNumItems; - - pvOffset = TranslateValueToOffset(psd, pvValue); - psd->ppvBuckets[dwIndex] = pvOffset; - psd->ppvItemList[psd->dwNumItems-1] = pvOffset; - -LExit: - return hr; -} - -extern "C" HRESULT DAPI DictGetValue( - __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, - __in_z LPCWSTR pszString, - __out void **ppvValue - ) -{ - HRESULT hr = S_OK; - - DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); - DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); - - const STRINGDICT_STRUCT *psd = static_cast(sdHandle); - - if (DICT_EMBEDDED_KEY != psd->dtType) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Tried to lookup value in wrong dictionary type! This dictionary type is: %d", psd->dtType); - } - - hr = GetValue(psd, pszString, ppvValue); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - DictExitOnFailure(hr, "Failed to call internal GetValue()"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI DictKeyExists( - __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, - __in_z LPCWSTR pszString - ) -{ - HRESULT hr = S_OK; - - DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); - DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); - - const STRINGDICT_STRUCT *psd = static_cast(sdHandle); - - // This works with either type of dictionary - hr = GetValue(psd, pszString, NULL); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - DictExitOnFailure(hr, "Failed to call internal GetValue()"); - -LExit: - return hr; -} - -extern "C" void DAPI DictDestroy( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle - ) -{ - DWORD i; - - STRINGDICT_STRUCT *psd = static_cast(sdHandle); - - if (DICT_STRING_LIST == psd->dtType) - { - for (i = 0; i < psd->dwNumItems; ++i) - { - ReleaseStr(reinterpret_cast(psd->ppvItemList[i])); - } - } - - ReleaseMem(psd->ppvItemList); - ReleaseMem(psd->ppvBuckets); - ReleaseMem(psd); -} - -static HRESULT StringHash( - __in const STRINGDICT_STRUCT *psd, - __in DWORD dwNumBuckets, - __in_z LPCWSTR pszString, - __out DWORD *pdwHash - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzKey = NULL; - LPWSTR sczNewKey = NULL; - DWORD result = 0; - - if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags) - { - hr = StrAllocStringToUpperInvariant(&sczNewKey, pszString, 0); - DictExitOnFailure(hr, "Failed to convert the string to upper-case."); - - wzKey = sczNewKey; - } - else - { - wzKey = pszString; - } - - while (*wzKey) - { - result = ~(*wzKey++ * 509) + result * 65599; - } - - *pdwHash = result % dwNumBuckets; - -LExit: - ReleaseStr(sczNewKey); - - return hr; -} - -static BOOL IsMatchExact( - __in const STRINGDICT_STRUCT *psd, - __in DWORD dwMatchIndex, - __in_z LPCWSTR wzOriginalString - ) -{ - LPCWSTR wzMatchString = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvBuckets[dwMatchIndex])); - DWORD dwFlags = 0; - - if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags) - { - dwFlags |= NORM_IGNORECASE; - } - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwFlags, wzOriginalString, -1, wzMatchString, -1)) - { - return TRUE; - } - - return FALSE; -} - -static HRESULT GetValue( - __in const STRINGDICT_STRUCT *psd, - __in_z LPCWSTR pszString, - __out_opt void **ppvValue - ) -{ - HRESULT hr = S_OK; - DWORD dwOriginalIndexCandidate = 0; - void *pvCandidateValue = NULL; - DWORD dwIndex = 0; - - DictExitOnNull(psd, hr, E_INVALIDARG, "Handle not specified while searching dict"); - DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); - - if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); - } - - hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate); - DictExitOnFailure(hr, "Failed to hash the string."); - - DWORD dwIndexCandidate = dwOriginalIndexCandidate; - - pvCandidateValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndexCandidate]); - - // If no match exists in the dict - if (NULL == pvCandidateValue) - { - if (NULL != ppvValue) - { - *ppvValue = NULL; - } - ExitFunction1(hr = E_NOTFOUND); - } - - hr = GetIndex(psd, pszString, &dwIndex); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - DictExitOnFailure(hr, "Failed to find index to get"); - - if (NULL != ppvValue) - { - *ppvValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndex]); - } - -LExit: - if (FAILED(hr) && NULL != ppvValue) - { - *ppvValue = NULL; - } - - return hr; -} - -static HRESULT GetInsertIndex( - __in const STRINGDICT_STRUCT *psd, - __in DWORD dwBucketCount, - __in void **ppvBuckets, - __in_z LPCWSTR pszString, - __out DWORD *pdwOutput - ) -{ - HRESULT hr = S_OK; - DWORD dwOriginalIndexCandidate = 0; - - hr = StringHash(psd, dwBucketCount, pszString, &dwOriginalIndexCandidate); - DictExitOnFailure(hr, "Failed to hash the string."); - - DWORD dwIndexCandidate = dwOriginalIndexCandidate; - - // If we collide, keep iterating forward from our intended position, even wrapping around to zero, until we find an empty bucket -#pragma prefast(push) -#pragma prefast(disable:26007) - while (NULL != ppvBuckets[dwIndexCandidate]) -#pragma prefast(pop) - { - ++dwIndexCandidate; - - // If we got to the end of the array, wrap around to zero index - if (dwIndexCandidate >= dwBucketCount) - { - dwIndexCandidate = 0; - } - - // If we wrapped all the way back around to our original index, the dict is full - throw an error - if (dwIndexCandidate == dwOriginalIndexCandidate) - { - // The dict table is full - this error seems to be a reasonably close match - hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL); - DictExitOnRootFailure(hr, "Failed to add item '%ls' to dict table because dict table is full of items", pszString); - } - } - - *pdwOutput = dwIndexCandidate; - -LExit: - return hr; -} - -static HRESULT GetIndex( - __in const STRINGDICT_STRUCT *psd, - __in_z LPCWSTR pszString, - __out DWORD *pdwOutput - ) -{ - HRESULT hr = S_OK; - DWORD dwOriginalIndexCandidate = 0; - - if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) - { - hr = E_INVALIDARG; - DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); - } - - hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate); - DictExitOnFailure(hr, "Failed to hash the string."); - - DWORD dwIndexCandidate = dwOriginalIndexCandidate; - - while (!IsMatchExact(psd, dwIndexCandidate, pszString)) - { - ++dwIndexCandidate; - - // If we got to the end of the array, wrap around to zero index - if (dwIndexCandidate >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) - { - dwIndexCandidate = 0; - } - - // If no match exists in the dict - if (NULL == psd->ppvBuckets[dwIndexCandidate]) - { - ExitFunction1(hr = E_NOTFOUND); - } - - // If we wrapped all the way back around to our original index, the dict is full and we found nothing, so return as such - if (dwIndexCandidate == dwOriginalIndexCandidate) - { - ExitFunction1(hr = E_NOTFOUND); - } - } - - *pdwOutput = dwIndexCandidate; - -LExit: - return hr; -} - -static LPCWSTR GetKey( - __in const STRINGDICT_STRUCT *psd, - __in void *pvValue - ) -{ - const BYTE *lpByte = reinterpret_cast(pvValue); - - if (DICT_EMBEDDED_KEY == psd->dtType) - { - void *pvKey = reinterpret_cast(reinterpret_cast(pvValue) + psd->cByteOffset); - -#pragma prefast(push) -#pragma prefast(disable:26010) - return *(reinterpret_cast(pvKey)); -#pragma prefast(pop) - } - else - { - return (reinterpret_cast(lpByte)); - } -} - -static HRESULT GrowDictionary( - __inout STRINGDICT_STRUCT *psd - ) -{ - HRESULT hr = S_OK; - DWORD dwInsertIndex = 0; - LPCWSTR wzKey = NULL; - DWORD dwNewBucketSizeIndex = 0; - size_t cbAllocSize = 0; - void **ppvNewBuckets = NULL; - - dwNewBucketSizeIndex = psd->dwBucketSizeIndex + 1; - - if (dwNewBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL)); - } - - hr = ::SizeTMult(sizeof(void *), MAX_BUCKET_SIZES[dwNewBucketSizeIndex], &cbAllocSize); - DictExitOnFailure(hr, "Overflow while calculating allocation size to grow dictionary"); - - ppvNewBuckets = static_cast(MemAlloc(cbAllocSize, TRUE)); - DictExitOnNull(ppvNewBuckets, hr, E_OUTOFMEMORY, "Failed to allocate %u buckets while growing dictionary", MAX_BUCKET_SIZES[dwNewBucketSizeIndex]); - - for (DWORD i = 0; i < psd->dwNumItems; ++i) - { - wzKey = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvItemList[i])); - DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified in existing dict value"); - - hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[dwNewBucketSizeIndex], ppvNewBuckets, wzKey, &dwInsertIndex); - DictExitOnFailure(hr, "Failed to get index to insert into"); - - ppvNewBuckets[dwInsertIndex] = psd->ppvItemList[i]; - } - - psd->dwBucketSizeIndex = dwNewBucketSizeIndex; - ReleaseMem(psd->ppvBuckets); - psd->ppvBuckets = ppvNewBuckets; - ppvNewBuckets = NULL; - -LExit: - ReleaseMem(ppvNewBuckets); - - return hr; -} - -static void * TranslateOffsetToValue( - __in const STRINGDICT_STRUCT *psd, - __in void *pvValue - ) -{ - if (NULL == pvValue) - { - return NULL; - } - - // All offsets are stored as (real offset + 1), so subtract 1 to get back to the real value - if (NULL != psd->ppvValueArray) - { - return reinterpret_cast(reinterpret_cast(pvValue) + reinterpret_cast(*psd->ppvValueArray) - 1); - } - else - { - return pvValue; - } -} - -static void * TranslateValueToOffset( - __in const STRINGDICT_STRUCT *psd, - __in void *pvValue - ) -{ - if (NULL != psd->ppvValueArray) - { - // 0 has a special meaning - we don't want offset 0 into the array to have NULL for the offset - so add 1 to avoid this issue - return reinterpret_cast(reinterpret_cast(pvValue) - reinterpret_cast(*psd->ppvValueArray) + 1); - } - else - { - return pvValue; - } -} diff --git a/src/dutil/dirutil.cpp b/src/dutil/dirutil.cpp deleted file mode 100644 index ae2c5e1c..00000000 --- a/src/dutil/dirutil.cpp +++ /dev/null @@ -1,441 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define DirExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) -#define DirExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) -#define DirExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) -#define DirExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) -#define DirExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) -#define DirExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) -#define DirExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__) -#define DirExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__) -#define DirExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__) -#define DirExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__) -#define DirExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DIRUTIL, e, x, s, __VA_ARGS__) -#define DirExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DIRUTIL, g, x, s, __VA_ARGS__) - - -/******************************************************************* - DirExists - -*******************************************************************/ -extern "C" BOOL DAPI DirExists( - __in_z LPCWSTR wzPath, - __out_opt DWORD *pdwAttributes - ) -{ - Assert(wzPath); - - BOOL fExists = FALSE; - - DWORD dwAttributes = ::GetFileAttributesW(wzPath); - if (0xFFFFFFFF == dwAttributes) // TODO: figure out why "INVALID_FILE_ATTRIBUTES" can't be used here - { - ExitFunction(); - } - - if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - if (pdwAttributes) - { - *pdwAttributes = dwAttributes; - } - - fExists = TRUE; - } - -LExit: - return fExists; -} - - -/******************************************************************* - DirCreateTempPath - - *******************************************************************/ -extern "C" HRESULT DAPI DirCreateTempPath( - __in_z LPCWSTR wzPrefix, - __out_ecount_z(cchPath) LPWSTR wzPath, - __in DWORD cchPath - ) -{ - Assert(wzPrefix); - Assert(wzPath); - - HRESULT hr = S_OK; - - WCHAR wzDir[MAX_PATH]; - WCHAR wzFile[MAX_PATH]; - DWORD cch = 0; - - cch = ::GetTempPathW(countof(wzDir), wzDir); - if (!cch || cch >= countof(wzDir)) - { - DirExitWithLastError(hr, "Failed to GetTempPath."); - } - - if (!::GetTempFileNameW(wzDir, wzPrefix, 0, wzFile)) - { - DirExitWithLastError(hr, "Failed to GetTempFileName."); - } - - hr = ::StringCchCopyW(wzPath, cchPath, wzFile); - -LExit: - return hr; -} - - -/******************************************************************* - DirEnsureExists - -*******************************************************************/ -extern "C" HRESULT DAPI DirEnsureExists( - __in_z LPCWSTR wzPath, - __in_opt LPSECURITY_ATTRIBUTES psa - ) -{ - HRESULT hr = S_OK; - UINT er; - - // try to create this directory - if (!::CreateDirectoryW(wzPath, psa)) - { - // if the directory already exists, bail - er = ::GetLastError(); - if (ERROR_ALREADY_EXISTS == er) - { - ExitFunction1(hr = S_OK); - } - else if (ERROR_PATH_NOT_FOUND != er && DirExists(wzPath, NULL)) // if the directory happens to exist (don't check if CreateDirectory said it doesn't), declare success. - { - ExitFunction1(hr = S_OK); - } - - // get the parent path and try to create it - LPWSTR pwzLastSlash = NULL; - for (LPWSTR pwz = const_cast(wzPath); *pwz; ++pwz) - { - if (*pwz == L'\\') - { - pwzLastSlash = pwz; - } - } - - // if there is no parent directory fail - DirExitOnNullDebugTrace(pwzLastSlash, hr, HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), "cannot find parent path"); - - *pwzLastSlash = L'\0'; // null terminate the parent path - hr = DirEnsureExists(wzPath, psa); // recurse! - *pwzLastSlash = L'\\'; // put the slash back - DirExitOnFailureDebugTrace(hr, "failed to create path: %ls", wzPath); - - // try to create the directory now that all parents are created - if (!::CreateDirectoryW(wzPath, psa)) - { - // if the directory already exists for some reason no error - er = ::GetLastError(); - if (ERROR_ALREADY_EXISTS == er) - { - hr = S_FALSE; - } - else - { - hr = HRESULT_FROM_WIN32(er); - } - } - else - { - hr = S_OK; - } - } - -LExit: - return hr; -} - - -/******************************************************************* - DirEnsureDelete - removes an entire directory structure - -*******************************************************************/ -extern "C" HRESULT DAPI DirEnsureDelete( - __in_z LPCWSTR wzPath, - __in BOOL fDeleteFiles, - __in BOOL fRecurse - ) -{ - HRESULT hr = S_OK; - DWORD dwDeleteFlags = 0; - - dwDeleteFlags |= fDeleteFiles ? DIR_DELETE_FILES : 0; - dwDeleteFlags |= fRecurse ? DIR_DELETE_RECURSE : 0; - - hr = DirEnsureDeleteEx(wzPath, dwDeleteFlags); - return hr; -} - - -/******************************************************************* - DirEnsureDeleteEx - removes an entire directory structure - -*******************************************************************/ -extern "C" HRESULT DAPI DirEnsureDeleteEx( - __in_z LPCWSTR wzPath, - __in DWORD dwFlags - ) -{ - Assert(wzPath && *wzPath); - - HRESULT hr = S_OK; - DWORD er; - - DWORD dwAttrib; - HANDLE hFind = INVALID_HANDLE_VALUE; - LPWSTR sczDelete = NULL; - WIN32_FIND_DATAW wfd; - - BOOL fDeleteFiles = (DIR_DELETE_FILES == (dwFlags & DIR_DELETE_FILES)); - BOOL fRecurse = (DIR_DELETE_RECURSE == (dwFlags & DIR_DELETE_RECURSE)); - BOOL fScheduleDelete = (DIR_DELETE_SCHEDULE == (dwFlags & DIR_DELETE_SCHEDULE)); - WCHAR wzTempDirectory[MAX_PATH] = { }; - WCHAR wzTempPath[MAX_PATH] = { }; - - if (-1 == (dwAttrib = ::GetFileAttributesW(wzPath))) - { - er = ::GetLastError(); - if (ERROR_FILE_NOT_FOUND == er) // change "file not found" to "path not found" since we were looking for a directory. - { - er = ERROR_PATH_NOT_FOUND; - } - hr = HRESULT_FROM_WIN32(er); - DirExitOnRootFailure(hr, "Failed to get attributes for path: %ls", wzPath); - } - - if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) - { - if (dwAttrib & FILE_ATTRIBUTE_READONLY) - { - if (!::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL)) - { - DirExitWithLastError(hr, "Failed to remove read-only attribute from path: %ls", wzPath); - } - } - - // If we're deleting files and/or child directories loop through the contents of the directory. - if (fDeleteFiles || fRecurse) - { - if (fScheduleDelete) - { - if (!::GetTempPathW(countof(wzTempDirectory), wzTempDirectory)) - { - DirExitWithLastError(hr, "Failed to get temp directory."); - } - } - - // Delete everything in this directory. - hr = PathConcat(wzPath, L"*.*", &sczDelete); - DirExitOnFailure(hr, "Failed to concat wild cards to string: %ls", wzPath); - - hFind = ::FindFirstFileW(sczDelete, &wfd); - if (INVALID_HANDLE_VALUE == hFind) - { - DirExitWithLastError(hr, "failed to get first file in directory: %ls", wzPath); - } - - do - { - // Skip the dot directories. - if (L'.' == wfd.cFileName[0] && (L'\0' == wfd.cFileName[1] || (L'.' == wfd.cFileName[1] && L'\0' == wfd.cFileName[2]))) - { - continue; - } - - // For extra safety and to silence OACR. - wfd.cFileName[MAX_PATH - 1] = L'\0'; - - hr = PathConcat(wzPath, wfd.cFileName, &sczDelete); - DirExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath); - - if (fRecurse && wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - hr = PathBackslashTerminate(&sczDelete); - DirExitOnFailure(hr, "Failed to ensure path is backslash terminated: %ls", sczDelete); - - hr = DirEnsureDeleteEx(sczDelete, dwFlags); // recursive call - if (FAILED(hr)) - { - // if we failed to delete a subdirectory, keep trying to finish any remaining files - ExitTraceSource(DUTIL_SOURCE_DIRUTIL, hr, "Failed to delete subdirectory; continuing: %ls", sczDelete); - hr = S_OK; - } - } - else if (fDeleteFiles) // this is a file, just delete it - { - if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY || wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN || wfd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) - { - if (!::SetFileAttributesW(sczDelete, FILE_ATTRIBUTE_NORMAL)) - { - DirExitWithLastError(hr, "Failed to remove attributes from file: %ls", sczDelete); - } - } - - if (!::DeleteFileW(sczDelete)) - { - if (fScheduleDelete) - { - if (!::GetTempFileNameW(wzTempDirectory, L"DEL", 0, wzTempPath)) - { - DirExitWithLastError(hr, "Failed to get temp file to move to."); - } - - // Try to move the file to the temp directory then schedule for delete, - // otherwise just schedule for delete. - if (::MoveFileExW(sczDelete, wzTempPath, MOVEFILE_REPLACE_EXISTING)) - { - ::MoveFileExW(wzTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); - } - else - { - ::MoveFileExW(sczDelete, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); - } - } - else - { - DirExitWithLastError(hr, "Failed to delete file: %ls", sczDelete); - } - } - } - } while (::FindNextFileW(hFind, &wfd)); - - er = ::GetLastError(); - if (ERROR_NO_MORE_FILES == er) - { - hr = S_OK; - } - else - { - DirExitWithLastError(hr, "Failed while looping through files in directory: %ls", wzPath); - } - } - - if (!::RemoveDirectoryW(wzPath)) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr && fScheduleDelete) - { - if (::MoveFileExW(wzPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)) - { - hr = S_OK; - } - } - - DirExitOnRootFailure(hr, "Failed to remove directory: %ls", wzPath); - } - } - else - { - hr = E_UNEXPECTED; - DirExitOnFailure(hr, "Directory delete cannot delete file: %ls", wzPath); - } - - Assert(S_OK == hr); - -LExit: - ReleaseFileFindHandle(hFind); - ReleaseStr(sczDelete); - - return hr; -} - - -/******************************************************************* -DirDeleteEmptyDirectoriesToRoot - removes an empty directory and as many - of its parents as possible. - - Returns: count of directories deleted. -*******************************************************************/ -extern "C" DWORD DAPI DirDeleteEmptyDirectoriesToRoot( - __in_z LPCWSTR wzPath, - __in DWORD /*dwFlags*/ - ) -{ - DWORD cDeletedDirs = 0; - LPWSTR sczPath = NULL; - - while (wzPath && *wzPath && ::RemoveDirectoryW(wzPath)) - { - ++cDeletedDirs; - - HRESULT hr = PathGetParentPath(wzPath, &sczPath); - DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", wzPath); - - wzPath = sczPath; - } - -LExit: - ReleaseStr(sczPath); - - return cDeletedDirs; -} - - -/******************************************************************* - DirGetCurrent - gets the current directory. - -*******************************************************************/ -extern "C" HRESULT DAPI DirGetCurrent( - __deref_out_z LPWSTR* psczCurrentDirectory - ) -{ - HRESULT hr = S_OK; - SIZE_T cch = 0; - - if (psczCurrentDirectory && *psczCurrentDirectory) - { - hr = StrMaxLength(*psczCurrentDirectory, &cch); - DirExitOnFailure(hr, "Failed to determine size of current directory."); - } - - DWORD cchRequired = ::GetCurrentDirectoryW((DWORD)min(DWORD_MAX, cch), 0 == cch ? NULL : *psczCurrentDirectory); - if (0 == cchRequired) - { - DirExitWithLastError(hr, "Failed to get current directory."); - } - else if (cch < cchRequired) - { - hr = StrAlloc(psczCurrentDirectory, cchRequired); - DirExitOnFailure(hr, "Failed to allocate string for current directory."); - - if (!::GetCurrentDirectoryW(cchRequired, *psczCurrentDirectory)) - { - DirExitWithLastError(hr, "Failed to get current directory using allocated string."); - } - } - -LExit: - return hr; -} - - -/******************************************************************* - DirSetCurrent - sets the current directory. - -*******************************************************************/ -extern "C" HRESULT DAPI DirSetCurrent( - __in_z LPCWSTR wzDirectory - ) -{ - HRESULT hr = S_OK; - - if (!::SetCurrentDirectoryW(wzDirectory)) - { - DirExitWithLastError(hr, "Failed to set current directory to: %ls", wzDirectory); - } - -LExit: - return hr; -} diff --git a/src/dutil/dlutil.cpp b/src/dutil/dlutil.cpp deleted file mode 100644 index 70155e6f..00000000 --- a/src/dutil/dlutil.cpp +++ /dev/null @@ -1,802 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -#include -#include - - -// Exit macros -#define DlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) -#define DlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) -#define DlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) -#define DlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) -#define DlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) -#define DlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) -#define DlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DLUTIL, p, x, e, s, __VA_ARGS__) -#define DlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DLUTIL, p, x, s, __VA_ARGS__) -#define DlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DLUTIL, p, x, e, s, __VA_ARGS__) -#define DlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DLUTIL, p, x, s, __VA_ARGS__) -#define DlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DLUTIL, e, x, s, __VA_ARGS__) -#define DlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DLUTIL, g, x, s, __VA_ARGS__) - -static const DWORD64 DOWNLOAD_ENGINE_TWO_GIGABYTES = DWORD64(2) * 1024 * 1024 * 1024; -static LPCWSTR DOWNLOAD_ENGINE_ACCEPT_TYPES[] = { L"*/*", NULL }; - -// internal function declarations - -static HRESULT InitializeResume( - __in LPCWSTR wzDestinationPath, - __out LPWSTR* psczResumePath, - __out HANDLE* phResumeFile, - __out DWORD64* pdw64ResumeOffset - ); -static HRESULT GetResourceMetadata( - __in HINTERNET hSession, - __inout_z LPWSTR* psczUrl, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out DWORD64* pdw64ResourceSize, - __out FILETIME* pftResourceCreated - ); -static HRESULT DownloadResource( - __in HINTERNET hSession, - __inout_z LPWSTR* psczUrl, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword, - __in_z LPCWSTR wzDestinationPath, - __in DWORD64 dw64AuthoredResourceLength, - __in DWORD64 dw64ResourceLength, - __in DWORD64 dw64ResumeOffset, - __in HANDLE hResumeFile, - __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate - ); -static HRESULT AllocateRangeRequestHeader( - __in DWORD64 dw64ResumeOffset, - __in DWORD64 dw64ResourceLength, - __deref_inout_z LPWSTR* psczHeader - ); -static HRESULT WriteToFile( - __in HINTERNET hUrl, - __in HANDLE hPayloadFile, - __inout DWORD64* pdw64ResumeOffset, - __in HANDLE hResumeFile, - __in DWORD64 dw64ResourceLength, - __in LPBYTE pbData, - __in DWORD cbData, - __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback - ); -static HRESULT UpdateResumeOffset( - __inout DWORD64* pdw64ResumeOffset, - __in HANDLE hResumeFile, - __in DWORD cbData - ); -static HRESULT MakeRequest( - __in HINTERNET hSession, - __inout_z LPWSTR* psczSourceUrl, - __in_z_opt LPCWSTR wzMethod, - __in_z_opt LPCWSTR wzHeaders, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out HINTERNET* phConnect, - __out HINTERNET* phUrl, - __out BOOL* pfRangeRequestsAccepted - ); -static HRESULT OpenRequest( - __in HINTERNET hConnect, - __in_z_opt LPCWSTR wzMethod, - __in INTERNET_SCHEME scheme, - __in_z LPCWSTR wzResource, - __in_z_opt LPCWSTR wzQueryString, - __in_z_opt LPCWSTR wzHeader, - __out HINTERNET* phUrl - ); -static HRESULT SendRequest( - __in HINTERNET hUrl, - __inout_z LPWSTR* psczUrl, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out BOOL* pfRetry, - __out BOOL* pfRangesAccepted - ); -static HRESULT AuthenticationRequired( - __in HINTERNET hUrl, - __in long lHttpCode, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out BOOL* pfRetrySend, - __out BOOL* pfRetry - ); -static HRESULT DownloadGetResumePath( - __in_z LPCWSTR wzPayloadWorkingPath, - __deref_out_z LPWSTR* psczResumePath - ); -static HRESULT DownloadSendProgressCallback( - __in DOWNLOAD_CACHE_CALLBACK* pCallback, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in HANDLE hDestinationFile - ); -// function definitions - -extern "C" HRESULT DAPI DownloadUrl( - __in DOWNLOAD_SOURCE* pDownloadSource, - __in DWORD64 dw64AuthoredDownloadSize, - __in LPCWSTR wzDestinationPath, - __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate - ) -{ - HRESULT hr = S_OK; - LPWSTR sczUrl = NULL; - HINTERNET hSession = NULL; - DWORD dwTimeout = 0; - LPWSTR sczResumePath = NULL; - HANDLE hResumeFile = INVALID_HANDLE_VALUE; - DWORD64 dw64ResumeOffset = 0; - DWORD64 dw64Size = 0; - FILETIME ftCreated = { }; - - // Copy the download source into a working variable to handle redirects then - // open the internet session. - hr = StrAllocString(&sczUrl, pDownloadSource->sczUrl, 0); - DlExitOnFailure(hr, "Failed to copy download source URL."); - - hSession = ::InternetOpenW(L"Burn", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); - DlExitOnNullWithLastError(hSession, hr, "Failed to open internet session"); - - // Make a best effort to set the download timeouts to 2 minutes or whatever policy says. - PolcReadNumber(POLICY_BURN_REGISTRY_PATH, L"DownloadTimeout", 2 * 60, &dwTimeout); - if (0 < dwTimeout) - { - dwTimeout *= 1000; // convert to milliseconds. - ::InternetSetOptionW(hSession, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); - ::InternetSetOptionW(hSession, INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); - ::InternetSetOptionW(hSession, INTERNET_OPTION_SEND_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); - } - - // Get the resource size and creation time from the internet. - hr = GetResourceMetadata(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, pAuthenticate, &dw64Size, &ftCreated); - DlExitOnFailure(hr, "Failed to get size and time for URL: %ls", sczUrl); - - // Ignore failure to initialize resume because we will fall back to full download then - // download. - InitializeResume(wzDestinationPath, &sczResumePath, &hResumeFile, &dw64ResumeOffset); - - hr = DownloadResource(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, wzDestinationPath, dw64AuthoredDownloadSize, dw64Size, dw64ResumeOffset, hResumeFile, pCache, pAuthenticate); - DlExitOnFailure(hr, "Failed to download URL: %ls", sczUrl); - - // Cleanup the resume file because we successfully downloaded the whole file. - if (sczResumePath && *sczResumePath) - { - ::DeleteFileW(sczResumePath); - } - -LExit: - ReleaseFileHandle(hResumeFile); - ReleaseStr(sczResumePath); - ReleaseInternet(hSession); - ReleaseStr(sczUrl); - - return hr; -} - - -// internal helper functions - -static HRESULT InitializeResume( - __in LPCWSTR wzDestinationPath, - __out LPWSTR* psczResumePath, - __out HANDLE* phResumeFile, - __out DWORD64* pdw64ResumeOffset - ) -{ - HRESULT hr = S_OK; - HANDLE hResumeFile = INVALID_HANDLE_VALUE; - DWORD cbTotalReadResumeData = 0; - DWORD cbReadData = 0; - - *pdw64ResumeOffset = 0; - - hr = DownloadGetResumePath(wzDestinationPath, psczResumePath); - DlExitOnFailure(hr, "Failed to calculate resume path from working path: %ls", wzDestinationPath); - - hResumeFile = ::CreateFileW(*psczResumePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hResumeFile) - { - DlExitWithLastError(hr, "Failed to create resume file: %ls", *psczResumePath); - } - - do - { - if (!::ReadFile(hResumeFile, reinterpret_cast(pdw64ResumeOffset) + cbTotalReadResumeData, sizeof(DWORD64) - cbTotalReadResumeData, &cbReadData, NULL)) - { - DlExitWithLastError(hr, "Failed to read resume file: %ls", *psczResumePath); - } - cbTotalReadResumeData += cbReadData; - } while (cbReadData && sizeof(DWORD64) > cbTotalReadResumeData); - - // Start over if we couldn't get a resume offset. - if (cbTotalReadResumeData != sizeof(DWORD64)) - { - *pdw64ResumeOffset = 0; - } - - *phResumeFile = hResumeFile; - hResumeFile = INVALID_HANDLE_VALUE; - -LExit: - ReleaseFileHandle(hResumeFile); - return hr; -} - -static HRESULT GetResourceMetadata( - __in HINTERNET hSession, - __inout_z LPWSTR* psczUrl, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out DWORD64* pdw64ResourceSize, - __out FILETIME* pftResourceCreated - ) -{ - HRESULT hr = S_OK; - BOOL fRangeRequestsAccepted = TRUE; - HINTERNET hConnect = NULL; - HINTERNET hUrl = NULL; - LONGLONG llLength = 0; - - hr = MakeRequest(hSession, psczUrl, L"HEAD", NULL, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted); - DlExitOnFailure(hr, "Failed to connect to URL: %ls", *psczUrl); - - hr = InternetGetSizeByHandle(hUrl, &llLength); - if (FAILED(hr)) - { - llLength = 0; - hr = S_OK; - } - - *pdw64ResourceSize = llLength; - - // Get the last modified time from the server, we'll use that as our downloaded time here. If - // the server time isn't available then use the local system time. - hr = InternetGetCreateTimeByHandle(hUrl, pftResourceCreated); - if (FAILED(hr)) - { - ::GetSystemTimeAsFileTime(pftResourceCreated); - hr = S_OK; - } - -LExit: - ReleaseInternet(hUrl); - ReleaseInternet(hConnect); - return hr; -} - -static HRESULT DownloadResource( - __in HINTERNET hSession, - __inout_z LPWSTR* psczUrl, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword, - __in_z LPCWSTR wzDestinationPath, - __in DWORD64 dw64AuthoredResourceLength, - __in DWORD64 dw64ResourceLength, - __in DWORD64 dw64ResumeOffset, - __in HANDLE hResumeFile, - __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate - ) -{ - HRESULT hr = S_OK; - HANDLE hPayloadFile = INVALID_HANDLE_VALUE; - DWORD cbMaxData = 64 * 1024; // 64 KB - BYTE* pbData = NULL; - BOOL fRangeRequestsAccepted = TRUE; - LPWSTR sczRangeRequestHeader = NULL; - HINTERNET hConnect = NULL; - HINTERNET hUrl = NULL; - LONGLONG llLength = 0; - - hPayloadFile = ::CreateFileW(wzDestinationPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hPayloadFile) - { - DlExitWithLastError(hr, "Failed to create download destination file: %ls", wzDestinationPath); - } - - // Allocate a memory block on a page boundary in case we want to do optimal writing. - pbData = static_cast(::VirtualAlloc(NULL, cbMaxData, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); - DlExitOnNullWithLastError(pbData, hr, "Failed to allocate buffer to download files into."); - - // Let's try downloading the file assuming that range requests are accepted. If range requests - // are not supported we'll have to start over and accept the fact that we only get one shot - // downloading the file however big it is. Hopefully, not more than 2 GB since wininet doesn't - // like files that big. - while (fRangeRequestsAccepted && (0 == dw64ResourceLength || dw64ResumeOffset < dw64ResourceLength)) - { - hr = AllocateRangeRequestHeader(dw64ResumeOffset, 0 == dw64ResourceLength ? dw64AuthoredResourceLength : dw64ResourceLength, &sczRangeRequestHeader); - DlExitOnFailure(hr, "Failed to allocate range request header."); - - ReleaseNullInternet(hConnect); - ReleaseNullInternet(hUrl); - - hr = MakeRequest(hSession, psczUrl, L"GET", sczRangeRequestHeader, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted); - DlExitOnFailure(hr, "Failed to request URL for download: %ls", *psczUrl); - - // If we didn't get the size of the resource from the initial "HEAD" request - // then let's try to get the size from this "GET" request. - if (0 == dw64ResourceLength) - { - hr = InternetGetSizeByHandle(hUrl, &llLength); - if (SUCCEEDED(hr)) - { - dw64ResourceLength = llLength; - } - else // server didn't tell us the resource length. - { - // Fallback to the authored size of the resource. However, since we - // don't really know the size on the server, don't try to use - // range requests either. - dw64ResourceLength = dw64AuthoredResourceLength; - fRangeRequestsAccepted = FALSE; - } - } - - // If we just tried to do a range request and found out that it isn't supported, start over. - if (!fRangeRequestsAccepted) - { - // TODO: log a message that the server did not accept range requests. - dw64ResumeOffset = 0; - } - - hr = WriteToFile(hUrl, hPayloadFile, &dw64ResumeOffset, hResumeFile, dw64ResourceLength, pbData, cbMaxData, pCache); - DlExitOnFailure(hr, "Failed while reading from internet and writing to: %ls", wzDestinationPath); - } - -LExit: - ReleaseInternet(hUrl); - ReleaseInternet(hConnect); - ReleaseStr(sczRangeRequestHeader); - if (pbData) - { - ::VirtualFree(pbData, 0, MEM_RELEASE); - } - ReleaseFileHandle(hPayloadFile); - - return hr; -} - -static HRESULT AllocateRangeRequestHeader( - __in DWORD64 dw64ResumeOffset, - __in DWORD64 dw64ResourceLength, - __deref_inout_z LPWSTR* psczHeader - ) -{ - HRESULT hr = S_OK; - - // If the remaining length is less that 2GB we'll be able to ask for everything. - DWORD64 dw64RemainingLength = dw64ResourceLength - dw64ResumeOffset; - if (DOWNLOAD_ENGINE_TWO_GIGABYTES > dw64RemainingLength) - { - // If we have a resume offset, let's download everything from there. Otherwise, we'll - // just get everything with no headers in the way. - if (0 < dw64ResumeOffset) - { - hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-", dw64ResumeOffset); - DlExitOnFailure(hr, "Failed to add range read header."); - } - else - { - ReleaseNullStr(*psczHeader); - } - } - else // we'll have to download in chunks. - { - hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-%I64u", dw64ResumeOffset, dw64ResumeOffset + dw64RemainingLength - 1); - DlExitOnFailure(hr, "Failed to add range read header."); - } - -LExit: - return hr; -} - -static HRESULT WriteToFile( - __in HINTERNET hUrl, - __in HANDLE hPayloadFile, - __inout DWORD64* pdw64ResumeOffset, - __in HANDLE hResumeFile, - __in DWORD64 dw64ResourceLength, - __in LPBYTE pbData, - __in DWORD cbData, - __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback - ) -{ - HRESULT hr = S_OK; - DWORD cbReadData = 0; - - hr = FileSetPointer(hPayloadFile, *pdw64ResumeOffset, NULL, FILE_BEGIN); - DlExitOnFailure(hr, "Failed to seek to start point in file."); - - do - { - // Read bits from the internet. - if (!::InternetReadFile(hUrl, static_cast(pbData), cbData, &cbReadData)) - { - DlExitWithLastError(hr, "Failed while reading from internet."); - } - - // Write bits to disk (if there are any). - if (cbReadData) - { - DWORD cbTotalWritten = 0; - DWORD cbWritten = 0; - do - { - if (!::WriteFile(hPayloadFile, pbData + cbTotalWritten, cbReadData - cbTotalWritten, &cbWritten, NULL)) - { - DlExitWithLastError(hr, "Failed to write data from internet."); - } - - cbTotalWritten += cbWritten; - } while (cbWritten && cbTotalWritten < cbReadData); - - // Ignore failure from updating resume file as this doesn't mean the download cannot succeed. - UpdateResumeOffset(pdw64ResumeOffset, hResumeFile, cbTotalWritten); - - if (pCallback && pCallback->pfnProgress) - { - hr = DownloadSendProgressCallback(pCallback, *pdw64ResumeOffset, dw64ResourceLength, hPayloadFile); - DlExitOnFailure(hr, "UX aborted on cache progress."); - } - } - } while (cbReadData); - -LExit: - return hr; -} - -static HRESULT UpdateResumeOffset( - __inout DWORD64* pdw64ResumeOffset, - __in HANDLE hResumeFile, - __in DWORD cbData - ) -{ - HRESULT hr = S_OK; - - *pdw64ResumeOffset += cbData; - - if (INVALID_HANDLE_VALUE != hResumeFile) - { - DWORD cbTotalWrittenResumeData = 0; - DWORD cbWrittenResumeData = 0; - - hr = FileSetPointer(hResumeFile, 0, NULL, FILE_BEGIN); - DlExitOnFailure(hr, "Failed to seek to start point in file."); - - do - { - // Ignore failure to write to the resume file as that should not prevent the download from happening. - if (!::WriteFile(hResumeFile, pdw64ResumeOffset + cbTotalWrittenResumeData, sizeof(DWORD64) - cbTotalWrittenResumeData, &cbWrittenResumeData, NULL)) - { - DlExitOnFailure(hr, "Failed to seek to write to file."); - } - - cbTotalWrittenResumeData += cbWrittenResumeData; - } while (cbWrittenResumeData && sizeof(DWORD64) > cbTotalWrittenResumeData); - } - -LExit: - return hr; -} - -static HRESULT MakeRequest( - __in HINTERNET hSession, - __inout_z LPWSTR* psczSourceUrl, - __in_z_opt LPCWSTR wzMethod, - __in_z_opt LPCWSTR wzHeaders, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out HINTERNET* phConnect, - __out HINTERNET* phUrl, - __out BOOL* pfRangeRequestsAccepted - ) -{ - HRESULT hr = S_OK; - HINTERNET hConnect = NULL; - HINTERNET hUrl = NULL; - URI_INFO uri = { }; - - // Try to open the URL. - BOOL fRetry; - do - { - fRetry = FALSE; - - // If the URL was opened close it, so we can reopen it again. - ReleaseInternet(hUrl); - ReleaseInternet(hConnect); - - // Open the url. - hr = UriCrackEx(*psczSourceUrl, &uri); - DlExitOnFailure(hr, "Failed to break URL into server and resource parts."); - - hConnect = ::InternetConnectW(hSession, uri.sczHostName, uri.port, (wzUser && *wzUser) ? wzUser : uri.sczUser, (wzPassword && *wzPassword) ? wzPassword : uri.sczPassword, INTERNET_SCHEME_FTP == uri.scheme ? INTERNET_SERVICE_FTP : INTERNET_SERVICE_HTTP, 0, 0); - DlExitOnNullWithLastError(hConnect, hr, "Failed to connect to URL: %ls", *psczSourceUrl); - - // Best effort set the proxy username and password, if they were provided. - if ((wzUser && *wzUser) && (wzPassword && *wzPassword)) - { - if (::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_USERNAME, (LPVOID)wzUser, lstrlenW(wzUser))) - { - ::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_PASSWORD, (LPVOID)wzPassword, lstrlenW(wzPassword)); - } - } - - hr = OpenRequest(hConnect, wzMethod, uri.scheme, uri.sczPath, uri.sczQueryString, wzHeaders, &hUrl); - DlExitOnFailure(hr, "Failed to open internet URL: %ls", *psczSourceUrl); - - hr = SendRequest(hUrl, psczSourceUrl, pAuthenticate, &fRetry, pfRangeRequestsAccepted); - DlExitOnFailure(hr, "Failed to send request to URL: %ls", *psczSourceUrl); - } while (fRetry); - - // Okay, we're all ready to start downloading. Update the connection information. - *phConnect = hConnect; - hConnect = NULL; - *phUrl = hUrl; - hUrl = NULL; - -LExit: - UriInfoUninitialize(&uri); - ReleaseInternet(hUrl); - ReleaseInternet(hConnect); - - return hr; -} - -static HRESULT OpenRequest( - __in HINTERNET hConnect, - __in_z_opt LPCWSTR wzMethod, - __in INTERNET_SCHEME scheme, - __in_z LPCWSTR wzResource, - __in_z_opt LPCWSTR wzQueryString, - __in_z_opt LPCWSTR wzHeader, - __out HINTERNET* phUrl - ) -{ - HRESULT hr = S_OK; - DWORD dwRequestFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD; - LPWSTR sczResource = NULL; - HINTERNET hUrl = NULL; - - if (INTERNET_SCHEME_HTTPS == scheme) - { - dwRequestFlags |= INTERNET_FLAG_SECURE; - } - else if (INTERNET_SCHEME_HTTP == scheme) - { - dwRequestFlags |= INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS; - } - - // Allocate the resource name. - hr = StrAllocString(&sczResource, wzResource, 0); - DlExitOnFailure(hr, "Failed to allocate string for resource URI."); - - if (wzQueryString && *wzQueryString) - { - hr = StrAllocConcat(&sczResource, wzQueryString, 0); - DlExitOnFailure(hr, "Failed to append query strong to resource from URI."); - } - - // Open the request and add the header if provided. - hUrl = ::HttpOpenRequestW(hConnect, wzMethod, sczResource, NULL, NULL, DOWNLOAD_ENGINE_ACCEPT_TYPES, dwRequestFlags, NULL); - DlExitOnNullWithLastError(hUrl, hr, "Failed to open internet request."); - - if (wzHeader && *wzHeader) - { - if (!::HttpAddRequestHeadersW(hUrl, wzHeader, static_cast(-1), HTTP_ADDREQ_FLAG_COALESCE)) - { - DlExitWithLastError(hr, "Failed to add header to HTTP request."); - } - } - - *phUrl = hUrl; - hUrl = NULL; - -LExit: - ReleaseInternet(hUrl); - ReleaseStr(sczResource); - return hr; -} - -static HRESULT SendRequest( - __in HINTERNET hUrl, - __inout_z LPWSTR* psczUrl, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out BOOL* pfRetry, - __out BOOL* pfRangesAccepted - ) -{ - HRESULT hr = S_OK; - BOOL fRetrySend = FALSE; - LONG lCode = 0; - - do - { - fRetrySend = FALSE; - - if (!::HttpSendRequestW(hUrl, NULL, 0, NULL, 0)) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); // remember the error that occurred and log it. - LogErrorString(hr, "Failed to send request to URL: %ls, trying to process HTTP status code anyway.", *psczUrl); - - // Try to get the HTTP status code and, if good, handle via the switch statement below but if it - // fails return the error code from the send request above as the result of the function. - HRESULT hrQueryStatusCode = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); - DlExitOnFailure(hrQueryStatusCode, "Failed to get HTTP status code for failed request to URL: %ls", *psczUrl); - } - else // get the http status code. - { - hr = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); - DlExitOnFailure(hr, "Failed to get HTTP status code for request to URL: %ls", *psczUrl); - } - - switch (lCode) - { - case 200: // OK but range requests don't work. - *pfRangesAccepted = FALSE; - hr = S_OK; - break; - - case 206: // Partial content means that range requests work! - *pfRangesAccepted = TRUE; - hr = S_OK; - break; - - // redirection cases - case 301: __fallthrough; // file moved - case 302: __fallthrough; // temporary - case 303: // redirect method - hr = InternetQueryInfoString(hUrl, HTTP_QUERY_CONTENT_LOCATION, psczUrl); - DlExitOnFailure(hr, "Failed to get redirect url: %ls", *psczUrl); - - *pfRetry = TRUE; - break; - - // error cases - case 400: // bad request - hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME); - break; - - case 401: __fallthrough; // unauthorized - case 407: __fallthrough; // proxy unauthorized - hr = AuthenticationRequired(hUrl, lCode, pAuthenticate, &fRetrySend, pfRetry); - break; - - case 403: // forbidden - hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); - break; - - case 404: // file not found - case 410: // gone - hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - break; - - case 405: // method not allowed - hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); - break; - - case 408: __fallthrough; // request timedout - case 504: // gateway timeout - hr = HRESULT_FROM_WIN32(WAIT_TIMEOUT); - break; - - case 414: // request URI too long - hr = CO_E_PATHTOOLONG; - break; - - case 502: __fallthrough; // server (through a gateway) was not found - case 503: // server unavailable - hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); - break; - - case 418: // I'm a teapot. - default: - // If the request failed and the HTTP status code was invalid (but wininet gave us a number anyway) - // do not overwrite the error code from the failed request. Otherwise, the error was unexpected. - if (SUCCEEDED(hr)) - { - hr = E_UNEXPECTED; - } - - LogErrorString(hr, "Unknown HTTP status code %d, returned from URL: %ls", lCode, *psczUrl); - break; - } - } while (fRetrySend); - -LExit: - return hr; -} - -static HRESULT AuthenticationRequired( - __in HINTERNET hUrl, - __in long lHttpCode, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, - __out BOOL* pfRetrySend, - __out BOOL* pfRetry - ) -{ - Assert(401 == lHttpCode || 407 == lHttpCode); - - HRESULT hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); - *pfRetrySend = FALSE; - *pfRetry = FALSE; - - if (pAuthenticate && pAuthenticate->pfnAuthenticate) - { - hr = (*pAuthenticate->pfnAuthenticate)(pAuthenticate->pv, hUrl, lHttpCode, pfRetrySend, pfRetry); - } - - return hr; -} - - -static HRESULT DownloadGetResumePath( - __in_z LPCWSTR wzPayloadWorkingPath, - __deref_out_z LPWSTR* psczResumePath - ) -{ - HRESULT hr = S_OK; - - hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath); - DlExitOnFailure(hr, "Failed to create resume path."); - -LExit: - return hr; -} - -static HRESULT DownloadSendProgressCallback( - __in DOWNLOAD_CACHE_CALLBACK* pCallback, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in HANDLE hDestinationFile - ) -{ - static LARGE_INTEGER LARGE_INTEGER_ZERO = { }; - - HRESULT hr = S_OK; - DWORD dwResult = PROGRESS_CONTINUE; - LARGE_INTEGER liTotalSize = { }; - LARGE_INTEGER liTotalTransferred = { }; - - if (pCallback->pfnProgress) - { - liTotalSize.QuadPart = dw64Total; - liTotalTransferred.QuadPart = dw64Progress; - - dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv); - switch (dwResult) - { - case PROGRESS_CONTINUE: - hr = S_OK; - break; - - case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently? - case PROGRESS_STOP: - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - DlExitOnRootFailure(hr, "UX aborted on download progress."); - - case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress. - pCallback->pfnProgress = NULL; - hr = S_OK; - break; - - default: - hr = E_UNEXPECTED; - DlExitOnRootFailure(hr, "Invalid return code from progress routine."); - } - } - -LExit: - return hr; -} diff --git a/src/dutil/dpiutil.cpp b/src/dutil/dpiutil.cpp deleted file mode 100644 index 4096c8d3..00000000 --- a/src/dutil/dpiutil.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define DpiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) -#define DpiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) -#define DpiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) -#define DpiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) -#define DpiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) -#define DpiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) -#define DpiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__) -#define DpiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__) -#define DpiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__) -#define DpiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__) -#define DpiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DPIUTIL, e, x, s, __VA_ARGS__) - -static PFN_ADJUSTWINDOWRECTEXFORDPI vpfnAdjustWindowRectExForDpi = NULL; -static PFN_GETDPIFORMONITOR vpfnGetDpiForMonitor = NULL; -static PFN_GETDPIFORWINDOW vpfnGetDpiForWindow = NULL; -static PFN_SETPROCESSDPIAWARE vpfnSetProcessDPIAware = NULL; -static PFN_SETPROCESSDPIAWARENESS vpfnSetProcessDpiAwareness = NULL; -static PFN_SETPROCESSDPIAWARENESSCONTEXT vpfnSetProcessDpiAwarenessContext = NULL; - -static HMODULE vhShcoreDll = NULL; -static HMODULE vhUser32Dll = NULL; -static BOOL vfDpiuInitialized = FALSE; - -DAPI_(void) DpiuInitialize() -{ - HRESULT hr = S_OK; - - hr = LoadSystemLibrary(L"Shcore.dll", &vhShcoreDll); - if (SUCCEEDED(hr)) - { - // Ignore failures. - vpfnGetDpiForMonitor = reinterpret_cast(::GetProcAddress(vhShcoreDll, "GetDpiForMonitor")); - vpfnSetProcessDpiAwareness = reinterpret_cast(::GetProcAddress(vhShcoreDll, "SetProcessDpiAwareness")); - } - - hr = LoadSystemLibrary(L"User32.dll", &vhUser32Dll); - if (SUCCEEDED(hr)) - { - // Ignore failures. - vpfnAdjustWindowRectExForDpi = reinterpret_cast(::GetProcAddress(vhUser32Dll, "AdjustWindowRectExForDpi")); - vpfnGetDpiForWindow = reinterpret_cast(::GetProcAddress(vhUser32Dll, "GetDpiForWindow")); - vpfnSetProcessDPIAware = reinterpret_cast(::GetProcAddress(vhUser32Dll, "SetProcessDPIAware")); - vpfnSetProcessDpiAwarenessContext = reinterpret_cast(::GetProcAddress(vhUser32Dll, "SetProcessDpiAwarenessContext")); - } - - vfDpiuInitialized = TRUE; -} - -DAPI_(void) DpiuUninitialize() -{ - if (vhShcoreDll) - { - ::FreeLibrary(vhShcoreDll); - } - - if (vhUser32Dll) - { - ::FreeLibrary(vhUser32Dll); - } - - vhShcoreDll = NULL; - vhUser32Dll = NULL; - vpfnAdjustWindowRectExForDpi = NULL; - vpfnGetDpiForMonitor = NULL; - vpfnGetDpiForWindow = NULL; - vfDpiuInitialized = FALSE; -} - -DAPI_(void) DpiuAdjustWindowRect( - __in RECT* pWindowRect, - __in DWORD dwStyle, - __in BOOL fMenu, - __in DWORD dwExStyle, - __in UINT nDpi - ) -{ - if (WS_SYSMENU & dwStyle) - { - dwStyle |= WS_CAPTION; // WS_CAPTION is required with WS_SYSMENU, AdjustWindowRect* won't work properly when it's not specified. - } - - if (vpfnAdjustWindowRectExForDpi) - { - vpfnAdjustWindowRectExForDpi(pWindowRect, dwStyle, fMenu, dwExStyle, nDpi); - } - else - { - ::AdjustWindowRectEx(pWindowRect, dwStyle, fMenu, dwExStyle); - } -} - -DAPI_(HRESULT) DpiuGetMonitorContextFromPoint( - __in const POINT* pt, - __out DPIU_MONITOR_CONTEXT** ppMonitorContext - ) -{ - HRESULT hr = S_OK; - DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; - HMONITOR hMonitor = NULL; - UINT dpiX = 0; - UINT dpiY = 0; - HDC hdc = NULL; - - pMonitorContext = reinterpret_cast(MemAlloc(sizeof(DPIU_MONITOR_CONTEXT), TRUE)); - DpiuExitOnNull(pMonitorContext, hr, E_OUTOFMEMORY, "Failed to allocate memory for DpiuMonitorContext."); - - hMonitor = ::MonitorFromPoint(*pt, MONITOR_DEFAULTTONEAREST); - DpiuExitOnNull(hMonitor, hr, E_FAIL, "Failed to get monitor from point."); - - pMonitorContext->mi.cbSize = sizeof(pMonitorContext->mi); - if (!::GetMonitorInfoW(hMonitor, &pMonitorContext->mi)) - { - DpiuExitOnFailure(hr = E_OUTOFMEMORY, "Failed to get monitor info for point."); - } - - if (vpfnGetDpiForMonitor) - { - hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); - DpiuExitOnFailure(hr, "Failed to get DPI for monitor."); - - pMonitorContext->nDpi = dpiX; - } - else - { - hdc = ::CreateDCW(L"DISPLAY", pMonitorContext->mi.szDevice, NULL, NULL); - DpiuExitOnNull(hdc, hr, E_OUTOFMEMORY, "Failed to get device context for monitor."); - - pMonitorContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX); - } - - *ppMonitorContext = pMonitorContext; - pMonitorContext = NULL; - -LExit: - if (hdc) - { - ::ReleaseDC(NULL, hdc); - } - - MemFree(pMonitorContext); - - return hr; -} - -DAPI_(void) DpiuGetWindowContext( - __in HWND hWnd, - __in DPIU_WINDOW_CONTEXT* pWindowContext - ) -{ - HRESULT hr = S_OK; - HMONITOR hMonitor = NULL; - UINT dpiX = 0; - UINT dpiY = 0; - HDC hdc = NULL; - - pWindowContext->nDpi = USER_DEFAULT_SCREEN_DPI; - - if (vpfnGetDpiForWindow) - { - pWindowContext->nDpi = vpfnGetDpiForWindow(hWnd); - ExitFunction(); - } - - if (vpfnGetDpiForMonitor) - { - hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); - if (hMonitor) - { - hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); - if (SUCCEEDED(hr)) - { - pWindowContext->nDpi = dpiX; - ExitFunction(); - } - } - } - - hdc = ::GetDC(hWnd); - if (hdc) - { - pWindowContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX); - } - -LExit: - if (hdc) - { - ::ReleaseDC(hWnd, hdc); - } -} - -DAPI_(int) DpiuScaleValue( - __in int nDefaultDpiValue, - __in UINT nTargetDpi - ) -{ - return ::MulDiv(nDefaultDpiValue, nTargetDpi, USER_DEFAULT_SCREEN_DPI); -} - -DAPI_(HRESULT) DpiuSetProcessDpiAwareness( - __in DPIU_AWARENESS supportedAwareness, - __in_opt DPIU_AWARENESS* pSelectedAwareness - ) -{ - HRESULT hr = S_OK; - DPIU_AWARENESS selectedAwareness = DPIU_AWARENESS_NONE; - DPI_AWARENESS_CONTEXT awarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE; - PROCESS_DPI_AWARENESS awareness = PROCESS_DPI_UNAWARE; - - if (vpfnSetProcessDpiAwarenessContext) - { - if (DPIU_AWARENESS_PERMONITORV2 & supportedAwareness) - { - awarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; - selectedAwareness = DPIU_AWARENESS_PERMONITORV2; - } - else if (DPIU_AWARENESS_PERMONITOR & supportedAwareness) - { - awarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE; - selectedAwareness = DPIU_AWARENESS_PERMONITOR; - } - else if (DPIU_AWARENESS_SYSTEM & supportedAwareness) - { - awarenessContext = DPI_AWARENESS_CONTEXT_SYSTEM_AWARE; - selectedAwareness = DPIU_AWARENESS_SYSTEM; - } - else if (DPIU_AWARENESS_GDISCALED & supportedAwareness) - { - awarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED; - selectedAwareness = DPIU_AWARENESS_GDISCALED; - } - - if (!vpfnSetProcessDpiAwarenessContext(awarenessContext)) - { - DpiuExitOnLastError(hr, "Failed to set process DPI awareness context."); - } - } - else if (vpfnSetProcessDpiAwareness) - { - if (DPIU_AWARENESS_PERMONITOR & supportedAwareness) - { - awareness = PROCESS_PER_MONITOR_DPI_AWARE; - selectedAwareness = DPIU_AWARENESS_PERMONITOR; - } - else if (DPIU_AWARENESS_SYSTEM & supportedAwareness) - { - awareness = PROCESS_SYSTEM_DPI_AWARE; - selectedAwareness = DPIU_AWARENESS_SYSTEM; - } - - hr = vpfnSetProcessDpiAwareness(awareness); - DpiuExitOnFailure(hr, "Failed to set process DPI awareness."); - } - else if (vpfnSetProcessDPIAware && (DPIU_AWARENESS_SYSTEM & supportedAwareness)) - { - selectedAwareness = DPIU_AWARENESS_SYSTEM; - if (!vpfnSetProcessDPIAware()) - { - DpiuExitOnLastError(hr, "Failed to set process DPI aware."); - } - } - -LExit: - if (pSelectedAwareness) - { - *pSelectedAwareness = selectedAwareness; - } - - return hr; -} diff --git a/src/dutil/dutil.cpp b/src/dutil/dutil.cpp deleted file mode 100644 index 56b85207..00000000 --- a/src/dutil/dutil.cpp +++ /dev/null @@ -1,524 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define DExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) -#define DExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) -#define DExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) -#define DExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) -#define DExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) -#define DExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) -#define DExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__) -#define DExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__) -#define DExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__) -#define DExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__) -#define DExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DUTIL, e, x, s, __VA_ARGS__) -#define DExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DUTIL, g, x, s, __VA_ARGS__) - -// No need for OACR to warn us about using non-unicode APIs in this file. -#pragma prefast(disable:25068) - -// Asserts & Tracing - -const int DUTIL_STRING_BUFFER = 1024; -static HMODULE Dutil_hAssertModule = NULL; -static DUTIL_ASSERTDISPLAYFUNCTION Dutil_pfnDisplayAssert = NULL; -static BOOL Dutil_fNoAsserts = FALSE; -static REPORT_LEVEL Dutil_rlCurrentTrace = REPORT_STANDARD; -static BOOL Dutil_fTraceFilenames = FALSE; -static DUTIL_CALLBACK_TRACEERROR vpfnTraceErrorCallback = NULL; - - -DAPI_(HRESULT) DutilInitialize( - __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback - ) -{ - HRESULT hr = S_OK; - - vpfnTraceErrorCallback = pfnTraceErrorCallback; - - return hr; -} - - -DAPI_(void) DutilUninitialize() -{ - vpfnTraceErrorCallback = NULL; -} - -/******************************************************************* -Dutil_SetAssertModule - -*******************************************************************/ -extern "C" void DAPI Dutil_SetAssertModule( - __in HMODULE hAssertModule - ) -{ - Dutil_hAssertModule = hAssertModule; -} - - -/******************************************************************* -Dutil_SetAssertDisplayFunction - -*******************************************************************/ -extern "C" void DAPI Dutil_SetAssertDisplayFunction( - __in DUTIL_ASSERTDISPLAYFUNCTION pfn - ) -{ - Dutil_pfnDisplayAssert = pfn; -} - - -/******************************************************************* -Dutil_AssertMsg - -*******************************************************************/ -extern "C" void DAPI Dutil_AssertMsg( - __in_z LPCSTR szMessage - ) -{ - static BOOL fInAssert = FALSE; // TODO: make this thread safe (this is a cheap hack to prevent re-entrant Asserts) - - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - int id = IDRETRY; - HKEY hkDebug = NULL; - HANDLE hAssertFile = INVALID_HANDLE_VALUE; - char szPath[MAX_PATH] = { }; - DWORD cch = 0; - - if (fInAssert) - { - return; - } - fInAssert = TRUE; - - char szMsg[DUTIL_STRING_BUFFER]; - hr = ::StringCchCopyA(szMsg, countof(szMsg), szMessage); - DExitOnFailure(hr, "failed to copy message while building assert message"); - - if (Dutil_pfnDisplayAssert) - { - // call custom function to display the assert string - if (!Dutil_pfnDisplayAssert(szMsg)) - { - ExitFunction(); - } - } - else - { - OutputDebugStringA(szMsg); - } - - if (!Dutil_fNoAsserts) - { - er = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Delivery\\Debug", 0, KEY_QUERY_VALUE, &hkDebug); - if (ERROR_SUCCESS == er) - { - cch = countof(szPath); - er = ::RegQueryValueExA(hkDebug, "DeliveryAssertsLog", NULL, NULL, reinterpret_cast(szPath), &cch); - szPath[countof(szPath) - 1] = '\0'; // ensure string is null terminated since registry won't guarantee that. - if (ERROR_SUCCESS == er) - { - hAssertFile = ::CreateFileA(szPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE != hAssertFile) - { - if (INVALID_SET_FILE_POINTER != ::SetFilePointer(hAssertFile, 0, 0, FILE_END)) - { - if (SUCCEEDED(::StringCchCatA(szMsg, countof(szMsg), "\r\n"))) - { - ::WriteFile(hAssertFile, szMsg, lstrlenA(szMsg), &cch, NULL); - } - } - } - } - } - - // if anything went wrong while fooling around with the registry, just show the usual assert dialog box - if (ERROR_SUCCESS != er) - { - hr = ::StringCchCatA(szMsg, countof(szMsg), "\nAbort=Debug, Retry=Skip, Ignore=Skip all"); - DExitOnFailure(hr, "failed to concat string while building assert message"); - - id = ::MessageBoxA(0, szMsg, "Debug Assert Message", - MB_SERVICE_NOTIFICATION | MB_TOPMOST | - MB_DEFBUTTON2 | MB_ABORTRETRYIGNORE); - } - } - - if (id == IDABORT) - { - if (Dutil_hAssertModule) - { - ::GetModuleFileNameA(Dutil_hAssertModule, szPath, countof(szPath)); - - hr = ::StringCchPrintfA(szMsg, countof(szMsg), "Module is running from: %s\nIf you are not using pdb-stamping, place your PDB near the module and attach to process id: %d (0x%x)", szPath, ::GetCurrentProcessId(), ::GetCurrentProcessId()); - if (SUCCEEDED(hr)) - { - ::MessageBoxA(0, szMsg, "Debug Assert Message", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK); - } - } - - ::DebugBreak(); - } - else if (id == IDIGNORE) - { - Dutil_fNoAsserts = TRUE; - } - -LExit: - ReleaseFileHandle(hAssertFile); - ReleaseRegKey(hkDebug); - fInAssert = FALSE; -} - - -/******************************************************************* -Dutil_Assert - -*******************************************************************/ -extern "C" void DAPI Dutil_Assert( - __in_z LPCSTR szFile, - __in int iLine - ) -{ - HRESULT hr = S_OK; - char szMessage[DUTIL_STRING_BUFFER] = { }; - hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i", szFile, iLine); - if (SUCCEEDED(hr)) - { - Dutil_AssertMsg(szMessage); - } - else - { - Dutil_AssertMsg("Assert failed to build string"); - } -} - - -/******************************************************************* -Dutil_AssertSz - -*******************************************************************/ -extern "C" void DAPI Dutil_AssertSz( - __in_z LPCSTR szFile, - __in int iLine, - __in_z __format_string LPCSTR szMsg - ) -{ - HRESULT hr = S_OK; - char szMessage[DUTIL_STRING_BUFFER] = { }; - - hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i\n%s", szFile, iLine, szMsg); - if (SUCCEEDED(hr)) - { - Dutil_AssertMsg(szMessage); - } - else - { - Dutil_AssertMsg("Assert failed to build string"); - } -} - - -/******************************************************************* -Dutil_TraceSetLevel - -*******************************************************************/ -extern "C" void DAPI Dutil_TraceSetLevel( - __in REPORT_LEVEL rl, - __in BOOL fTraceFilenames - ) -{ - Dutil_rlCurrentTrace = rl; - Dutil_fTraceFilenames = fTraceFilenames; -} - - -/******************************************************************* -Dutil_TraceGetLevel - -*******************************************************************/ -extern "C" REPORT_LEVEL DAPI Dutil_TraceGetLevel() -{ - return Dutil_rlCurrentTrace; -} - - -/******************************************************************* -Dutil_Trace - -*******************************************************************/ -extern "C" void DAPIV Dutil_Trace( - __in_z LPCSTR szFile, - __in int iLine, - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid tracing level"); - - HRESULT hr = S_OK; - char szOutput[DUTIL_STRING_BUFFER] = { }; - char szMsg[DUTIL_STRING_BUFFER] = { }; - - if (Dutil_rlCurrentTrace < rl) - { - return; - } - - va_list args; - va_start(args, szFormat); - hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args); - va_end(args); - - if (SUCCEEDED(hr)) - { - LPCSTR szPrefix = "Trace/u"; - switch (rl) - { - case REPORT_STANDARD: - szPrefix = "Trace/s"; - break; - case REPORT_VERBOSE: - szPrefix = "Trace/v"; - break; - case REPORT_DEBUG: - szPrefix = "Trace/d"; - break; - } - - if (Dutil_fTraceFilenames) - { - hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s [%s,%d]: %s\r\n", szPrefix, szFile, iLine, szOutput); - } - else - { - hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s: %s\r\n", szPrefix, szOutput); - } - - if (SUCCEEDED(hr)) - { - OutputDebugStringA(szMsg); - } - // else fall through to the case below - } - - if (FAILED(hr)) - { - if (Dutil_fTraceFilenames) - { - ::StringCchPrintfA(szMsg, countof(szMsg), "Trace [%s,%d]: message too long, skipping\r\n", szFile, iLine); - } - else - { - ::StringCchPrintfA(szMsg, countof(szMsg), "Trace: message too long, skipping\r\n"); - } - - szMsg[countof(szMsg)-1] = '\0'; - OutputDebugStringA(szMsg); - } -} - - -/******************************************************************* -Dutil_TraceError - -*******************************************************************/ -extern "C" void DAPIV Dutil_TraceError( - __in_z LPCSTR szFile, - __in int iLine, - __in REPORT_LEVEL rl, - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - char szOutput[DUTIL_STRING_BUFFER] = { }; - char szMsg[DUTIL_STRING_BUFFER] = { }; - - // if this is NOT an error report and we're not logging at this level, bail - if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl) - { - return; - } - - va_list args; - va_start(args, szFormat); - hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args); - va_end(args); - - if (SUCCEEDED(hr)) - { - if (Dutil_fTraceFilenames) - { - if (FAILED(hrError)) - { - hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: %s\r\n", hrError, szFile, iLine, szOutput); - } - else - { - hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: %s\r\n", szFile, iLine, szOutput); - } - } - else - { - if (FAILED(hrError)) - { - hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: %s\r\n", hrError, szOutput); - } - else - { - hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: %s\r\n", szOutput); - } - } - - if (SUCCEEDED(hr)) - { - OutputDebugStringA(szMsg); - } - // else fall through to the failure case below - } - - if (FAILED(hr)) - { - if (Dutil_fTraceFilenames) - { - if (FAILED(hrError)) - { - ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: message too long, skipping\r\n", hrError, szFile, iLine); - } - else - { - ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: message too long, skipping\r\n", szFile, iLine); - } - } - else - { - if (FAILED(hrError)) - { - ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: message too long, skipping\r\n", hrError); - } - else - { - ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: message too long, skipping\r\n"); - } - } - - szMsg[countof(szMsg)-1] = '\0'; - OutputDebugStringA(szMsg); - } -} - - -DAPIV_(void) Dutil_TraceErrorSource( - __in_z LPCSTR szFile, - __in int iLine, - __in REPORT_LEVEL rl, - __in UINT source, - __in HRESULT hr, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - // if this is NOT an error report and we're not logging at this level, bail - if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl) - { - return; - } - - if (DUTIL_SOURCE_UNKNOWN != source && vpfnTraceErrorCallback) - { - va_list args; - va_start(args, szFormat); - vpfnTraceErrorCallback(szFile, iLine, rl, source, hr, szFormat, args); - va_end(args); - } -} - - -/******************************************************************* -Dutil_RootFailure - -*******************************************************************/ -extern "C" void DAPI Dutil_RootFailure( - __in_z LPCSTR szFile, - __in int iLine, - __in HRESULT hrError - ) -{ -#ifndef DEBUG - UNREFERENCED_PARAMETER(szFile); - UNREFERENCED_PARAMETER(iLine); - UNREFERENCED_PARAMETER(hrError); -#endif // DEBUG - - TraceError(hrError, "Root failure at %s:%d", szFile, iLine); -} - -/******************************************************************* - LoadSystemLibrary - Fully qualifies the path to a module in the - Windows system directory and loads it. - - Returns - E_MODNOTFOUND - The module could not be found. - * - Another error occured. -********************************************************************/ -extern "C" HRESULT DAPI LoadSystemLibrary( - __in_z LPCWSTR wzModuleName, - __out HMODULE *phModule - ) -{ - HRESULT hr = LoadSystemLibraryWithPath(wzModuleName, phModule, NULL); - return hr; -} - -/******************************************************************* - LoadSystemLibraryWithPath - Fully qualifies the path to a module in - the Windows system directory and loads it - and returns the path - - Returns - E_MODNOTFOUND - The module could not be found. - * - Another error occured. -********************************************************************/ -extern "C" HRESULT DAPI LoadSystemLibraryWithPath( - __in_z LPCWSTR wzModuleName, - __out HMODULE *phModule, - __deref_out_z_opt LPWSTR* psczPath - ) -{ - HRESULT hr = S_OK; - DWORD cch = 0; - WCHAR wzPath[MAX_PATH] = { }; - - cch = ::GetSystemDirectoryW(wzPath, MAX_PATH); - DExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory."); - - if (L'\\' != wzPath[cch - 1]) - { - hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1); - DExitOnRootFailure(hr, "Failed to terminate the string with a backslash."); - } - - hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName); - DExitOnRootFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName); - - *phModule = ::LoadLibraryW(wzPath); - DExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", wzModuleName); - - if (psczPath) - { - hr = StrAllocString(psczPath, wzPath, MAX_PATH); - DExitOnFailure(hr, "Failed to copy the path to library."); - } - -LExit: - return hr; -} diff --git a/src/dutil/dutil.nuspec b/src/dutil/dutil.nuspec deleted file mode 100644 index 3499a2d5..00000000 --- a/src/dutil/dutil.nuspec +++ /dev/null @@ -1,27 +0,0 @@ - - - - $id$ - $version$ - $authors$ - $authors$ - MS-RL - https://github.com/wixtoolset/dutil - false - $description$ - $copyright$ - - - - - - - - - - - - - - - diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj deleted file mode 100644 index 4e341438..00000000 --- a/src/dutil/dutil.vcxproj +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - Debug - ARM64 - - - Debug - Win32 - - - Release - ARM64 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - - {1244E671-F108-4334-BA52-8A7517F26ECD} - StaticLibrary - dutil - true - v142 - MultiByte - WiX Toolset native library foundation - WixToolset.DUtil - - - - - - - - - - - - - - - - - - - - - - - - - - - Create - 4091;4458 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/dutil/dutil.vcxproj.filters b/src/dutil/dutil.vcxproj.filters deleted file mode 100644 index b93d166b..00000000 --- a/src/dutil/dutil.vcxproj.filters +++ /dev/null @@ -1,372 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Header Files - - - - \ No newline at end of file diff --git a/src/dutil/eseutil.cpp b/src/dutil/eseutil.cpp deleted file mode 100644 index b9455d4b..00000000 --- a/src/dutil/eseutil.cpp +++ /dev/null @@ -1,1340 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define EseExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) -#define EseExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) -#define EseExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) -#define EseExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) -#define EseExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) -#define EseExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) -#define EseExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__) -#define EseExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__) -#define EseExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__) -#define EseExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__) -#define EseExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ESEUTIL, e, x, s, __VA_ARGS__) -#define EseExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ESEUTIL, g, x, s, __VA_ARGS__) - -struct ESE_QUERY -{ - ESE_QUERY_TYPE qtQueryType; - BOOL fIndexRangeSet; - - JET_SESID jsSession; - JET_TABLEID jtTable; - - DWORD dwColumns; - void *pvData[10]; // The data queried for for this column - DWORD cbData[10]; // One for each column, describes the size of the corresponding entry in ppvData -}; - -// Todo: convert more JET_ERR to HRESULTS here -HRESULT HresultFromJetError(JET_ERR jEr) -{ - HRESULT hr = S_OK; - - switch (jEr) - { - case JET_errSuccess: - return S_OK; - - case JET_wrnNyi: - return E_NOTIMPL; - break; - - case JET_errOutOfMemory: - hr = E_OUTOFMEMORY; - break; - - case JET_errInvalidParameter: __fallthrough; - case JET_errInvalidInstance: - hr = E_INVALIDARG; - break; - - case JET_errDatabaseInUse: - hr = HRESULT_FROM_WIN32(ERROR_DEVICE_IN_USE); - break; - - case JET_errKeyDuplicate: - hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); - break; - - case JET_errInvalidSystemPath: __fallthrough; - case JET_errInvalidLogDirectory: __fallthrough; - case JET_errInvalidPath: __fallthrough; - case JET_errDatabaseInvalidPath: - hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME); - break; - - case JET_errDatabaseLocked: - hr = HRESULT_FROM_WIN32(ERROR_FILE_CHECKED_OUT); - break; - - case JET_errInvalidDatabase: - hr = HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION); - break; - - case JET_errCallbackNotResolved: - hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); - break; - - case JET_errNoCurrentRecord: __fallthrough; - case JET_errRecordNotFound: __fallthrough; - case JET_errFileNotFound: __fallthrough; - case JET_errObjectNotFound: - hr = E_NOTFOUND; - break; - - case JET_wrnBufferTruncated: - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - break; - - case JET_errFileAccessDenied: - hr = E_ACCESSDENIED; - break; - - default: - hr = E_FAIL; - } - - // Log the actual Jet error code so we have record of it before it's morphed into an HRESULT to be compatible with the rest of our code - ExitTraceSource(DUTIL_SOURCE_ESEUTIL, hr, "Encountered Jet Error: 0x%08x", jEr); - - return hr; -} - -#define ExitOnJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { ExitTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__); goto LExit; }} -#define ExitOnRootJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__); goto LExit; }} - -HRESULT DAPI EseBeginSession( - __out JET_INSTANCE *pjiInstance, - __out JET_SESID *pjsSession, - __in_z LPCWSTR pszInstance, - __in_z LPCWSTR pszPath - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - LPSTR pszAnsiInstance = NULL; - LPSTR pszAnsiPath = NULL; - - hr = DirEnsureExists(pszPath, NULL); - EseExitOnFailure(hr, "Failed to ensure database directory exists"); - - // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling, - // likely breaking everyone with unicode characters in their path. - hr = StrAnsiAllocString(&pszAnsiInstance, pszInstance, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting instance name to ansi"); - - hr = StrAnsiAllocString(&pszAnsiPath, pszPath, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting session path name to ansi"); - - jEr = JetCreateInstanceA(pjiInstance, pszAnsiInstance); - ExitOnJetFailure(jEr, hr, "Failed to create instance"); - - jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramSystemPath, NULL, pszAnsiPath); - ExitOnJetFailure(jEr, hr, "Failed to set jet system path to: %s", pszAnsiPath); - - // This makes sure log files that are created are created next to the database, not next to our EXE (note they last after execution) - jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramLogFilePath, NULL, pszAnsiPath); - ExitOnJetFailure(jEr, hr, "Failed to set jet log file path to: %s", pszAnsiPath); - - jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramMaxOpenTables, 10, NULL); - ExitOnJetFailure(jEr, hr, "Failed to set jet max open tables parameter"); - - // TODO: Use callback hooks so that Jet Engine uses our memory allocation methods, etc.? (search docs for "JET_PFNREALLOC" - there are other callbacks too) - - jEr = JetInit(pjiInstance); - ExitOnJetFailure(jEr, hr, "Failed to initialize jet engine instance"); - - jEr = JetBeginSession(*pjiInstance, pjsSession, NULL, NULL); - ExitOnJetFailure(jEr, hr, "Failed to begin jet session"); - -LExit: - ReleaseStr(pszAnsiInstance); - ReleaseStr(pszAnsiPath); - - return hr; -} - -HRESULT DAPI EseEndSession( - __in JET_INSTANCE jiInstance, - __in JET_SESID jsSession - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetEndSession(jsSession, 0); - ExitOnJetFailure(jEr, hr, "Failed to end jet session"); - - jEr = JetTerm(jiInstance); - ExitOnJetFailure(jEr, hr, "Failed to uninitialize jet engine instance"); - -LExit: - return hr; -} - -// Utility function used by EnsureSchema() -HRESULT AllocColumnCreateStruct( - __in const ESE_TABLE_SCHEMA *ptsSchema, - __deref_out JET_COLUMNCREATE **ppjccColumnCreate - ) -{ - HRESULT hr = S_OK; - DWORD_PTR i; - size_t cbAllocSize = 0; - - hr = ::SizeTMult(ptsSchema->dwColumns, sizeof(JET_COLUMNCREATE), &(cbAllocSize)); - EseExitOnFailure(hr, "Maximum allocation exceeded."); - - *ppjccColumnCreate = static_cast(MemAlloc(cbAllocSize, TRUE)); - EseExitOnNull(*ppjccColumnCreate, hr, E_OUTOFMEMORY, "Failed to allocate column create structure for database"); - - for (i = 0; i < ptsSchema->dwColumns; ++i) - { - (*ppjccColumnCreate)[i].cbStruct = sizeof(JET_COLUMNCREATE); - - hr = StrAnsiAllocString(&(*ppjccColumnCreate)[i].szColumnName, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP); - EseExitOnFailure(hr, "Failed to allocate ansi column name: %ls", ptsSchema->pcsColumns[i].pszName); - - (*ppjccColumnCreate)[i].coltyp = ptsSchema->pcsColumns[i].jcColumnType; - - if (JET_coltypText == (*ppjccColumnCreate)[i].coltyp) - { - (*ppjccColumnCreate)[i].cbMax = 256; - } - else if (JET_coltypLongText == (*ppjccColumnCreate)[i].coltyp) - { - (*ppjccColumnCreate)[i].cbMax = 2147483648; - (*ppjccColumnCreate)[i].grbit = JET_bitColumnTagged; // LongText columns must be tagged - ptsSchema->pcsColumns[i].fNullable = TRUE; - } - else if (JET_coltypLong == (*ppjccColumnCreate)[i].coltyp) - { - (*ppjccColumnCreate)[i].cbMax = 4; - - if (ptsSchema->pcsColumns[i].fAutoIncrement) - { - (*ppjccColumnCreate)[i].grbit |= JET_bitColumnAutoincrement; - } - } - - if (!(ptsSchema->pcsColumns[i].fNullable)) - { - (*ppjccColumnCreate)[i].grbit |= JET_bitColumnNotNULL; - } - - (*ppjccColumnCreate)[i].pvDefault = NULL; - (*ppjccColumnCreate)[i].cbDefault = 0; - (*ppjccColumnCreate)[i].cp = 1200; - (*ppjccColumnCreate)[i].columnid = 0; - (*ppjccColumnCreate)[i].err = 0; - } - -LExit: - return hr; -} - -HRESULT FreeColumnCreateStruct( - __in_ecount(dwColumns) JET_COLUMNCREATE *pjccColumnCreate, - __in DWORD dwColumns - ) -{ - HRESULT hr = S_OK; - DWORD i; - - for (i = 0; i < dwColumns; ++i) - { - ReleaseStr((pjccColumnCreate[i]).szColumnName); - } - - hr = MemFree(pjccColumnCreate); - EseExitOnFailure(hr, "Failed to release core column create struct"); - -LExit: - return hr; -} - -// Utility function used by EnsureSchema() -HRESULT AllocIndexCreateStruct( - __in const ESE_TABLE_SCHEMA *ptsSchema, - __deref_out JET_INDEXCREATE **ppjicIndexCreate - ) -{ - HRESULT hr = S_OK; - LPSTR pszMultiSzKeys = NULL; - LPSTR pszIndexName = NULL; - LPSTR pszTempString = NULL; - BOOL fKeyColumns = FALSE; - DWORD_PTR i; - - for (i=0; i < ptsSchema->dwColumns; ++i) - { - if (ptsSchema->pcsColumns[i].fKey) - { - hr = StrAnsiAllocString(&pszTempString, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP); - EseExitOnFailure(hr, "Failed to convert string to ansi: %ls", ptsSchema->pcsColumns[i].pszName); - - hr = StrAnsiAllocConcat(&pszMultiSzKeys, "+", 0); - EseExitOnFailure(hr, "Failed to append plus sign to multisz string: %s", pszTempString); - - hr = StrAnsiAllocConcat(&pszMultiSzKeys, pszTempString, 0); - EseExitOnFailure(hr, "Failed to append column name to multisz string: %s", pszTempString); - - ReleaseNullStr(pszTempString); - - // All question marks will be converted to null characters later; this is just to trick dutil - // into letting us create an ansi, double-null-terminated list of single-null-terminated strings - hr = StrAnsiAllocConcat(&pszMultiSzKeys, "?", 0); - EseExitOnFailure(hr, "Failed to append placeholder character to multisz string: %hs", pszMultiSzKeys); - - // Record that at least one key column was found - fKeyColumns = TRUE; - } - } - - // If no key columns were found, don't create an index - just return - if (!fKeyColumns) - { - ExitFunction1(hr = S_OK); - } - - hr = StrAnsiAllocString(&pszIndexName, ptsSchema->pszName, 0, CP_ACP); - EseExitOnFailure(hr, "Failed to allocate ansi string version of %ls", ptsSchema->pszName); - - hr = StrAnsiAllocConcat(&pszIndexName, "_Index", 0); - EseExitOnFailure(hr, "Failed to append table name string version of %ls", ptsSchema->pszName); - - *ppjicIndexCreate = static_cast(MemAlloc(sizeof(JET_INDEXCREATE), TRUE)); - EseExitOnNull(*ppjicIndexCreate, hr, E_OUTOFMEMORY, "Failed to allocate index create structure for database"); - - // Record the size including both null terminators - the struct requires this - size_t cchSize = 0; - hr = ::StringCchLengthA(pszMultiSzKeys, STRSAFE_MAX_LENGTH, &cchSize); - EseExitOnRootFailure(hr, "Failed to get size of keys string"); - - ++cchSize; // add 1 to include null character at the end - - // At this point convert all question marks to null characters - for (i = 0; i < cchSize; ++i) - { - if ('?' == pszMultiSzKeys[i]) - { - pszMultiSzKeys[i] = '\0'; - } - } - - (*ppjicIndexCreate)->cbStruct = sizeof(JET_INDEXCREATE); - (*ppjicIndexCreate)->szIndexName = pszIndexName; - (*ppjicIndexCreate)->szKey = pszMultiSzKeys; - (*ppjicIndexCreate)->cbKey = (DWORD)cchSize; - (*ppjicIndexCreate)->grbit = JET_bitIndexUnique | JET_bitIndexPrimary; - (*ppjicIndexCreate)->ulDensity = 80; - (*ppjicIndexCreate)->lcid = 1033; - (*ppjicIndexCreate)->pidxunicode = NULL; - (*ppjicIndexCreate)->cbVarSegMac = 0; - (*ppjicIndexCreate)->rgconditionalcolumn = NULL; - (*ppjicIndexCreate)->cConditionalColumn = 0; - (*ppjicIndexCreate)->err = 0; - -LExit: - ReleaseStr(pszTempString); - - return hr; -} - -HRESULT EnsureSchema( - __in JET_DBID jdbDb, - __in JET_SESID jsSession, - __in ESE_DATABASE_SCHEMA *pdsSchema - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - BOOL fTransaction = FALSE; - DWORD dwTable; - DWORD dwColumn; - JET_TABLECREATE jtTableCreate = { }; - - // Set parameters which apply to all tables here - jtTableCreate.cbStruct = sizeof(jtTableCreate); - jtTableCreate.ulPages = 100; - jtTableCreate.ulDensity = 0; // per the docs, 0 means "use the default value" - jtTableCreate.cIndexes = 1; - - hr = EseBeginTransaction(jsSession); - EseExitOnFailure(hr, "Failed to begin transaction to create tables"); - fTransaction = TRUE; - - for (dwTable = 0;dwTable < pdsSchema->dwTables; ++dwTable) - { - // Don't free this pointer - it's just a shortcut to the current table's name within the struct - LPCWSTR pwzTableName = pdsSchema->ptsTables[dwTable].pszName; - - // Ensure table exists - hr = EseOpenTable(jsSession, jdbDb, pwzTableName, &pdsSchema->ptsTables[dwTable].jtTable); - if (E_NOTFOUND == hr) // if the table is missing, create it - { - // Fill out the JET_TABLECREATE struct - hr = StrAnsiAllocString(&jtTableCreate.szTableName, pdsSchema->ptsTables[dwTable].pszName, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting table name to ansi"); - - hr = AllocColumnCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgcolumncreate); - EseExitOnFailure(hr, "Failed to allocate column create struct"); - - hr = AllocIndexCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgindexcreate); - EseExitOnFailure(hr, "Failed to allocate index create struct"); - - jtTableCreate.cColumns = pdsSchema->ptsTables[dwTable].dwColumns; - jtTableCreate.tableid = NULL; - - // TODO: Investigate why we can't create a table without a key column? - // Actually create the table using our JET_TABLECREATE struct - jEr = JetCreateTableColumnIndex(jsSession, jdbDb, &jtTableCreate); - ExitOnJetFailure(jEr, hr, "Failed to create %ls table", pwzTableName); - - // Record the table ID in our cache - pdsSchema->ptsTables[dwTable].jtTable = jtTableCreate.tableid; - - // Record the column IDs in our cache - for (dwColumn = 0; dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn) - { - pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn].jcColumn = jtTableCreate.rgcolumncreate[dwColumn].columnid; - } - - // Free and NULL things we allocated in this struct - ReleaseNullStr(jtTableCreate.szTableName); - - hr = FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns); - EseExitOnFailure(hr, "Failed to free column create struct"); - jtTableCreate.rgcolumncreate = NULL; - } - else - { - // If the table already exists, grab the column ids and put them into our cache - for (dwColumn = 0;dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn) - { - // Don't free this - it's just a shortcut to the current column within the struct - ESE_COLUMN_SCHEMA *pcsColumn = &(pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn]); - ULONG ulColumnSize = 0; - BOOL fNullable = pcsColumn->fNullable; - - // Todo: this code is nearly duplicated from AllocColumnCreateStruct - factor it out! - if (JET_coltypText == pcsColumn->jcColumnType) - { - ulColumnSize = 256; - } - else if (JET_coltypLongText == pcsColumn->jcColumnType) - { - ulColumnSize = 2147483648; - fNullable = TRUE; - } - else if (JET_coltypLong == pcsColumn->jcColumnType) - { - ulColumnSize = 4; - fNullable = TRUE; - } - - hr = EseEnsureColumn(jsSession, pdsSchema->ptsTables[dwTable].jtTable, pcsColumn->pszName, pcsColumn->jcColumnType, ulColumnSize, pcsColumn->fFixed, fNullable, &pcsColumn->jcColumn); - EseExitOnFailure(hr, "Failed to create column %u of %ls table", dwColumn, pwzTableName); - } - } - } - -LExit: - ReleaseStr(jtTableCreate.szTableName); - - if (NULL != jtTableCreate.rgcolumncreate) - { - // Don't record the HRESULT here or it will override the return value of this function - FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns); - } - - if (fTransaction) - { - EseCommitTransaction(jsSession); - } - - return hr; -} - -// Todo: support overwrite flag? Unfortunately, requires WinXP and up -// Todo: Add version parameter, and a built-in dutil table that stores the version of the database schema on disk - then allow overriding the "migrate to new schema" functionality with a callback -HRESULT DAPI EseEnsureDatabase( - __in JET_SESID jsSession, - __in_z LPCWSTR pszFile, - __in ESE_DATABASE_SCHEMA *pdsSchema, - __out JET_DBID* pjdbDb, - __in BOOL fExclusive, - __in BOOL fReadonly - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - JET_GRBIT jgrOptions = 0; - LPWSTR pszDir = NULL; - LPSTR pszAnsiFile = NULL; - - // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling, - // likely breaking all those with unicode characters in their path. - hr = StrAnsiAllocString(&pszAnsiFile, pszFile, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting database name to ansi"); - - hr = PathGetDirectory(pszFile, &pszDir); - EseExitOnFailure(hr, "Failed to get directory that will contain database file"); - - hr = DirEnsureExists(pszDir, NULL); - EseExitOnFailure(hr, "Failed to ensure directory exists for database: %ls", pszDir); - - if (FileExistsEx(pszFile, NULL)) - { - if (fReadonly) - { - jgrOptions = jgrOptions | JET_bitDbReadOnly; - } - - jEr = JetAttachDatabaseA(jsSession, pszAnsiFile, jgrOptions); - ExitOnJetFailure(jEr, hr, "Failed to attach to database %s", pszAnsiFile); - - // This flag doesn't apply to attach, only applies to Open, so only set it after the attach - if (fExclusive) - { - jgrOptions = jgrOptions | JET_bitDbExclusive; - } - - jEr = JetOpenDatabaseA(jsSession, pszAnsiFile, NULL, pjdbDb, jgrOptions); - ExitOnJetFailure(jEr, hr, "Failed to open database %s", pszAnsiFile); - } - else - { - jEr = JetCreateDatabase2A(jsSession, pszAnsiFile, 0, pjdbDb, 0); - ExitOnJetFailure(jEr, hr, "Failed to create database %ls", pszFile); - } - - hr = EnsureSchema(*pjdbDb, jsSession, pdsSchema); - EseExitOnFailure(hr, "Failed to ensure database schema matches expectations"); - -LExit: - ReleaseStr(pszDir); - ReleaseStr(pszAnsiFile); - - return hr; -} - -HRESULT DAPI EseCloseDatabase( - __in JET_SESID jsSession, - __in JET_DBID jdbDb - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - JET_GRBIT jgrOptions = 0; - - jEr = JetCloseDatabase(jsSession, jdbDb, jgrOptions); - ExitOnJetFailure(jEr, hr, "Failed to close database"); - -LExit: - return hr; -} - -HRESULT DAPI EseCreateTable( - __in JET_SESID jsSession, - __in JET_DBID jdbDb, - __in_z LPCWSTR pszTable, - __out JET_TABLEID *pjtTable - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - LPSTR pszAnsiTable = NULL; - - hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting table name to ansi"); - - jEr = JetCreateTableA(jsSession, jdbDb, pszAnsiTable, 100, 0, pjtTable); - ExitOnJetFailure(jEr, hr, "Failed to create table %s", pszAnsiTable); - -LExit: - ReleaseStr(pszAnsiTable); - - return hr; -} - -HRESULT DAPI EseOpenTable( - __in JET_SESID jsSession, - __in JET_DBID jdbDb, - __in_z LPCWSTR pszTable, - __out JET_TABLEID *pjtTable - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - LPSTR pszAnsiTable = NULL; - - hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting table name to ansi"); - - jEr = JetOpenTableA(jsSession, jdbDb, pszAnsiTable, NULL, 0, 0, pjtTable); - ExitOnJetFailure(jEr, hr, "Failed to open table %s", pszAnsiTable); - -LExit: - ReleaseStr(pszAnsiTable); - - return hr; -} - -HRESULT DAPI EseCloseTable( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetCloseTable(jsSession, jtTable); - ExitOnJetFailure(jEr, hr, "Failed to close table"); - -LExit: - return hr; -} - -HRESULT DAPI EseEnsureColumn( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in_z LPCWSTR pszColumnName, - __in JET_COLTYP jcColumnType, - __in ULONG ulColumnSize, - __in BOOL fFixed, - __in BOOL fNullable, - __out_opt JET_COLUMNID *pjcColumn - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - LPSTR pszAnsiColumnName = NULL; - JET_COLUMNDEF jcdColumnDef = { sizeof(JET_COLUMNDEF) }; - JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) }; - - hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting column name to ansi"); - - jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase); - if (JET_errSuccess == jEr) - { - // Return the found columnID - if (NULL != pjcColumn) - { - *pjcColumn = jcdTempBase.columnid; - } - - ExitFunction1(hr = S_OK); - } - else if (JET_errColumnNotFound == jEr) - { - jEr = JET_errSuccess; - } - ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName); - - jcdColumnDef.columnid = 0; - jcdColumnDef.coltyp = jcColumnType; - jcdColumnDef.wCountry = 0; - jcdColumnDef.langid = 0; - jcdColumnDef.cp = 1200; - jcdColumnDef.wCollate = 0; - jcdColumnDef.cbMax = ulColumnSize; - jcdColumnDef.grbit = 0; - - if (fFixed) - { - jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnFixed; - } - if (!fNullable) - { - jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnNotNULL; - } - - jEr = JetAddColumnA(jsSession, jtTable, pszAnsiColumnName, &jcdColumnDef, NULL, 0, pjcColumn); - ExitOnJetFailure(jEr, hr, "Failed to add column %ls", pszColumnName); - -LExit: - ReleaseStr(pszAnsiColumnName); - - return hr; -} - -HRESULT DAPI EseGetColumn( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in_z LPCWSTR pszColumnName, - __out JET_COLUMNID *pjcColumn - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - LPSTR pszAnsiColumnName = NULL; - JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) }; - - hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP); - EseExitOnFailure(hr, "Failed converting column name to ansi"); - - jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase); - if (JET_errSuccess == jEr) - { - // Return the found columnID - if (NULL != pjcColumn) - { - *pjcColumn = jcdTempBase.columnid; - } - - ExitFunction1(hr = S_OK); - } - ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName); - -LExit: - ReleaseStr(pszAnsiColumnName); - - return hr; -} - -HRESULT DAPI EseMoveCursor( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in LONG lRow - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetMove(jsSession, jtTable, lRow, 0); - ExitOnJetFailure(jEr, hr, "Failed to move jet cursor by amount: %d", lRow); - -LExit: - return hr; -} - -HRESULT DAPI EseDeleteRow( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetDelete(jsSession, jtTable); - ExitOnJetFailure(jEr, hr, "Failed to delete row"); - -LExit: - return hr; -} - -HRESULT DAPI EseBeginTransaction( - __in JET_SESID jsSession - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetBeginTransaction(jsSession); - ExitOnJetFailure(jEr, hr, "Failed to begin transaction"); - -LExit: - return hr; -} - -HRESULT DAPI EseRollbackTransaction( - __in JET_SESID jsSession, - __in BOOL fAll - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetRollback(jsSession, fAll ? JET_bitRollbackAll : 0); - ExitOnJetFailure(jEr, hr, "Failed to rollback transaction"); - -LExit: - return hr; -} - -HRESULT DAPI EseCommitTransaction( - __in JET_SESID jsSession - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetCommitTransaction(jsSession, 0); - ExitOnJetFailure(jEr, hr, "Failed to commit transaction"); - -LExit: - return hr; -} - -HRESULT DAPI EsePrepareUpdate( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in ULONG ulPrep - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetPrepareUpdate(jsSession, jtTable, ulPrep); - ExitOnJetFailure(jEr, hr, "Failed to prepare for update of type: %ul", ulPrep); - -LExit: - return hr; -} - -HRESULT DAPI EseFinishUpdate( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in BOOL fSeekToInsertedRecord - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - unsigned char rgbBookmark[JET_cbBookmarkMost + 1]; - DWORD cbBookmark; - - if (fSeekToInsertedRecord) - { - jEr = JetUpdate(jsSession, jtTable, rgbBookmark, sizeof(rgbBookmark), &cbBookmark); - ExitOnJetFailure(jEr, hr, "Failed to run update and retrieve bookmark"); - - jEr = JetGotoBookmark(jsSession, jtTable, rgbBookmark, cbBookmark); - ExitOnJetFailure(jEr, hr, "Failed to seek to recently updated record using bookmark"); - } - else - { - jEr = JetUpdate(jsSession, jtTable, NULL, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to run update (without retrieving bookmark)"); - } - -LExit: - // If we fail, the caller won't expect that the update wasn't finished, so we'll cancel their entire update to leave them in a good state - if (FAILED(hr)) - { - JetPrepareUpdate(jsSession, jtTable, JET_prepCancel); - } - - return hr; -} - -HRESULT DAPI EseSetColumnBinary( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pbBuffer, static_cast(cbBuffer), 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to set binary value into column of database"); - -LExit: - return hr; -} - -HRESULT DAPI EseSetColumnDword( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in DWORD dwValue - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &dwValue, sizeof(DWORD), 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to set dword value into column of database: %u", dwValue); - -LExit: - return hr; -} - -HRESULT DAPI EseSetColumnBool( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in BOOL fValue - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - BYTE bValue = fValue ? 0xFF : 0x00; - - jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to set bool value into column of database"); - -LExit: - return hr; -} - -HRESULT DAPI EseSetColumnString( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in_z LPCWSTR pwzValue - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - size_t cchValue = 0; - ULONG cbValueSize = 0; - - if (pwzValue) - { - hr = ::StringCchLengthW(pwzValue, STRSAFE_MAX_LENGTH, &cchValue); - EseExitOnRootFailure(hr, "Failed to get string length: %ls", pwzValue); - } - - cbValueSize = static_cast((cchValue + 1) * sizeof(WCHAR)); // add 1 for null character, then multiply by size of WCHAR to get bytes - - jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pwzValue, cbValueSize, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to set string value into column of database: %ls", pwzValue); - -LExit: - return hr; -} - -HRESULT DAPI EseSetColumnEmpty( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to set empty value into column of database"); - -LExit: - return hr; -} - -HRESULT DAPI EseGetColumnBinary( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - ULONG ulActualSize = 0; - - jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL); - if (JET_wrnBufferTruncated == jEr) - { - jEr = JET_errSuccess; - } - ExitOnJetFailure(jEr, hr, "Failed to check size of binary value from record"); - - if (NULL == *ppbBuffer) - { - *ppbBuffer = reinterpret_cast(MemAlloc(ulActualSize, FALSE)); - EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for reading binary value column"); - } - else - { - *ppbBuffer = reinterpret_cast(MemReAlloc(*ppbBuffer, ulActualSize, FALSE)); - EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate memory for reading binary value column"); - } - - jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppbBuffer, ulActualSize, NULL, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to retrieve binary value from record"); - - *piBuffer = static_cast(ulActualSize); - -LExit: - if (FAILED(hr)) - { - ReleaseNullMem(*ppbBuffer); - } - - return hr; -} - -HRESULT DAPI EseGetColumnDword( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __out DWORD *pdwValue - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pdwValue, sizeof(DWORD), NULL, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to retrieve dword value from record"); - -LExit: - return hr; -} - -HRESULT DAPI EseGetColumnBool( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __out BOOL *pfValue - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - BYTE bValue = 0; - - jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, NULL, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to retrieve bool value from record"); - - if (bValue == 0) - { - *pfValue = FALSE; - } - else - { - *pfValue = TRUE; - } - -LExit: - return hr; -} - -HRESULT DAPI EseGetColumnString( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __out LPWSTR *ppszValue - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - ULONG ulActualSize = 0; - - jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL); - if (JET_wrnBufferTruncated == jEr) - { - jEr = JET_errSuccess; - } - ExitOnJetFailure(jEr, hr, "Failed to check size of string value from record"); - - hr = StrAlloc(ppszValue, ulActualSize); - EseExitOnFailure(hr, "Failed to allocate string while retrieving column value"); - - jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppszValue, ulActualSize, NULL, 0, NULL); - ExitOnJetFailure(jEr, hr, "Failed to retrieve string value from record"); - -LExit: - return hr; -} - -HRESULT DAPI EseBeginQuery( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in ESE_QUERY_TYPE qtQueryType, - __out ESE_QUERY_HANDLE *peqhHandle - ) -{ - UNREFERENCED_PARAMETER(jsSession); - UNREFERENCED_PARAMETER(jtTable); - - HRESULT hr = S_OK; - - *peqhHandle = static_cast(MemAlloc(sizeof(ESE_QUERY), TRUE)); - EseExitOnNull(*peqhHandle, hr, E_OUTOFMEMORY, "Failed to allocate new query"); - - ESE_QUERY *peqHandle = static_cast(*peqhHandle); - peqHandle->qtQueryType = qtQueryType; - peqHandle->jsSession = jsSession; - peqHandle->jtTable = jtTable; - -LExit: - return hr; -} - -// Utility function used by other functions to set a query column -HRESULT DAPI SetQueryColumn( - __in ESE_QUERY_HANDLE eqhHandle, - __in_bcount(cbData) const void *pvData, - __in DWORD cbData, - __in JET_GRBIT jGrb - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - ESE_QUERY *peqHandle = static_cast(eqhHandle); - - if (peqHandle->dwColumns == countof(peqHandle->pvData)) - { - hr = E_NOTIMPL; - EseExitOnFailure(hr, "Dutil hasn't implemented support for queries of more than %d columns", countof(peqHandle->pvData)); - } - - if (0 == peqHandle->dwColumns) // If it's the first column, start a new key - { - jGrb = jGrb | JET_bitNewKey; - } - - jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, pvData, cbData, jGrb); - ExitOnJetFailure(jEr, hr, "Failed to begin new query"); - - // If the query is wildcard, setup the cached copy of pvData - if (ESE_QUERY_EXACT != peqHandle->qtQueryType) - { - peqHandle->pvData[peqHandle->dwColumns] = MemAlloc(cbData, FALSE); - EseExitOnNull(peqHandle->pvData[peqHandle->dwColumns], hr, E_OUTOFMEMORY, "Failed to allocate memory"); - - memcpy(peqHandle->pvData[peqHandle->dwColumns], pvData, cbData); - - peqHandle->cbData[peqHandle->dwColumns] = cbData; - } - - // Increment the number of total columns - ++peqHandle->dwColumns; - -LExit: - return hr; -} - -HRESULT DAPI EseSetQueryColumnBinary( - __in ESE_QUERY_HANDLE eqhHandle, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" - ) -{ - HRESULT hr = S_OK; - ESE_QUERY *peqHandle = static_cast(eqhHandle); - JET_GRBIT jGrb = 0; - - if (cbBuffer > DWORD_MAX) - { - ExitFunction1(hr = E_INVALIDARG); - } - - if (fFinal) - { - if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnStartLimit; - } - else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnEndLimit; - } - } - - hr = SetQueryColumn(eqhHandle, reinterpret_cast(pbBuffer), static_cast(cbBuffer), jGrb); - EseExitOnFailure(hr, "Failed to set value of query colum (as binary) to:"); - -LExit: - return hr; -} - -HRESULT DAPI EseSetQueryColumnDword( - __in ESE_QUERY_HANDLE eqhHandle, - __in DWORD dwData, - __in BOOL fFinal - ) -{ - HRESULT hr = S_OK; - ESE_QUERY *peqHandle = static_cast(eqhHandle); - JET_GRBIT jGrb = 0; - - if (fFinal) - { - if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnStartLimit; - } - else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnEndLimit; - } - } - - hr = SetQueryColumn(eqhHandle, (const void *)&dwData, sizeof(DWORD), jGrb); - EseExitOnFailure(hr, "Failed to set value of query colum (as dword) to: %u", dwData); - -LExit: - return hr; -} - -HRESULT DAPI EseSetQueryColumnBool( - __in ESE_QUERY_HANDLE eqhHandle, - __in BOOL fValue, - __in BOOL fFinal - ) -{ - HRESULT hr = S_OK; - BYTE bByte = fValue ? 0xFF : 0x00; - ESE_QUERY *peqHandle = static_cast(eqhHandle); - JET_GRBIT jGrb = 0; - - if (fFinal) - { - if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnStartLimit; - } - else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnEndLimit; - } - } - - hr = SetQueryColumn(eqhHandle, (const void *)&bByte, 1, jGrb); - EseExitOnFailure(hr, "Failed to set value of query colum (as bool) to: %s", fValue ? "TRUE" : "FALSE"); - -LExit: - return hr; -} - -HRESULT DAPI EseSetQueryColumnString( - __in ESE_QUERY_HANDLE eqhHandle, - __in_z LPCWSTR pszString, - __in BOOL fFinal - ) -{ - HRESULT hr = S_OK; - DWORD dwStringSize = 0; - size_t cchString = 0; - ESE_QUERY *peqHandle = static_cast(eqhHandle); - JET_GRBIT jGrb = 0; - - if (pszString) - { - hr = ::StringCchLengthW(pszString, STRSAFE_MAX_LENGTH, &cchString); - EseExitOnRootFailure(hr, "Failed to get size of column string"); - } - - dwStringSize = static_cast(sizeof(WCHAR) * (cchString + 1)); // Add 1 for null terminator - - if (fFinal) - { - if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnStartLimit; - } - else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) - { - jGrb = jGrb | JET_bitFullColumnEndLimit; - } - } - - hr = SetQueryColumn(eqhHandle, (const void *)pszString, dwStringSize, jGrb); - EseExitOnFailure(hr, "Failed to set value of query colum (as string) to: %ls", pszString); - -LExit: - return hr; -} - -HRESULT DAPI EseFinishQuery( - __in ESE_QUERY_HANDLE eqhHandle - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - - ESE_QUERY *peqHandle = static_cast(eqhHandle); - - if (peqHandle->fIndexRangeSet) - { - jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeRemove); - ExitOnJetFailure(jEr, hr, "Failed to release index range"); - - peqHandle->fIndexRangeSet = FALSE; - } - - for (int i=0; i < countof(peqHandle->pvData); ++i) - { - ReleaseMem(peqHandle->pvData[i]); - } - - ReleaseMem(peqHandle); - -LExit: - return hr; -} - -HRESULT DAPI EseRunQuery( - __in ESE_QUERY_HANDLE eqhHandle - ) -{ - HRESULT hr = S_OK; - JET_ERR jEr = JET_errSuccess; - JET_GRBIT jGrb = 0; - JET_GRBIT jGrbSeekType = 0; - DWORD i; - - ESE_QUERY *peqHandle = static_cast(eqhHandle); - - if (ESE_QUERY_EXACT == peqHandle->qtQueryType) - { - jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, JET_bitSeekEQ); - ExitOnJetFailure(jEr, hr, "Failed to seek EQ within jet table"); - } - else - { - if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) - { - jGrbSeekType = JET_bitSeekGE; - } - else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) - { - jGrbSeekType = JET_bitSeekLE; - } - - jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, jGrbSeekType); - if (jEr == JET_wrnSeekNotEqual) - { - jEr = JET_errSuccess; - } - - // At this point we've already set our cursor to the beginning of the range of records to select. - // Now we'll make a key pointing to the end of the range of records to select, so we can call JetSetIndexRange() - // For a semi-explanation, see this doc page: http://msdn.microsoft.com/en-us/library/aa964799%28EXCHG.10%29.aspx - for (i = 0; i < peqHandle->dwColumns; ++i) - { - if (i == 0) - { - jGrb = JET_bitNewKey; - } - else - { - jGrb = 0; - } - - // On the last iteration - if (i == peqHandle->dwColumns - 1) - { - jGrb |= JET_bitFullColumnEndLimit; - } - - jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, peqHandle->pvData[i], peqHandle->cbData[i], jGrb); - ExitOnJetFailure(jEr, hr, "Failed to begin new query"); - } - - jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeUpperLimit); - ExitOnJetFailure(jEr, hr, "Failed to set index range"); - - peqHandle->fIndexRangeSet = TRUE; - - // Sometimes JetBlue doesn't check if there is a current record when calling the above function (and sometimes it does) - // So, let's check if there is a current record before returning (by reading the first byte of one). - jEr = JetMove(peqHandle->jsSession, peqHandle->jtTable, 0, 0); - ExitOnJetFailure(jEr, hr, "Failed to check if there is a current record after query"); - } - -LExit: - return hr; -} diff --git a/src/dutil/fileutil.cpp b/src/dutil/fileutil.cpp deleted file mode 100644 index 1822727a..00000000 --- a/src/dutil/fileutil.cpp +++ /dev/null @@ -1,2032 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define FileExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) -#define FileExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) -#define FileExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) -#define FileExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) -#define FileExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) -#define FileExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) -#define FileExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__) -#define FileExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__) -#define FileExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__) -#define FileExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__) -#define FileExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_FILEUTIL, e, x, s, __VA_ARGS__) -#define FileExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_FILEUTIL, g, x, s, __VA_ARGS__) - -// constants - -const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF}; -const BYTE UTF16BOM[] = {0xFF, 0xFE}; - -const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager"; -const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations"; - -/******************************************************************* - FileFromPath - returns a pointer to the file part of the path - -********************************************************************/ -extern "C" LPWSTR DAPI FileFromPath( - __in_z LPCWSTR wzPath - ) -{ - if (!wzPath) - return NULL; - - LPWSTR wzFile = const_cast(wzPath); - for (LPWSTR wz = wzFile; *wz; ++wz) - { - // valid delineators - // \ => Windows path - // / => unix and URL path - // : => relative path from mapped root - if (L'\\' == *wz || L'/' == *wz || L':' == *wz) - wzFile = wz + 1; - } - - return wzFile; -} - - -/******************************************************************* - FileResolvePath - gets the full path to a file resolving environment - variables along the way. - -********************************************************************/ -extern "C" HRESULT DAPI FileResolvePath( - __in_z LPCWSTR wzRelativePath, - __out LPWSTR *ppwzFullPath - ) -{ - Assert(wzRelativePath && *wzRelativePath); - - HRESULT hr = S_OK; - DWORD cch = 0; - LPWSTR pwzExpandedPath = NULL; - DWORD cchExpandedPath = 0; - - LPWSTR pwzFullPath = NULL; - DWORD cchFullPath = 0; - - LPWSTR wzFileName = NULL; - - // - // First, expand any environment variables. - // - cchExpandedPath = MAX_PATH; - hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); - FileExitOnFailure(hr, "Failed to allocate space for expanded path."); - - cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); - if (0 == cch) - { - FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); - } - else if (cchExpandedPath < cch) - { - cchExpandedPath = cch; - hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); - FileExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); - - cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); - if (0 == cch) - { - FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); - } - else if (cchExpandedPath < cch) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - FileExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); - } - } - - // - // Second, get the full path. - // - cchFullPath = MAX_PATH; - hr = StrAlloc(&pwzFullPath, cchFullPath); - FileExitOnFailure(hr, "Failed to allocate space for full path."); - - cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); - if (0 == cch) - { - FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); - } - else if (cchFullPath < cch) - { - cchFullPath = cch; - hr = StrAlloc(&pwzFullPath, cchFullPath); - FileExitOnFailure(hr, "Failed to re-allocate more space for full path."); - - cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); - if (0 == cch) - { - FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); - } - else if (cchFullPath < cch) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - FileExitOnRootFailure(hr, "Failed to allocate buffer for full path."); - } - } - - *ppwzFullPath = pwzFullPath; - pwzFullPath = NULL; - -LExit: - ReleaseStr(pwzFullPath); - ReleaseStr(pwzExpandedPath); - - return hr; -} - - -/******************************************************************* -FileStripExtension - Strip extension from filename -********************************************************************/ -extern "C" HRESULT DAPI FileStripExtension( -__in_z LPCWSTR wzFileName, -__out LPWSTR *ppwzFileNameNoExtension -) -{ - Assert(wzFileName && *wzFileName); - - HRESULT hr = S_OK; - size_t cchFileName = 0; - LPWSTR pwzFileNameNoExtension = NULL; - size_t cchFileNameNoExtension = 0; - errno_t err = 0; - - hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_LENGTH, &cchFileName); - FileExitOnRootFailure(hr, "failed to get length of file name: %ls", wzFileName); - - cchFileNameNoExtension = cchFileName + 1; - - hr = StrAlloc(&pwzFileNameNoExtension, cchFileNameNoExtension); - FileExitOnFailure(hr, "failed to allocate space for File Name without extension"); - - // _wsplitpath_s can handle drive/path/filename/extension - err = _wsplitpath_s(wzFileName, NULL, NULL, NULL, NULL, pwzFileNameNoExtension, cchFileNameNoExtension, NULL, NULL); - if (err) - { - hr = E_INVALIDARG; - FileExitOnRootFailure(hr, "failed to parse filename: '%ls', error: %d", wzFileName, err); - } - - *ppwzFileNameNoExtension = pwzFileNameNoExtension; - pwzFileNameNoExtension = NULL; - -LExit: - ReleaseStr(pwzFileNameNoExtension); - - return hr; -} - - -/******************************************************************* -FileChangeExtension - Changes the extension of a filename -********************************************************************/ -extern "C" HRESULT DAPI FileChangeExtension( - __in_z LPCWSTR wzFileName, - __in_z LPCWSTR wzNewExtension, - __out LPWSTR *ppwzFileNameNewExtension - ) -{ - Assert(wzFileName && *wzFileName); - - HRESULT hr = S_OK; - LPWSTR sczFileName = NULL; - - hr = FileStripExtension(wzFileName, &sczFileName); - FileExitOnFailure(hr, "Failed to strip extension from file name: %ls", wzFileName); - - hr = StrAllocConcat(&sczFileName, wzNewExtension, 0); - FileExitOnFailure(hr, "Failed to add new extension."); - - *ppwzFileNameNewExtension = sczFileName; - sczFileName = NULL; - -LExit: - ReleaseStr(sczFileName); - - return hr; -} - - -/******************************************************************* -FileAddSuffixToBaseName - Adds a suffix the base portion of a file -name; e.g., file.ext to fileSuffix.ext. -********************************************************************/ -extern "C" HRESULT DAPI FileAddSuffixToBaseName( - __in_z LPCWSTR wzFileName, - __in_z LPCWSTR wzSuffix, - __out_z LPWSTR* psczNewFileName - ) -{ - Assert(wzFileName && *wzFileName); - - HRESULT hr = S_OK; - LPWSTR sczNewFileName = NULL; - size_t cchFileName = 0; - - hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_CCH, &cchFileName); - FileExitOnRootFailure(hr, "Failed to get length of file name: %ls", wzFileName); - - LPCWSTR wzExtension = wzFileName + cchFileName; - while (wzFileName < wzExtension && L'.' != *wzExtension) - { - --wzExtension; - } - - if (wzFileName < wzExtension) - { - // found an extension so add the suffix before it - hr = StrAllocFormatted(&sczNewFileName, L"%.*ls%ls%ls", static_cast(wzExtension - wzFileName), wzFileName, wzSuffix, wzExtension); - } - else - { - // no extension, so add the suffix at the end of the whole name - hr = StrAllocString(&sczNewFileName, wzFileName, 0); - FileExitOnFailure(hr, "Failed to allocate new file name."); - - hr = StrAllocConcat(&sczNewFileName, wzSuffix, 0); - } - FileExitOnFailure(hr, "Failed to allocate new file name with suffix."); - - *psczNewFileName = sczNewFileName; - sczNewFileName = NULL; - -LExit: - ReleaseStr(sczNewFileName); - - return hr; -} - - -/******************************************************************* - FileVersion - -********************************************************************/ -extern "C" HRESULT DAPI FileVersion( - __in_z LPCWSTR wzFilename, - __out DWORD *pdwVerMajor, - __out DWORD* pdwVerMinor - ) -{ - HRESULT hr = S_OK; - - DWORD dwHandle = 0; - UINT cbVerBuffer = 0; - LPVOID pVerBuffer = NULL; - VS_FIXEDFILEINFO* pvsFileInfo = NULL; - UINT cbFileInfo = 0; - - if (0 == (cbVerBuffer = ::GetFileVersionInfoSizeW(wzFilename, &dwHandle))) - { - FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); - } - - pVerBuffer = ::GlobalAlloc(GMEM_FIXED, cbVerBuffer); - FileExitOnNullDebugTrace(pVerBuffer, hr, E_OUTOFMEMORY, "failed to allocate version info for file: %ls", wzFilename); - - if (!::GetFileVersionInfoW(wzFilename, dwHandle, cbVerBuffer, pVerBuffer)) - { - FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); - } - - if (!::VerQueryValueW(pVerBuffer, L"\\", (void**)&pvsFileInfo, &cbFileInfo)) - { - FileExitOnLastErrorDebugTrace(hr, "failed to get version value for file: %ls", wzFilename); - } - - *pdwVerMajor = pvsFileInfo->dwFileVersionMS; - *pdwVerMinor = pvsFileInfo->dwFileVersionLS; - -LExit: - if (pVerBuffer) - { - ::GlobalFree(pVerBuffer); - } - return hr; -} - - -/******************************************************************* - FileVersionFromString - -*******************************************************************/ -extern "C" HRESULT DAPI FileVersionFromString( - __in_z LPCWSTR wzVersion, - __out DWORD* pdwVerMajor, - __out DWORD* pdwVerMinor - ) -{ - Assert(pdwVerMajor && pdwVerMinor); - - HRESULT hr = S_OK; - LPCWSTR pwz = wzVersion; - DWORD dw; - - *pdwVerMajor = 0; - *pdwVerMinor = 0; - - if ((L'v' == *pwz) || (L'V' == *pwz)) - { - ++pwz; - } - - dw = wcstoul(pwz, (WCHAR**)&pwz, 10); - if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) - { - *pdwVerMajor = dw << 16; - - if (!*pwz) - { - ExitFunction1(hr = S_OK); - } - ++pwz; - } - else - { - ExitFunction1(hr = S_FALSE); - } - - dw = wcstoul(pwz, (WCHAR**)&pwz, 10); - if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) - { - *pdwVerMajor |= dw; - - if (!*pwz) - { - ExitFunction1(hr = S_OK); - } - ++pwz; - } - else - { - ExitFunction1(hr = S_FALSE); - } - - dw = wcstoul(pwz, (WCHAR**)&pwz, 10); - if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) - { - *pdwVerMinor = dw << 16; - - if (!*pwz) - { - ExitFunction1(hr = S_OK); - } - ++pwz; - } - else - { - ExitFunction1(hr = S_FALSE); - } - - dw = wcstoul(pwz, (WCHAR**)&pwz, 10); - if (pwz && L'\0' == *pwz && dw < 0x10000) - { - *pdwVerMinor |= dw; - } - else - { - ExitFunction1(hr = S_FALSE); - } - -LExit: - return hr; -} - - -/******************************************************************* - FileVersionFromStringEx - -*******************************************************************/ -extern "C" HRESULT DAPI FileVersionFromStringEx( - __in_z LPCWSTR wzVersion, - __in SIZE_T cchVersion, - __out DWORD64* pqwVersion - ) -{ - Assert(wzVersion); - Assert(pqwVersion); - - HRESULT hr = S_OK; - LPCWSTR wzEnd = NULL; - LPCWSTR wzPartBegin = wzVersion; - LPCWSTR wzPartEnd = wzVersion; - DWORD iPart = 0; - USHORT us = 0; - DWORD64 qwVersion = 0; - - // get string length if not provided - if (0 >= cchVersion) - { - hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast(&cchVersion)); - FileExitOnRootFailure(hr, "Failed to get length of file version string: %ls", wzVersion); - - if (0 >= cchVersion) - { - ExitFunction1(hr = E_INVALIDARG); - } - } - - if ((L'v' == *wzVersion) || (L'V' == *wzVersion)) - { - ++wzVersion; - --cchVersion; - wzPartBegin = wzVersion; - wzPartEnd = wzVersion; - } - - // save end pointer - wzEnd = wzVersion + cchVersion; - - // loop through parts - for (;;) - { - if (4 <= iPart) - { - // error, too many parts - ExitFunction1(hr = E_INVALIDARG); - } - - // find end of part - while (wzPartEnd < wzEnd && L'.' != *wzPartEnd) - { - ++wzPartEnd; - } - if (wzPartBegin == wzPartEnd) - { - // error, empty part - ExitFunction1(hr = E_INVALIDARG); - } - - DWORD cchPart; - hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart); - FileExitOnFailure(hr, "Version number part was too long."); - - // parse version part - hr = StrStringToUInt16(wzPartBegin, cchPart, &us); - FileExitOnFailure(hr, "Failed to parse version number part."); - - // add part to qword version - qwVersion |= (DWORD64)us << ((3 - iPart) * 16); - - if (wzPartEnd >= wzEnd) - { - // end of string - break; - } - - wzPartBegin = ++wzPartEnd; // skip over separator - ++iPart; - } - - *pqwVersion = qwVersion; - -LExit: - return hr; -} - -/******************************************************************* - FileVersionFromStringEx - Formats the DWORD64 as a string version. - -*******************************************************************/ -extern "C" HRESULT DAPI FileVersionToStringEx( - __in DWORD64 qwVersion, - __out LPWSTR* psczVersion - ) -{ - HRESULT hr = S_OK; - WORD wMajor = 0; - WORD wMinor = 0; - WORD wBuild = 0; - WORD wRevision = 0; - - // Mask and shift each WORD for each field. - wMajor = (WORD)(qwVersion >> 48 & 0xffff); - wMinor = (WORD)(qwVersion >> 32 & 0xffff); - wBuild = (WORD)(qwVersion >> 16 & 0xffff); - wRevision = (WORD)(qwVersion & 0xffff); - - // Format and return the version string. - hr = StrAllocFormatted(psczVersion, L"%u.%u.%u.%u", wMajor, wMinor, wBuild, wRevision); - FileExitOnFailure(hr, "Failed to allocate and format the version number."); - -LExit: - return hr; -} - -/******************************************************************* - FileSetPointer - sets the file pointer. - -********************************************************************/ -extern "C" HRESULT DAPI FileSetPointer( - __in HANDLE hFile, - __in DWORD64 dw64Move, - __out_opt DWORD64* pdw64NewPosition, - __in DWORD dwMoveMethod - ) -{ - Assert(INVALID_HANDLE_VALUE != hFile); - - HRESULT hr = S_OK; - LARGE_INTEGER liMove; - LARGE_INTEGER liNewPosition; - - liMove.QuadPart = dw64Move; - if (!::SetFilePointerEx(hFile, liMove, &liNewPosition, dwMoveMethod)) - { - FileExitWithLastError(hr, "Failed to set file pointer."); - } - - if (pdw64NewPosition) - { - *pdw64NewPosition = liNewPosition.QuadPart; - } - -LExit: - return hr; -} - - -/******************************************************************* - FileSize - -********************************************************************/ -extern "C" HRESULT DAPI FileSize( - __in_z LPCWSTR pwzFileName, - __out LONGLONG* pllSize - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - - FileExitOnNull(pwzFileName, hr, E_INVALIDARG, "Attempted to check filename, but no filename was provided"); - - hFile = ::CreateFileW(pwzFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - if (INVALID_HANDLE_VALUE == hFile) - { - FileExitWithLastError(hr, "Failed to open file %ls while checking file size", pwzFileName); - } - - hr = FileSizeByHandle(hFile, pllSize); - FileExitOnFailure(hr, "Failed to check size of file %ls by handle", pwzFileName); - -LExit: - ReleaseFileHandle(hFile); - - return hr; -} - - -/******************************************************************* - FileSizeByHandle - -********************************************************************/ -extern "C" HRESULT DAPI FileSizeByHandle( - __in HANDLE hFile, - __out LONGLONG* pllSize - ) -{ - Assert(INVALID_HANDLE_VALUE != hFile && pllSize); - HRESULT hr = S_OK; - LARGE_INTEGER li; - - *pllSize = 0; - - if (!::GetFileSizeEx(hFile, &li)) - { - FileExitWithLastError(hr, "Failed to get size of file."); - } - - *pllSize = li.QuadPart; - -LExit: - return hr; -} - - -/******************************************************************* - FileExistsEx - -********************************************************************/ -extern "C" BOOL DAPI FileExistsEx( - __in_z LPCWSTR wzPath, - __out_opt DWORD *pdwAttributes - ) -{ - Assert(wzPath && *wzPath); - BOOL fExists = FALSE; - - WIN32_FIND_DATAW fd = { }; - HANDLE hff; - - if (INVALID_HANDLE_VALUE != (hff = ::FindFirstFileW(wzPath, &fd))) - { - ::FindClose(hff); - if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - { - if (pdwAttributes) - { - *pdwAttributes = fd.dwFileAttributes; - } - - fExists = TRUE; - } - } - - return fExists; -} - - -/******************************************************************* - FileExistsAfterRestart - checks that a file exists and will continue - to exist after restart. - -********************************************************************/ -extern "C" BOOL DAPI FileExistsAfterRestart( - __in_z LPCWSTR wzPath, - __out_opt DWORD *pdwAttributes - ) -{ - HRESULT hr = S_OK; - BOOL fExists = FALSE; - HKEY hkPendingFileRename = NULL; - LPWSTR* rgsczRenames = NULL; - DWORD cRenames = 0; - int nCompare = 0; - - fExists = FileExistsEx(wzPath, pdwAttributes); - if (fExists) - { - hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE, &hkPendingFileRename); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - FileExitOnFailure(hr, "Failed to open pending file rename registry key."); - - hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - FileExitOnFailure(hr, "Failed to read pending file renames."); - - // The pending file renames array is pairs of source and target paths. We only care - // about checking the source paths so skip the target paths (i += 2). - for (DWORD i = 0; i < cRenames; i += 2) - { - LPWSTR wzRename = rgsczRenames[i]; - if (wzRename && *wzRename) - { - // Skip the long path designator if present. - if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) - { - wzRename += 4; - } - - hr = PathCompare(wzPath, wzRename, &nCompare); - FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); - - if (CSTR_EQUAL == nCompare) - { - fExists = FALSE; - break; - } - } - } - } - -LExit: - ReleaseStrArray(rgsczRenames, cRenames); - ReleaseRegKey(hkPendingFileRename); - - return fExists; -} - - -/******************************************************************* - FileRemoveFromPendingRename - removes the file path from the pending - file rename list. - -********************************************************************/ -extern "C" HRESULT DAPI FileRemoveFromPendingRename( - __in_z LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - HKEY hkPendingFileRename = NULL; - LPWSTR* rgsczRenames = NULL; - DWORD cRenames = 0; - int nCompare = 0; - BOOL fRemoved = FALSE; - DWORD cNewRenames = 0; - - hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkPendingFileRename); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - FileExitOnFailure(hr, "Failed to open pending file rename registry key."); - - hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - FileExitOnFailure(hr, "Failed to read pending file renames."); - - // The pending file renames array is pairs of source and target paths. We only care - // about checking the source paths so skip the target paths (i += 2). - for (DWORD i = 0; i < cRenames; i += 2) - { - LPWSTR wzRename = rgsczRenames[i]; - if (wzRename && *wzRename) - { - // Skip the long path designator if present. - if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) - { - wzRename += 4; - } - - hr = PathCompare(wzPath, wzRename, &nCompare); - FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); - - // If we find our path in the list, null out the source and target slot and - // we'll compact the array next. - if (CSTR_EQUAL == nCompare) - { - ReleaseNullStr(rgsczRenames[i]); - ReleaseNullStr(rgsczRenames[i + 1]); - fRemoved = TRUE; - } - } - } - - if (fRemoved) - { - // Compact the array by removing any nulls. - for (DWORD i = 0; i < cRenames; ++i) - { - LPWSTR wzRename = rgsczRenames[i]; - if (wzRename) - { - rgsczRenames[cNewRenames] = wzRename; - ++cNewRenames; - } - } - - cRenames = cNewRenames; // ignore the pointers on the end of the array since an early index points to them already. - - // Write the new array back to the pending file rename key. - hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames); - FileExitOnFailure(hr, "Failed to update pending file renames."); - } - -LExit: - ReleaseStrArray(rgsczRenames, cRenames); - ReleaseRegKey(hkPendingFileRename); - - return hr; -} - - -/******************************************************************* - FileRead - read a file into memory - -********************************************************************/ -extern "C" HRESULT DAPI FileRead( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath - ) -{ - HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE); - return hr; -} - -/******************************************************************* - FileRead - read a file into memory with specified share mode - -********************************************************************/ -extern "C" HRESULT DAPI FileReadEx( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in DWORD dwShareMode - ) -{ - HRESULT hr = FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE, dwShareMode); - return hr; -} - -/******************************************************************* - FileReadUntil - read a file into memory with a maximum size - -********************************************************************/ -extern "C" HRESULT DAPI FileReadUntil( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in DWORD cbMaxRead - ) -{ - HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, cbMaxRead, FALSE); - return hr; -} - - -/******************************************************************* - FileReadPartial - read a portion of a file into memory - -********************************************************************/ -extern "C" HRESULT DAPI FileReadPartial( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in BOOL fSeek, - __in DWORD cbStartPosition, - __in DWORD cbMaxRead, - __in BOOL fPartialOK - ) -{ - return FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, fSeek, cbStartPosition, cbMaxRead, fPartialOK, FILE_SHARE_READ | FILE_SHARE_DELETE); -} - -/******************************************************************* - FileReadPartial - read a portion of a file into memory - (with specified share mode) -********************************************************************/ -extern "C" HRESULT DAPI FileReadPartialEx( - __deref_inout_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in BOOL fSeek, - __in DWORD cbStartPosition, - __in DWORD cbMaxRead, - __in BOOL fPartialOK, - __in DWORD dwShareMode - ) -{ - HRESULT hr = S_OK; - - UINT er = ERROR_SUCCESS; - HANDLE hFile = INVALID_HANDLE_VALUE; - LARGE_INTEGER liFileSize = { }; - DWORD cbData = 0; - BYTE* pbData = NULL; - - FileExitOnNull(pcbDest, hr, E_INVALIDARG, "Invalid argument pcbDest"); - FileExitOnNull(ppbDest, hr, E_INVALIDARG, "Invalid argument ppbDest"); - FileExitOnNull(wzSrcPath, hr, E_INVALIDARG, "Invalid argument wzSrcPath"); - FileExitOnNull(*wzSrcPath, hr, E_INVALIDARG, "*wzSrcPath is null"); - - hFile = ::CreateFileW(wzSrcPath, GENERIC_READ, dwShareMode, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - er = ::GetLastError(); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - FileExitOnWin32Error(er, hr, "Failed to open file: %ls", wzSrcPath); - } - - if (!::GetFileSizeEx(hFile, &liFileSize)) - { - FileExitWithLastError(hr, "Failed to get size of file: %ls", wzSrcPath); - } - - if (fSeek) - { - if (cbStartPosition > liFileSize.QuadPart) - { - hr = E_INVALIDARG; - FileExitOnFailure(hr, "Start position %d bigger than file '%ls' size %llu", cbStartPosition, wzSrcPath, liFileSize.QuadPart); - } - - DWORD dwErr = ::SetFilePointer(hFile, cbStartPosition, NULL, FILE_CURRENT); - if (INVALID_SET_FILE_POINTER == dwErr) - { - FileExitOnLastError(hr, "Failed to seek position %d", cbStartPosition); - } - } - else - { - cbStartPosition = 0; - } - - if (fPartialOK) - { - cbData = cbMaxRead; - } - else - { - cbData = liFileSize.LowPart - cbStartPosition; // should only need the low part because we cap at DWORD - if (cbMaxRead < liFileSize.QuadPart - cbStartPosition) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - FileExitOnRootFailure(hr, "Failed to load file: %ls, too large.", wzSrcPath); - } - } - - if (*ppbDest) - { - if (0 == cbData) - { - ReleaseNullMem(*ppbDest); - *pcbDest = 0; - ExitFunction1(hr = S_OK); - } - - LPVOID pv = MemReAlloc(*ppbDest, cbData, TRUE); - FileExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to re-allocate memory to read in file: %ls", wzSrcPath); - - pbData = static_cast(pv); - } - else - { - if (0 == cbData) - { - *pcbDest = 0; - ExitFunction1(hr = S_OK); - } - - pbData = static_cast(MemAlloc(cbData, TRUE)); - FileExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in file: %ls", wzSrcPath); - } - - DWORD cbTotalRead = 0; - DWORD cbRead = 0; - do - { - DWORD cbRemaining = 0; - hr = ::ULongSub(cbData, cbTotalRead, &cbRemaining); - FileExitOnFailure(hr, "Underflow calculating remaining buffer size."); - - if (!::ReadFile(hFile, pbData + cbTotalRead, cbRemaining, &cbRead, NULL)) - { - FileExitWithLastError(hr, "Failed to read from file: %ls", wzSrcPath); - } - - cbTotalRead += cbRead; - } while (cbRead); - - if (cbTotalRead != cbData) - { - hr = E_UNEXPECTED; - FileExitOnFailure(hr, "Failed to completely read file: %ls", wzSrcPath); - } - - *ppbDest = pbData; - pbData = NULL; - *pcbDest = cbData; - -LExit: - ReleaseMem(pbData); - ReleaseFile(hFile); - - return hr; -} - -extern "C" HRESULT DAPI FileReadHandle( - __in HANDLE hFile, - __in_bcount(cbDest) LPBYTE pbDest, - __in SIZE_T cbDest - ) -{ - HRESULT hr = 0; - DWORD cbDataRead = 0; - SIZE_T cbRemaining = cbDest; - SIZE_T cbTotal = 0; - - while (0 < cbRemaining) - { - if (!::ReadFile(hFile, pbDest + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataRead, NULL)) - { - DWORD er = ::GetLastError(); - if (ERROR_MORE_DATA == er) - { - hr = S_OK; - } - else - { - hr = HRESULT_FROM_WIN32(er); - } - FileExitOnRootFailure(hr, "Failed to read data from file handle."); - } - - cbRemaining -= cbDataRead; - cbTotal += cbDataRead; - } - -LExit: - return hr; -} - - -/******************************************************************* - FileWrite - write a file from memory - -********************************************************************/ -extern "C" HRESULT DAPI FileWrite( - __in_z LPCWSTR pwzFileName, - __in DWORD dwFlagsAndAttributes, - __in_bcount_opt(cbData) LPCBYTE pbData, - __in SIZE_T cbData, - __out_opt HANDLE* pHandle - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - - // Open the file - hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, dwFlagsAndAttributes, NULL); - FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file: %ls", pwzFileName); - - hr = FileWriteHandle(hFile, pbData, cbData); - FileExitOnFailure(hr, "Failed to write to file: %ls", pwzFileName); - - if (pHandle) - { - *pHandle = hFile; - hFile = INVALID_HANDLE_VALUE; - } - -LExit: - ReleaseFile(hFile); - - return hr; -} - - -/******************************************************************* - FileWriteHandle - write to a file handle from memory - -********************************************************************/ -extern "C" HRESULT DAPI FileWriteHandle( - __in HANDLE hFile, - __in_bcount_opt(cbData) LPCBYTE pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - DWORD cbDataWritten = 0; - SIZE_T cbTotal = 0; - SIZE_T cbRemaining = cbData; - - // Write out all of the data. - while (0 < cbRemaining) - { - if (!::WriteFile(hFile, pbData + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataWritten, NULL)) - { - FileExitOnLastError(hr, "Failed to write data to file handle."); - } - - cbRemaining -= cbDataWritten; - cbTotal += cbDataWritten; - } - -LExit: - return hr; -} - - -/******************************************************************* - FileCopyUsingHandles - -*******************************************************************/ -extern "C" HRESULT DAPI FileCopyUsingHandles( - __in HANDLE hSource, - __in HANDLE hTarget, - __in DWORD64 cbCopy, - __out_opt DWORD64* pcbCopied - ) -{ - HRESULT hr = S_OK; - DWORD64 cbTotalCopied = 0; - BYTE rgbData[4 * 1024]; - DWORD cbRead = 0; - - do - { - cbRead = static_cast((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied)); - if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL)) - { - FileExitWithLastError(hr, "Failed to read from source."); - } - - if (cbRead) - { - hr = FileWriteHandle(hTarget, rgbData, cbRead); - FileExitOnFailure(hr, "Failed to write to target."); - } - - cbTotalCopied += cbRead; - } while (cbTotalCopied < cbCopy && 0 != cbRead); - - if (pcbCopied) - { - *pcbCopied = cbTotalCopied; - } - -LExit: - return hr; -} - - -/******************************************************************* - FileCopyUsingHandlesWithProgress - -*******************************************************************/ -extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( - __in HANDLE hSource, - __in HANDLE hTarget, - __in DWORD64 cbCopy, - __in_opt LPPROGRESS_ROUTINE lpProgressRoutine, - __in_opt LPVOID lpData - ) -{ - HRESULT hr = S_OK; - DWORD64 cbTotalCopied = 0; - BYTE rgbData[64 * 1024]; - DWORD cbRead = 0; - - LARGE_INTEGER liSourceSize = { }; - LARGE_INTEGER liTotalCopied = { }; - LARGE_INTEGER liZero = { }; - DWORD dwResult = 0; - - hr = FileSizeByHandle(hSource, &liSourceSize.QuadPart); - FileExitOnFailure(hr, "Failed to get size of source."); - - if (0 < cbCopy && cbCopy < (DWORD64)liSourceSize.QuadPart) - { - liSourceSize.QuadPart = cbCopy; - } - - if (lpProgressRoutine) - { - dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_STREAM_SWITCH, hSource, hTarget, lpData); - switch (dwResult) - { - case PROGRESS_CONTINUE: - break; - - case PROGRESS_CANCEL: - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); - - case PROGRESS_STOP: - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); - - case PROGRESS_QUIET: - lpProgressRoutine = NULL; - break; - } - } - - // Set size of the target file. - ::SetFilePointerEx(hTarget, liSourceSize, NULL, FILE_BEGIN); - - if (!::SetEndOfFile(hTarget)) - { - FileExitWithLastError(hr, "Failed to set end of target file."); - } - - if (!::SetFilePointerEx(hTarget, liZero, NULL, FILE_BEGIN)) - { - FileExitWithLastError(hr, "Failed to reset target file pointer."); - } - - // Copy with progress. - while (0 == cbCopy || cbTotalCopied < cbCopy) - { - cbRead = static_cast((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied)); - if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL)) - { - FileExitWithLastError(hr, "Failed to read from source."); - } - - if (cbRead) - { - hr = FileWriteHandle(hTarget, rgbData, cbRead); - FileExitOnFailure(hr, "Failed to write to target."); - - cbTotalCopied += cbRead; - - if (lpProgressRoutine) - { - liTotalCopied.QuadPart = cbTotalCopied; - dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_CHUNK_FINISHED, hSource, hTarget, lpData); - switch (dwResult) - { - case PROGRESS_CONTINUE: - break; - - case PROGRESS_CANCEL: - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); - - case PROGRESS_STOP: - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); - - case PROGRESS_QUIET: - lpProgressRoutine = NULL; - break; - } - } - } - else - { - break; - } - } - -LExit: - return hr; -} - - -/******************************************************************* - FileEnsureCopy - -*******************************************************************/ -extern "C" HRESULT DAPI FileEnsureCopy( - __in_z LPCWSTR wzSource, - __in_z LPCWSTR wzTarget, - __in BOOL fOverwrite - ) -{ - HRESULT hr = S_OK; - DWORD er; - - // try to copy the file first - if (::CopyFileW(wzSource, wzTarget, !fOverwrite)) - { - ExitFunction(); // we're done - } - - er = ::GetLastError(); // check the error and do the right thing below - if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er)) - { - // if not overwriting this is an expected error - ExitFunction1(hr = S_FALSE); - } - else if (ERROR_PATH_NOT_FOUND == er) // if the path doesn't exist - { - // try to create the directory then do the copy - LPWSTR pwzLastSlash = NULL; - for (LPWSTR pwz = const_cast(wzTarget); *pwz; ++pwz) - { - if (*pwz == L'\\') - { - pwzLastSlash = pwz; - } - } - - if (pwzLastSlash) - { - *pwzLastSlash = L'\0'; // null terminate - hr = DirEnsureExists(wzTarget, NULL); - *pwzLastSlash = L'\\'; // now put the slash back - FileExitOnFailureDebugTrace(hr, "failed to create directory while copying file: '%ls' to: '%ls'", wzSource, wzTarget); - - // try to copy again - if (!::CopyFileW(wzSource, wzTarget, fOverwrite)) - { - FileExitOnLastErrorDebugTrace(hr, "failed to copy file: '%ls' to: '%ls'", wzSource, wzTarget); - } - } - else // no path was specified so just return the error - { - hr = HRESULT_FROM_WIN32(er); - } - } - else // unexpected error - { - hr = HRESULT_FROM_WIN32(er); - } - -LExit: - return hr; -} - - -/******************************************************************* - FileEnsureCopyWithRetry - -*******************************************************************/ -extern "C" HRESULT DAPI FileEnsureCopyWithRetry( - __in LPCWSTR wzSource, - __in LPCWSTR wzTarget, - __in BOOL fOverwrite, - __in DWORD cRetry, - __in DWORD dwWaitMilliseconds - ) -{ - AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry."); - - HRESULT hr = E_FAIL; - DWORD i = 0; - - for (i = 0; FAILED(hr) && i <= cRetry; ++i) - { - if (0 < i) - { - ::Sleep(dwWaitMilliseconds); - } - - hr = FileEnsureCopy(wzSource, wzTarget, fOverwrite); - if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr - || HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr || HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) == hr) - { - break; // no reason to retry these errors. - } - } - FileExitOnFailure(hr, "Failed to copy file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); - -LExit: - return hr; -} - - -/******************************************************************* - FileEnsureMove - -*******************************************************************/ -extern "C" HRESULT DAPI FileEnsureMove( - __in_z LPCWSTR wzSource, - __in_z LPCWSTR wzTarget, - __in BOOL fOverwrite, - __in BOOL fAllowCopy - ) -{ - HRESULT hr = S_OK; - DWORD er; - - DWORD dwFlags = 0; - - if (fOverwrite) - { - dwFlags |= MOVEFILE_REPLACE_EXISTING; - } - if (fAllowCopy) - { - dwFlags |= MOVEFILE_COPY_ALLOWED; - } - - // try to move the file first - if (::MoveFileExW(wzSource, wzTarget, dwFlags)) - { - ExitFunction(); // we're done - } - - er = ::GetLastError(); // check the error and do the right thing below - if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er)) - { - // if not overwriting this is an expected error - ExitFunction1(hr = S_FALSE); - } - else if (ERROR_FILE_NOT_FOUND == er) - { - // We are seeing some cases where ::MoveFileEx() says a file was not found - // but the source file is actually present. In that case, return path not - // found so we try to create the target path since that is most likely - // what is missing. Otherwise, the source file is missing and we're obviously - // not going to be recovering from that. - if (FileExistsEx(wzSource, NULL)) - { - er = ERROR_PATH_NOT_FOUND; - } - } - - // If the path doesn't exist, try to create the directory tree then do the move. - if (ERROR_PATH_NOT_FOUND == er) - { - LPWSTR pwzLastSlash = NULL; - for (LPWSTR pwz = const_cast(wzTarget); *pwz; ++pwz) - { - if (*pwz == L'\\') - { - pwzLastSlash = pwz; - } - } - - if (pwzLastSlash) - { - *pwzLastSlash = L'\0'; // null terminate - hr = DirEnsureExists(wzTarget, NULL); - *pwzLastSlash = L'\\'; // now put the slash back - FileExitOnFailureDebugTrace(hr, "failed to create directory while moving file: '%ls' to: '%ls'", wzSource, wzTarget); - - // try to move again - if (!::MoveFileExW(wzSource, wzTarget, dwFlags)) - { - FileExitOnLastErrorDebugTrace(hr, "failed to move file: '%ls' to: '%ls'", wzSource, wzTarget); - } - } - else // no path was specified so just return the error - { - hr = HRESULT_FROM_WIN32(er); - } - } - else // unexpected error - { - hr = HRESULT_FROM_WIN32(er); - } - -LExit: - return hr; -} - - -/******************************************************************* - FileEnsureMoveWithRetry - -*******************************************************************/ -extern "C" HRESULT DAPI FileEnsureMoveWithRetry( - __in LPCWSTR wzSource, - __in LPCWSTR wzTarget, - __in BOOL fOverwrite, - __in BOOL fAllowCopy, - __in DWORD cRetry, - __in DWORD dwWaitMilliseconds - ) -{ - AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry."); - - HRESULT hr = E_FAIL; - DWORD i = 0; - - for (i = 0; FAILED(hr) && i < cRetry + 1; ++i) - { - if (0 < i) - { - ::Sleep(dwWaitMilliseconds); - } - - hr = FileEnsureMove(wzSource, wzTarget, fOverwrite, fAllowCopy); - } - FileExitOnFailure(hr, "Failed to move file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); - -LExit: - return hr; -} - - -/******************************************************************* - FileCreateTemp - creates an empty temp file - - NOTE: uses ANSI functions internally so it is Win9x safe -********************************************************************/ -extern "C" HRESULT DAPI FileCreateTemp( - __in_z LPCWSTR wzPrefix, - __in_z LPCWSTR wzExtension, - __deref_opt_out_z LPWSTR* ppwzTempFile, - __out_opt HANDLE* phTempFile - ) -{ - Assert(wzPrefix && *wzPrefix); - HRESULT hr = S_OK; - LPSTR pszTempPath = NULL; - DWORD cchTempPath = MAX_PATH; - - HANDLE hTempFile = INVALID_HANDLE_VALUE; - LPSTR pszTempFile = NULL; - - int i = 0; - - hr = StrAnsiAlloc(&pszTempPath, cchTempPath); - FileExitOnFailure(hr, "failed to allocate memory for the temp path"); - ::GetTempPathA(cchTempPath, pszTempPath); - - for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) - { - hr = StrAnsiAllocFormatted(&pszTempFile, "%s%ls%05d.%ls", pszTempPath, wzPrefix, i, wzExtension); - FileExitOnFailure(hr, "failed to allocate memory for log file"); - - hTempFile = ::CreateFileA(pszTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hTempFile) - { - // if the file already exists, just try again - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) - { - hr = S_OK; - continue; - } - FileExitOnFailureDebugTrace(hr, "failed to create file: %hs", pszTempFile); - } - } - - if (ppwzTempFile) - { - hr = StrAllocStringAnsi(ppwzTempFile, pszTempFile, 0, CP_UTF8); - } - - if (phTempFile) - { - *phTempFile = hTempFile; - hTempFile = INVALID_HANDLE_VALUE; - } - -LExit: - ReleaseFile(hTempFile); - ReleaseStr(pszTempFile); - ReleaseStr(pszTempPath); - - return hr; -} - - -/******************************************************************* - FileCreateTempW - creates an empty temp file - -*******************************************************************/ -extern "C" HRESULT DAPI FileCreateTempW( - __in_z LPCWSTR wzPrefix, - __in_z LPCWSTR wzExtension, - __deref_opt_out_z LPWSTR* ppwzTempFile, - __out_opt HANDLE* phTempFile - ) -{ - Assert(wzPrefix && *wzPrefix); - HRESULT hr = E_FAIL; - - WCHAR wzTempPath[MAX_PATH]; - DWORD cchTempPath = countof(wzTempPath); - LPWSTR pwzTempFile = NULL; - - HANDLE hTempFile = INVALID_HANDLE_VALUE; - int i = 0; - - if (!::GetTempPathW(cchTempPath, wzTempPath)) - { - FileExitOnLastError(hr, "failed to get temp path"); - } - - for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) - { - hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", wzTempPath, wzPrefix, i, wzExtension); - FileExitOnFailure(hr, "failed to allocate memory for temp filename"); - - hTempFile = ::CreateFileW(pwzTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hTempFile) - { - // if the file already exists, just try again - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) - { - hr = S_OK; - continue; - } - FileExitOnFailureDebugTrace(hr, "failed to create file: %ls", pwzTempFile); - } - } - - if (phTempFile) - { - *phTempFile = hTempFile; - hTempFile = INVALID_HANDLE_VALUE; - } - - if (ppwzTempFile) - { - *ppwzTempFile = pwzTempFile; - pwzTempFile = NULL; - } - -LExit: - ReleaseFile(hTempFile); - ReleaseStr(pwzTempFile); - - return hr; -} - - -/******************************************************************* - FileIsSame - -********************************************************************/ -extern "C" HRESULT DAPI FileIsSame( - __in_z LPCWSTR wzFile1, - __in_z LPCWSTR wzFile2, - __out LPBOOL lpfSameFile - ) -{ - HRESULT hr = S_OK; - HANDLE hFile1 = NULL; - HANDLE hFile2 = NULL; - BY_HANDLE_FILE_INFORMATION fileInfo1 = { }; - BY_HANDLE_FILE_INFORMATION fileInfo2 = { }; - - hFile1 = ::CreateFileW(wzFile1, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - FileExitOnInvalidHandleWithLastError(hFile1, hr, "Failed to open file 1. File = '%ls'", wzFile1); - - hFile2 = ::CreateFileW(wzFile2, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - FileExitOnInvalidHandleWithLastError(hFile2, hr, "Failed to open file 2. File = '%ls'", wzFile2); - - if (!::GetFileInformationByHandle(hFile1, &fileInfo1)) - { - FileExitWithLastError(hr, "Failed to get information for file 1. File = '%ls'", wzFile1); - } - - if (!::GetFileInformationByHandle(hFile2, &fileInfo2)) - { - FileExitWithLastError(hr, "Failed to get information for file 2. File = '%ls'", wzFile2); - } - - *lpfSameFile = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber && - fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh && - fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow ? TRUE : FALSE; - -LExit: - ReleaseFile(hFile1); - ReleaseFile(hFile2); - - return hr; -} - -/******************************************************************* - FileEnsureDelete - deletes a file, first removing read-only, - hidden, or system attributes if necessary. -********************************************************************/ -extern "C" HRESULT DAPI FileEnsureDelete( - __in_z LPCWSTR wzFile - ) -{ - HRESULT hr = S_OK; - - DWORD dwAttrib = INVALID_FILE_ATTRIBUTES; - if (FileExistsEx(wzFile, &dwAttrib)) - { - if (dwAttrib & FILE_ATTRIBUTE_READONLY || dwAttrib & FILE_ATTRIBUTE_HIDDEN || dwAttrib & FILE_ATTRIBUTE_SYSTEM) - { - if (!::SetFileAttributesW(wzFile, FILE_ATTRIBUTE_NORMAL)) - { - FileExitOnLastError(hr, "Failed to remove attributes from file: %ls", wzFile); - } - } - - if (!::DeleteFileW(wzFile)) - { - FileExitOnLastError(hr, "Failed to delete file: %ls", wzFile); - } - } - -LExit: - return hr; -} - -/******************************************************************* - FileGetTime - Gets the file time of a specified file -********************************************************************/ -extern "C" HRESULT DAPI FileGetTime( - __in_z LPCWSTR wzFile, - __out_opt LPFILETIME lpCreationTime, - __out_opt LPFILETIME lpLastAccessTime, - __out_opt LPFILETIME lpLastWriteTime - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = NULL; - - hFile = ::CreateFileW(wzFile, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); - FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); - - if (!::GetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) - { - FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); - } - -LExit: - ReleaseFile(hFile); - return hr; -} - -/******************************************************************* - FileSetTime - Sets the file time of a specified file -********************************************************************/ -extern "C" HRESULT DAPI FileSetTime( - __in_z LPCWSTR wzFile, - __in_opt const FILETIME *lpCreationTime, - __in_opt const FILETIME *lpLastAccessTime, - __in_opt const FILETIME *lpLastWriteTime - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = NULL; - - hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); - FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); - - if (!::SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) - { - FileExitWithLastError(hr, "Failed to set file time for file. File = '%ls'", wzFile); - } - -LExit: - ReleaseFile(hFile); - return hr; -} - -/******************************************************************* - FileReSetTime - ReSets a file's last acess and modified time to the - creation time of the file -********************************************************************/ -extern "C" HRESULT DAPI FileResetTime( - __in_z LPCWSTR wzFile - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = NULL; - FILETIME ftCreateTime; - - hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); - - if (!::GetFileTime(hFile, &ftCreateTime, NULL, NULL)) - { - FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); - } - - if (!::SetFileTime(hFile, NULL, NULL, &ftCreateTime)) - { - FileExitWithLastError(hr, "Failed to reset file time for file. File = '%ls'", wzFile); - } - -LExit: - ReleaseFile(hFile); - return hr; -} - - -/******************************************************************* - FileExecutableArchitecture - -*******************************************************************/ -extern "C" HRESULT DAPI FileExecutableArchitecture( - __in_z LPCWSTR wzFile, - __out FILE_ARCHITECTURE *pArchitecture - ) -{ - HRESULT hr = S_OK; - - HANDLE hFile = INVALID_HANDLE_VALUE; - DWORD cbRead = 0; - IMAGE_DOS_HEADER DosImageHeader = { }; - IMAGE_NT_HEADERS NtImageHeader = { }; - - hFile = ::CreateFileW(wzFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - if (hFile == INVALID_HANDLE_VALUE) - { - FileExitWithLastError(hr, "Failed to open file: %ls", wzFile); - } - - if (!::ReadFile(hFile, &DosImageHeader, sizeof(DosImageHeader), &cbRead, NULL)) - { - FileExitWithLastError(hr, "Failed to read DOS header from file: %ls", wzFile); - } - - if (DosImageHeader.e_magic != IMAGE_DOS_SIGNATURE) - { - hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - FileExitOnRootFailure(hr, "Read invalid DOS header from file: %ls", wzFile); - } - - if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hFile, DosImageHeader.e_lfanew, NULL, FILE_BEGIN)) - { - FileExitWithLastError(hr, "Failed to seek the NT header in file: %ls", wzFile); - } - - if (!::ReadFile(hFile, &NtImageHeader, sizeof(NtImageHeader), &cbRead, NULL)) - { - FileExitWithLastError(hr, "Failed to read NT header from file: %ls", wzFile); - } - - if (NtImageHeader.Signature != IMAGE_NT_SIGNATURE) - { - hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - FileExitOnRootFailure(hr, "Read invalid NT header from file: %ls", wzFile); - } - - if (IMAGE_SUBSYSTEM_NATIVE == NtImageHeader.OptionalHeader.Subsystem || - IMAGE_SUBSYSTEM_WINDOWS_GUI == NtImageHeader.OptionalHeader.Subsystem || - IMAGE_SUBSYSTEM_WINDOWS_CUI == NtImageHeader.OptionalHeader.Subsystem) - { - switch (NtImageHeader.FileHeader.Machine) - { - case IMAGE_FILE_MACHINE_I386: - *pArchitecture = FILE_ARCHITECTURE_X86; - break; - case IMAGE_FILE_MACHINE_IA64: - *pArchitecture = FILE_ARCHITECTURE_IA64; - break; - case IMAGE_FILE_MACHINE_AMD64: - *pArchitecture = FILE_ARCHITECTURE_X64; - break; - default: - hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - break; - } - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - } - FileExitOnFailure(hr, "Unexpected subsystem: %d machine type: %d specified in NT header from file: %ls", NtImageHeader.OptionalHeader.Subsystem, NtImageHeader.FileHeader.Machine, wzFile); - -LExit: - if (hFile != INVALID_HANDLE_VALUE) - { - ::CloseHandle(hFile); - } - - return hr; -} - -/******************************************************************* - FileToString - -*******************************************************************/ -extern "C" HRESULT DAPI FileToString( - __in_z LPCWSTR wzFile, - __out LPWSTR *psczString, - __out_opt FILE_ENCODING *pfeEncoding - ) -{ - HRESULT hr = S_OK; - BYTE *pbFullFileBuffer = NULL; - SIZE_T cbFullFileBuffer = 0; - BOOL fNullCharFound = FALSE; - LPWSTR sczFileText = NULL; - - // Check if the file is ANSI - hr = FileRead(&pbFullFileBuffer, &cbFullFileBuffer, wzFile); - FileExitOnFailure(hr, "Failed to read file: %ls", wzFile); - - if (0 == cbFullFileBuffer) - { - *psczString = NULL; - ExitFunction1(hr = S_OK); - } - - // UTF-8 BOM - if (cbFullFileBuffer > sizeof(UTF8BOM) && 0 == memcmp(pbFullFileBuffer, UTF8BOM, sizeof(UTF8BOM))) - { - if (pfeEncoding) - { - *pfeEncoding = FILE_ENCODING_UTF8_WITH_BOM; - } - - hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast(pbFullFileBuffer + 3), cbFullFileBuffer - 3, CP_UTF8); - FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8 as its BOM indicated", wzFile); - - *psczString = sczFileText; - sczFileText = NULL; - } - // UTF-16 BOM, little endian (windows regular UTF-16) - else if (cbFullFileBuffer > sizeof(UTF16BOM) && 0 == memcmp(pbFullFileBuffer, UTF16BOM, sizeof(UTF16BOM))) - { - if (pfeEncoding) - { - *pfeEncoding = FILE_ENCODING_UTF16_WITH_BOM; - } - - hr = StrAllocString(psczString, reinterpret_cast(pbFullFileBuffer + 2), (cbFullFileBuffer - 2) / sizeof(WCHAR)); - FileExitOnFailure(hr, "Failed to allocate copy of string"); - } - // No BOM, let's try to detect - else - { - for (DWORD i = 0; i < cbFullFileBuffer; ++i) - { - if (pbFullFileBuffer[i] == '\0') - { - fNullCharFound = TRUE; - break; - } - } - - if (!fNullCharFound) - { - if (pfeEncoding) - { - *pfeEncoding = FILE_ENCODING_UTF8; - } - - hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast(pbFullFileBuffer), cbFullFileBuffer, CP_UTF8); - if (FAILED(hr)) - { - if (E_OUTOFMEMORY == hr) - { - FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8", wzFile); - } - } - else - { - *psczString = sczFileText; - sczFileText = NULL; - } - } - else if (NULL == *psczString) - { - if (pfeEncoding) - { - *pfeEncoding = FILE_ENCODING_UTF16; - } - - hr = StrAllocString(psczString, reinterpret_cast(pbFullFileBuffer), cbFullFileBuffer / sizeof(WCHAR)); - FileExitOnFailure(hr, "Failed to allocate copy of string"); - } - } - -LExit: - ReleaseStr(sczFileText); - ReleaseMem(pbFullFileBuffer); - - return hr; -} - -/******************************************************************* - FileFromString - -*******************************************************************/ -extern "C" HRESULT DAPI FileFromString( - __in_z LPCWSTR wzFile, - __in DWORD dwFlagsAndAttributes, - __in_z LPCWSTR sczString, - __in FILE_ENCODING feEncoding - ) -{ - HRESULT hr = S_OK; - LPSTR sczUtf8String = NULL; - BYTE *pbFullFileBuffer = NULL; - const BYTE *pcbFullFileBuffer = NULL; - SIZE_T cbFullFileBuffer = 0; - SIZE_T cbStrLen = 0; - - switch (feEncoding) - { - case FILE_ENCODING_UTF8: - hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); - FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); - - hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast(&cbFullFileBuffer)); - FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string"); - - pcbFullFileBuffer = reinterpret_cast(sczUtf8String); - break; - case FILE_ENCODING_UTF8_WITH_BOM: - hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); - FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); - - hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); - FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string"); - - cbFullFileBuffer = sizeof(UTF8BOM) + cbStrLen; - - pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); - FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); - - memcpy_s(pbFullFileBuffer, sizeof(UTF8BOM), UTF8BOM, sizeof(UTF8BOM)); - memcpy_s(pbFullFileBuffer + sizeof(UTF8BOM), cbStrLen, sczUtf8String, cbStrLen); - pcbFullFileBuffer = pbFullFileBuffer; - break; - case FILE_ENCODING_UTF16: - hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); - FileExitOnRootFailure(hr, "Failed to get length of string"); - - cbFullFileBuffer = cbStrLen * sizeof(WCHAR); - pcbFullFileBuffer = reinterpret_cast(sczString); - break; - case FILE_ENCODING_UTF16_WITH_BOM: - hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); - FileExitOnRootFailure(hr, "Failed to get length of string"); - - cbStrLen *= sizeof(WCHAR); - cbFullFileBuffer = sizeof(UTF16BOM) + cbStrLen; - - pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); - FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); - - memcpy_s(pbFullFileBuffer, sizeof(UTF16BOM), UTF16BOM, sizeof(UTF16BOM)); - memcpy_s(pbFullFileBuffer + sizeof(UTF16BOM), cbStrLen, sczString, cbStrLen); - pcbFullFileBuffer = pbFullFileBuffer; - break; - } - - hr = FileWrite(wzFile, dwFlagsAndAttributes, pcbFullFileBuffer, cbFullFileBuffer, NULL); - FileExitOnFailure(hr, "Failed to write file from string to: %ls", wzFile); - -LExit: - ReleaseStr(sczUtf8String); - ReleaseMem(pbFullFileBuffer); - - return hr; -} diff --git a/src/dutil/gdiputil.cpp b/src/dutil/gdiputil.cpp deleted file mode 100644 index b5a0087c..00000000 --- a/src/dutil/gdiputil.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace Gdiplus; - - -// Exit macros -#define GdipExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) -#define GdipExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) -#define GdipExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) -#define GdipExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) -#define GdipExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) -#define GdipExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) -#define GdipExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GDIPUTIL, p, x, e, s, __VA_ARGS__) -#define GdipExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, p, x, s, __VA_ARGS__) -#define GdipExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, p, x, e, s, __VA_ARGS__) -#define GdipExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, p, x, s, __VA_ARGS__) -#define GdipExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_GDIPUTIL, e, x, s, __VA_ARGS__) -#define GdipExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_GDIPUTIL, g, x, s, __VA_ARGS__) - -/******************************************************************** - GdipInitialize - initializes GDI+. - - Note: pOutput must be non-NULL if pInput->SuppressBackgroundThread - is TRUE. See GdiplusStartup() for more information. -********************************************************************/ -extern "C" HRESULT DAPI GdipInitialize( - __in const Gdiplus::GdiplusStartupInput* pInput, - __out ULONG_PTR* pToken, - __out_opt Gdiplus::GdiplusStartupOutput *pOutput - ) -{ - AssertSz(!pInput->SuppressBackgroundThread || pOutput, "pOutput required if background thread suppressed."); - - HRESULT hr = S_OK; - Status status = Ok; - - status = GdiplusStartup(pToken, pInput, pOutput); - GdipExitOnGdipFailure(status, hr, "Failed to initialize GDI+."); - -LExit: - return hr; -} - -/******************************************************************** - GdipUninitialize - uninitializes GDI+. - -********************************************************************/ -extern "C" void DAPI GdipUninitialize( - __in ULONG_PTR token - ) -{ - GdiplusShutdown(token); -} - -/******************************************************************** - GdipBitmapFromResource - read a GDI+ image out of a resource stream - -********************************************************************/ -extern "C" HRESULT DAPI GdipBitmapFromResource( - __in_opt HINSTANCE hinst, - __in_z LPCSTR szId, - __out Bitmap **ppBitmap - ) -{ - HRESULT hr = S_OK; - LPVOID pvData = NULL; - DWORD cbData = 0; - HGLOBAL hGlobal = NULL;; - LPVOID pv = NULL; - IStream *pStream = NULL; - Bitmap *pBitmap = NULL; - Status gs = Ok; - - hr = ResReadData(hinst, szId, &pvData, &cbData); - GdipExitOnFailure(hr, "Failed to load GDI+ bitmap from resource."); - - // Have to copy the fixed resource data into moveable (heap) memory - // since that's what GDI+ expects. - hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, cbData); - GdipExitOnNullWithLastError(hGlobal, hr, "Failed to allocate global memory."); - - pv = ::GlobalLock(hGlobal); - GdipExitOnNullWithLastError(pv, hr, "Failed to lock global memory."); - - memcpy(pv, pvData, cbData); - - ::GlobalUnlock(pv); // no point taking any more memory than we have already - pv = NULL; - - hr = ::CreateStreamOnHGlobal(hGlobal, TRUE, &pStream); - GdipExitOnFailure(hr, "Failed to allocate stream from global memory."); - - hGlobal = NULL; // we gave the global memory to the stream object so it will close it - - pBitmap = Bitmap::FromStream(pStream); - GdipExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from stream."); - - gs = pBitmap->GetLastStatus(); - GdipExitOnGdipFailure(gs, hr, "Failed to load bitmap from stream."); - - *ppBitmap = pBitmap; - pBitmap = NULL; - -LExit: - if (pBitmap) - { - delete pBitmap; - } - - ReleaseObject(pStream); - - if (pv) - { - ::GlobalUnlock(pv); - } - - if (hGlobal) - { - ::GlobalFree(hGlobal); - } - - return hr; -} - - -/******************************************************************** - GdipBitmapFromFile - read a GDI+ image from a file. - -********************************************************************/ -extern "C" HRESULT DAPI GdipBitmapFromFile( - __in_z LPCWSTR wzFileName, - __out Bitmap **ppBitmap - ) -{ - HRESULT hr = S_OK; - Bitmap *pBitmap = NULL; - Status gs = Ok; - - GdipExitOnNull(ppBitmap, hr, E_INVALIDARG, "Invalid null wzFileName"); - - pBitmap = Bitmap::FromFile(wzFileName); - GdipExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from file."); - - gs = pBitmap->GetLastStatus(); - GdipExitOnGdipFailure(gs, hr, "Failed to load bitmap from file: %ls", wzFileName); - - *ppBitmap = pBitmap; - pBitmap = NULL; - -LExit: - if (pBitmap) - { - delete pBitmap; - } - - return hr; -} - - -HRESULT DAPI GdipHresultFromStatus( - __in Gdiplus::Status gs - ) -{ - switch (gs) - { - case Ok: - return S_OK; - - case GenericError: - return E_FAIL; - - case InvalidParameter: - return E_INVALIDARG; - - case OutOfMemory: - return E_OUTOFMEMORY; - - case ObjectBusy: - return HRESULT_FROM_WIN32(ERROR_BUSY); - - case InsufficientBuffer: - return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - - case NotImplemented: - return E_NOTIMPL; - - case Win32Error: - return E_FAIL; - - case WrongState: - return E_FAIL; - - case Aborted: - return E_ABORT; - - case FileNotFound: - return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - - case ValueOverflow: - return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); - - case AccessDenied: - return E_ACCESSDENIED; - - case UnknownImageFormat: - return HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - - case FontFamilyNotFound: __fallthrough; - case FontStyleNotFound: __fallthrough; - case NotTrueTypeFont: - return E_UNEXPECTED; - - case UnsupportedGdiplusVersion: - return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); - - case GdiplusNotInitialized: - return E_UNEXPECTED; - - case PropertyNotFound: __fallthrough; - case PropertyNotSupported: - return E_FAIL; - } - - return E_UNEXPECTED; -} diff --git a/src/dutil/guidutil.cpp b/src/dutil/guidutil.cpp deleted file mode 100644 index 204c9af2..00000000 --- a/src/dutil/guidutil.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define GuidExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) -#define GuidExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) -#define GuidExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) -#define GuidExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) -#define GuidExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) -#define GuidExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) -#define GuidExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__) -#define GuidExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__) -#define GuidExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__) -#define GuidExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__) -#define GuidExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_GUIDUTIL, e, x, s, __VA_ARGS__) -#define GuidExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_GUIDUTIL, g, x, s, __VA_ARGS__) - -extern "C" HRESULT DAPI GuidFixedCreate( - _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid - ) -{ - HRESULT hr = S_OK; - UUID guid = { }; - - hr = HRESULT_FROM_RPC(::UuidCreate(&guid)); - GuidExitOnFailure(hr, "UuidCreate failed."); - - if (!::StringFromGUID2(guid, wzGuid, GUID_STRING_LENGTH)) - { - hr = E_OUTOFMEMORY; - GuidExitOnRootFailure(hr, "Failed to convert guid into string."); - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI GuidCreate( - __deref_out_z LPWSTR* psczGuid - ) -{ - HRESULT hr = S_OK; - - hr = StrAlloc(psczGuid, GUID_STRING_LENGTH); - GuidExitOnFailure(hr, "Failed to allocate space for guid"); - - hr = GuidFixedCreate(*psczGuid); - GuidExitOnFailure(hr, "Failed to create new guid."); - -LExit: - return hr; -} diff --git a/src/dutil/iis7util.cpp b/src/dutil/iis7util.cpp deleted file mode 100644 index d0a0b000..00000000 --- a/src/dutil/iis7util.cpp +++ /dev/null @@ -1,535 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" -#include "iis7util.h" - - -// Exit macros -#define IisExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) -#define IisExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) -#define IisExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) -#define IisExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) -#define IisExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) -#define IisExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) -#define IisExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_IIS7UTIL, p, x, e, s, __VA_ARGS__) -#define IisExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, p, x, s, __VA_ARGS__) -#define IisExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, p, x, e, s, __VA_ARGS__) -#define IisExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, p, x, s, __VA_ARGS__) -#define IisExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_IIS7UTIL, e, x, s, __VA_ARGS__) -#define IisExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_IIS7UTIL, g, x, s, __VA_ARGS__) - -#define ISSTRINGVARIANT(vt) (VT_BSTR == vt || VT_LPWSTR == vt) - -extern "C" HRESULT DAPI Iis7PutPropertyVariant( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in VARIANT vtPut - ) -{ - HRESULT hr = S_OK; - IAppHostProperty *pProperty = NULL; - BSTR bstrPropName = NULL; - - bstrPropName = ::SysAllocString(wzPropName); - IisExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); - - hr = pElement->GetPropertyByName(bstrPropName, &pProperty); - IisExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); - - hr = pProperty->put_Value(vtPut); - IisExitOnFailure(hr, "Failed to set property value for %ls", wzPropName); - -LExit: - ReleaseBSTR(bstrPropName); - // caller responsible for cleaning up variant vtPut - ReleaseObject(pProperty); - - return hr; -} - -extern "C" HRESULT DAPI Iis7PutPropertyString( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in LPCWSTR wzString - ) -{ - HRESULT hr = S_OK; - VARIANT vtPut; - - ::VariantInit(&vtPut); - vtPut.vt = VT_BSTR; - vtPut.bstrVal = ::SysAllocString(wzString); - IisExitOnNull(vtPut.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); - - hr = Iis7PutPropertyVariant(pElement, wzPropName, vtPut); - -LExit: - ReleaseVariant(vtPut); - - return hr; -} - -extern "C" HRESULT DAPI Iis7PutPropertyInteger( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in DWORD dValue - ) -{ - VARIANT vtPut; - - ::VariantInit(&vtPut); - vtPut.vt = VT_I4; - vtPut.lVal = dValue; - return Iis7PutPropertyVariant(pElement, wzPropName, vtPut); -} - -extern "C" HRESULT DAPI Iis7PutPropertyBool( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in BOOL fValue) -{ - VARIANT vtPut; - - ::VariantInit(&vtPut); - vtPut.vt = VT_BOOL; - vtPut.boolVal = (fValue == FALSE) ? VARIANT_FALSE : VARIANT_TRUE; - return Iis7PutPropertyVariant(pElement, wzPropName, vtPut); -} - -extern "C" HRESULT DAPI Iis7GetPropertyVariant( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in VARIANT* vtGet - ) -{ - HRESULT hr = S_OK; - IAppHostProperty *pProperty = NULL; - BSTR bstrPropName = NULL; - - bstrPropName = ::SysAllocString(wzPropName); - IisExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); - - hr = pElement->GetPropertyByName(bstrPropName, &pProperty); - IisExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); - - hr = pProperty->get_Value(vtGet); - IisExitOnFailure(hr, "Failed to get property value for %ls", wzPropName); - -LExit: - ReleaseBSTR(bstrPropName); - // caller responsible for cleaning up variant vtGet - ReleaseObject(pProperty); - - return hr; -} - -extern "C" HRESULT DAPI Iis7GetPropertyString( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in LPWSTR* psczGet - ) -{ - HRESULT hr = S_OK; - VARIANT vtGet; - - ::VariantInit(&vtGet); - hr = Iis7GetPropertyVariant(pElement, wzPropName, &vtGet); - IisExitOnFailure(hr, "Failed to get iis7 property variant with name: %ls", wzPropName); - - if (!ISSTRINGVARIANT(vtGet.vt)) - { - hr = E_UNEXPECTED; - IisExitOnFailure(hr, "Tried to get property as a string, but type was %d instead.", vtGet.vt); - } - - hr = StrAllocString(psczGet, vtGet.bstrVal, 0); - -LExit: - ReleaseVariant(vtGet); - - return hr; -} - -BOOL DAPI CompareVariantDefault( - __in VARIANT* pVariant1, - __in VARIANT* pVariant2 - ) -{ - BOOL fEqual = FALSE; - - switch (pVariant1->vt) - { - // VarCmp doesn't work for unsigned ints - // We'd like to allow signed/unsigned comparison as well since - // IIS doesn't document variant type for integer fields - case VT_I1: - case VT_UI1: - if (VT_I1 == pVariant2->vt || VT_UI1 == pVariant2->vt) - { - fEqual = pVariant1->bVal == pVariant2->bVal; - } - break; - case VT_I2: - case VT_UI2: - if (VT_I2 == pVariant2->vt || VT_UI2 == pVariant2->vt) - { - fEqual = pVariant1->uiVal == pVariant2->uiVal; - } - break; - case VT_UI4: - case VT_I4: - if (VT_I4 == pVariant2->vt || VT_UI4 == pVariant2->vt) - { - fEqual = pVariant1->ulVal == pVariant2->ulVal; - } - break; - case VT_UI8: - case VT_I8: - if (VT_I8 == pVariant2->vt || VT_UI8 == pVariant2->vt) - { - fEqual = pVariant1->ullVal == pVariant2->ullVal; - } - break; - default: - fEqual = VARCMP_EQ == ::VarCmp(pVariant1, - pVariant2, - LOCALE_INVARIANT, - NORM_IGNORECASE); - } - - return fEqual; -} - -BOOL DAPI CompareVariantPath( - __in VARIANT* pVariant1, - __in VARIANT* pVariant2 - ) -{ - HRESULT hr = S_OK; - BOOL fEqual = FALSE; - LPWSTR wzValue1 = NULL; - LPWSTR wzValue2 = NULL; - - if (ISSTRINGVARIANT(pVariant1->vt)) - { - hr = PathExpand(&wzValue1, pVariant1->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - IisExitOnFailure(hr, "Failed to expand path %ls", pVariant1->bstrVal); - } - - if (ISSTRINGVARIANT(pVariant2->vt)) - { - hr = PathExpand(&wzValue2, pVariant2->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - IisExitOnFailure(hr, "Failed to expand path %ls", pVariant2->bstrVal); - } - - fEqual = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzValue1, -1, wzValue2, -1); - -LExit: - ReleaseNullStr(wzValue1); - ReleaseNullStr(wzValue2); - return fEqual; -} - -BOOL DAPI IsMatchingAppHostElementCallback( - __in IAppHostElement *pElement, - __in_bcount(sizeof(IIS7_APPHOSTELEMENTCOMPARISON)) LPVOID pContext - ) -{ - IIS7_APPHOSTELEMENTCOMPARISON* pComparison = (IIS7_APPHOSTELEMENTCOMPARISON*) pContext; - - return Iis7IsMatchingAppHostElement(pElement, pComparison); -} - -extern "C" BOOL DAPI Iis7IsMatchingAppHostElement( - __in IAppHostElement *pElement, - __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison - ) -{ - HRESULT hr = S_OK; - BOOL fResult = FALSE; - IAppHostProperty *pProperty = NULL; - BSTR bstrElementName = NULL; - - VARIANT vPropValue; - ::VariantInit(&vPropValue); - - // Use the default comparator if a comparator is not specified - VARIANTCOMPARATORPROC pComparator = pComparison->pComparator ? pComparison->pComparator : CompareVariantDefault; - - hr = pElement->get_Name(&bstrElementName); - IisExitOnFailure(hr, "Failed to get name of element"); - if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pComparison->sczElementName, -1, bstrElementName, -1)) - { - ExitFunction(); - } - - hr = Iis7GetPropertyVariant(pElement, pComparison->sczAttributeName, &vPropValue); - IisExitOnFailure(hr, "Failed to get value of %ls attribute of %ls element", pComparison->sczAttributeName, pComparison->sczElementName); - - if (TRUE == pComparator(pComparison->pvAttributeValue, &vPropValue)) - { - fResult = TRUE; - } - -LExit: - ReleaseBSTR(bstrElementName); - ReleaseVariant(vPropValue); - ReleaseObject(pProperty); - - return fResult; -} - -BOOL DAPI IsMatchingAppHostMethod( - __in IAppHostMethod *pMethod, - __in LPCWSTR wzMethodName - ) -{ - HRESULT hr = S_OK; - BOOL fResult = FALSE; - BSTR bstrName = NULL; - - hr = pMethod->get_Name(&bstrName); - IisExitOnFailure(hr, "Failed to get name of element"); - - Assert(bstrName); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzMethodName, -1, bstrName, -1)) - { - fResult = TRUE; - } - -LExit: - ReleaseBSTR(bstrName); - - return fResult; -} - -extern "C" HRESULT DAPI Iis7FindAppHostElementPath( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in LPCWSTR wzAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ) -{ - HRESULT hr = S_OK; - IIS7_APPHOSTELEMENTCOMPARISON comparison = { }; - VARIANT vtValue = { }; - ::VariantInit(&vtValue); - - vtValue.vt = VT_BSTR; - vtValue.bstrVal = ::SysAllocString(wzAttributeValue); - IisExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); - - comparison.sczElementName = wzElementName; - comparison.sczAttributeName = wzAttributeName; - comparison.pvAttributeValue = &vtValue; - comparison.pComparator = CompareVariantPath; - - hr = Iis7EnumAppHostElements(pCollection, - IsMatchingAppHostElementCallback, - &comparison, - ppElement, - pdwIndex); - -LExit: - ReleaseVariant(vtValue); - - return hr; -} - -extern "C" HRESULT DAPI Iis7FindAppHostElementString( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in LPCWSTR wzAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ) -{ - HRESULT hr = S_OK; - VARIANT vtValue; - ::VariantInit(&vtValue); - - vtValue.vt = VT_BSTR; - vtValue.bstrVal = ::SysAllocString(wzAttributeValue); - IisExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); - - hr = Iis7FindAppHostElementVariant(pCollection, - wzElementName, - wzAttributeName, - &vtValue, - ppElement, - pdwIndex); - -LExit: - ReleaseVariant(vtValue); - - return hr; -} - -extern "C" HRESULT DAPI Iis7FindAppHostElementInteger( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in DWORD dwAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ) -{ - HRESULT hr = S_OK; - VARIANT vtValue; - ::VariantInit(&vtValue); - - vtValue.vt = VT_UI4; - vtValue.ulVal = dwAttributeValue; - - hr = Iis7FindAppHostElementVariant(pCollection, - wzElementName, - wzAttributeName, - &vtValue, - ppElement, - pdwIndex); - - ReleaseVariant(vtValue); - - return hr; -} - -extern "C" HRESULT DAPI Iis7FindAppHostElementVariant( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in VARIANT* pvAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ) -{ - IIS7_APPHOSTELEMENTCOMPARISON comparison = { }; - comparison.sczElementName = wzElementName; - comparison.sczAttributeName = wzAttributeName; - comparison.pvAttributeValue = pvAttributeValue; - comparison.pComparator = CompareVariantDefault; - - return Iis7EnumAppHostElements(pCollection, - IsMatchingAppHostElementCallback, - &comparison, - ppElement, - pdwIndex); -} - -extern "C" HRESULT DAPI Iis7EnumAppHostElements( - __in IAppHostElementCollection *pCollection, - __in ENUMAPHOSTELEMENTPROC pCallback, - __in LPVOID pContext, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ) -{ - HRESULT hr = S_OK; - IAppHostElement *pElement = NULL; - DWORD dwElements = 0; - - VARIANT vtIndex; - ::VariantInit(&vtIndex); - - if (NULL != ppElement) - { - *ppElement = NULL; - } - if (NULL != pdwIndex) - { - *pdwIndex = MAXDWORD; - } - - hr = pCollection->get_Count(&dwElements); - IisExitOnFailure(hr, "Failed get application IAppHostElementCollection count"); - - vtIndex.vt = VT_UI4; - for (DWORD i = 0; i < dwElements; ++i) - { - vtIndex.ulVal = i; - hr = pCollection->get_Item(vtIndex , &pElement); - IisExitOnFailure(hr, "Failed get IAppHostElement element"); - - if (pCallback(pElement, pContext)) - { - if (NULL != ppElement) - { - *ppElement = pElement; - pElement = NULL; - } - if (NULL != pdwIndex) - { - *pdwIndex = i; - } - break; - } - - ReleaseNullObject(pElement); - } - -LExit: - ReleaseObject(pElement); - ReleaseVariant(vtIndex); - - return hr; -} - -extern "C" HRESULT DAPI Iis7FindAppHostMethod( - __in IAppHostMethodCollection *pCollection, - __in LPCWSTR wzMethodName, - __out IAppHostMethod** ppMethod, - __out DWORD* pdwIndex - ) -{ - HRESULT hr = S_OK; - IAppHostMethod *pMethod = NULL; - DWORD dwMethods = 0; - - VARIANT vtIndex; - ::VariantInit(&vtIndex); - - if (NULL != ppMethod) - { - *ppMethod = NULL; - } - if (NULL != pdwIndex) - { - *pdwIndex = MAXDWORD; - } - - hr = pCollection->get_Count(&dwMethods); - IisExitOnFailure(hr, "Failed get application IAppHostMethodCollection count"); - - vtIndex.vt = VT_UI4; - for (DWORD i = 0; i < dwMethods; ++i) - { - vtIndex.ulVal = i; - hr = pCollection->get_Item(vtIndex , &pMethod); - IisExitOnFailure(hr, "Failed get IAppHostMethod element"); - - if (IsMatchingAppHostMethod(pMethod, wzMethodName)) - { - if (NULL != ppMethod) - { - *ppMethod = pMethod; - pMethod = NULL; - } - if (NULL != pdwIndex) - { - *pdwIndex = i; - } - break; - } - - ReleaseNullObject(pMethod); - } - -LExit: - ReleaseObject(pMethod); - ReleaseVariant(vtIndex); - - return hr; -} diff --git a/src/dutil/inc/aclutil.h b/src/dutil/inc/aclutil.h deleted file mode 100644 index ac03f9a8..00000000 --- a/src/dutil/inc/aclutil.h +++ /dev/null @@ -1,154 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include - -#define ReleaseSid(x) if (x) { AclFreeSid(x); } -#define ReleaseNullSid(x) if (x) { AclFreeSid(x); x = NULL; } - -#ifdef __cplusplus -extern "C" { -#endif - -// structs -struct ACL_ACCESS -{ - BOOL fDenyAccess; - DWORD dwAccessMask; - - // TODO: consider using a union - LPCWSTR pwzAccountName; // NOTE: the last three items in this structure are ignored if this is not NULL - - SID_IDENTIFIER_AUTHORITY sia; // used if pwzAccountName is NULL - BYTE nSubAuthorityCount; - DWORD nSubAuthority[8]; -}; - -struct ACL_ACE -{ - DWORD dwFlags; - DWORD dwMask; - PSID psid; -}; - - -// functions -HRESULT DAPI AclCheckAccess( - __in HANDLE hToken, - __in ACL_ACCESS* paa - ); -HRESULT DAPI AclCheckAdministratorAccess( - __in HANDLE hToken - ); -HRESULT DAPI AclCheckLocalSystemAccess( - __in HANDLE hToken - ); - -HRESULT DAPI AclGetWellKnownSid( - __in WELL_KNOWN_SID_TYPE wkst, - __deref_out PSID* ppsid - ); -HRESULT DAPI AclGetAccountSid( - __in_opt LPCWSTR wzSystem, - __in_z LPCWSTR wzAccount, - __deref_out PSID* ppsid - ); -HRESULT DAPI AclGetAccountSidString( - __in_z LPCWSTR wzSystem, - __in_z LPCWSTR wzAccount, - __deref_out_z LPWSTR* ppwzSid - ); - -HRESULT DAPI AclCreateDacl( - __in_ecount(cDeny) ACL_ACE rgaaDeny[], - __in DWORD cDeny, - __in_ecount(cAllow) ACL_ACE rgaaAllow[], - __in DWORD cAllow, - __deref_out ACL** ppAcl - ); -HRESULT DAPI AclAddToDacl( - __in ACL* pAcl, - __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[], - __in DWORD cDeny, - __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[], - __in DWORD cAllow, - __deref_out ACL** ppAclNew - ); -HRESULT DAPI AclMergeDacls( - __in const ACL* pAcl1, - __in const ACL* pAcl2, - __deref_out ACL** ppAclNew - ); -HRESULT DAPI AclCreateDaclOld( - __in_ecount(cAclAccesses) ACL_ACCESS* paa, - __in DWORD cAclAccesses, - __deref_out ACL** ppAcl - ); -HRESULT DAPI AclCreateSecurityDescriptor( - __in_ecount(cAclAccesses) ACL_ACCESS* paa, - __in DWORD cAclAccesses, - __deref_out SECURITY_DESCRIPTOR** ppsd - ); -HRESULT DAPI AclCreateSecurityDescriptorFromDacl( - __in ACL* pACL, - __deref_out SECURITY_DESCRIPTOR** ppsd - ); -HRESULT __cdecl AclCreateSecurityDescriptorFromString( - __deref_out SECURITY_DESCRIPTOR** ppsd, - __in_z __format_string LPCWSTR wzSddlFormat, - ... - ); -HRESULT DAPI AclDuplicateSecurityDescriptor( - __in SECURITY_DESCRIPTOR* psd, - __deref_out SECURITY_DESCRIPTOR** ppsd - ); -HRESULT DAPI AclGetSecurityDescriptor( - __in_z LPCWSTR wzObject, - __in SE_OBJECT_TYPE sot, - __in SECURITY_INFORMATION securityInformation, - __deref_out SECURITY_DESCRIPTOR** ppsd - ); -HRESULT DAPI AclSetSecurityWithRetry( - __in_z LPCWSTR wzObject, - __in SE_OBJECT_TYPE sot, - __in SECURITY_INFORMATION securityInformation, - __in_opt PSID psidOwner, - __in_opt PSID psidGroup, - __in_opt PACL pDacl, - __in_opt PACL pSacl, - __in DWORD cRetry, - __in DWORD dwWaitMilliseconds - ); - -HRESULT DAPI AclFreeSid( - __in PSID psid - ); -HRESULT DAPI AclFreeDacl( - __in ACL* pACL - ); -HRESULT DAPI AclFreeSecurityDescriptor( - __in SECURITY_DESCRIPTOR* psd - ); - -HRESULT DAPI AclAddAdminToSecurityDescriptor( - __in SECURITY_DESCRIPTOR* pSecurity, - __deref_out SECURITY_DESCRIPTOR** ppSecurityNew - ); - -// Following code in acl2util.cpp due to dependency on crypt32.dll. -HRESULT DAPI AclCalculateServiceSidString( - __in LPCWSTR wzServiceName, - __in SIZE_T cchServiceName, - __deref_out_z LPWSTR* psczSid - ); -HRESULT DAPI AclGetAccountSidStringEx( - __in_z LPCWSTR wzSystem, - __in_z LPCWSTR wzAccount, - __deref_out_z LPWSTR* psczSid - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/apputil.h b/src/dutil/inc/apputil.h deleted file mode 100644 index 1a1e14f7..00000000 --- a/src/dutil/inc/apputil.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -// functions - -/******************************************************************** -AppFreeCommandLineArgs - frees argv from AppParseCommandLine. - -********************************************************************/ -void DAPI AppFreeCommandLineArgs( - __in LPWSTR* argv - ); - -void DAPI AppInitialize( - __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[], - __in DWORD cSafelyLoadSystemDlls - ); - -/******************************************************************** -AppInitializeUnsafe - initializes without the full standard safety - precautions for an application. - -********************************************************************/ -void DAPI AppInitializeUnsafe(); - -/******************************************************************** -AppParseCommandLine - parses the command line using CommandLineToArgvW. - The caller must free the value of pArgv on success - by calling AppFreeCommandLineArgs. - -********************************************************************/ -DAPI_(HRESULT) AppParseCommandLine( - __in LPCWSTR wzCommandLine, - __in int* argc, - __in LPWSTR** pArgv - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/apuputil.h b/src/dutil/inc/apuputil.h deleted file mode 100644 index f26a12b7..00000000 --- a/src/dutil/inc/apuputil.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseApupChain(p) if (p) { ApupFreeChain(p); p = NULL; } -#define ReleaseNullApupChain(p) if (p) { ApupFreeChain(p); p = NULL; } - - -const LPCWSTR APPLICATION_SYNDICATION_NAMESPACE = L"http://appsyndication.org/2006/appsyn"; - -typedef enum APUP_HASH_ALGORITHM -{ - APUP_HASH_ALGORITHM_UNKNOWN, - APUP_HASH_ALGORITHM_MD5, - APUP_HASH_ALGORITHM_SHA1, - APUP_HASH_ALGORITHM_SHA256, - APUP_HASH_ALGORITHM_SHA512, -} APUP_HASH_ALGORITHM; - - -struct APPLICATION_UPDATE_ENCLOSURE -{ - LPWSTR wzUrl; - LPWSTR wzLocalName; - DWORD64 dw64Size; - - BYTE* rgbDigest; - DWORD cbDigest; - APUP_HASH_ALGORITHM digestAlgorithm; - - BOOL fInstaller; -}; - - -struct APPLICATION_UPDATE_ENTRY -{ - LPWSTR wzApplicationId; - LPWSTR wzApplicationType; - LPWSTR wzTitle; - LPWSTR wzSummary; - LPWSTR wzContentType; - LPWSTR wzContent; - - LPWSTR wzUpgradeId; - BOOL fUpgradeExclusive; - VERUTIL_VERSION* pVersion; - VERUTIL_VERSION* pUpgradeVersion; - - DWORD64 dw64TotalSize; - - DWORD cEnclosures; - APPLICATION_UPDATE_ENCLOSURE* rgEnclosures; -}; - - -struct APPLICATION_UPDATE_CHAIN -{ - LPWSTR wzDefaultApplicationId; - LPWSTR wzDefaultApplicationType; - - DWORD cEntries; - APPLICATION_UPDATE_ENTRY* rgEntries; -}; - - -HRESULT DAPI ApupAllocChainFromAtom( - __in ATOM_FEED* pFeed, - __out APPLICATION_UPDATE_CHAIN** ppChain - ); - -HRESULT DAPI ApupFilterChain( - __in APPLICATION_UPDATE_CHAIN* pChain, - __in VERUTIL_VERSION* pVersion, - __out APPLICATION_UPDATE_CHAIN** ppFilteredChain - ); - -void DAPI ApupFreeChain( - __in APPLICATION_UPDATE_CHAIN* pChain - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/atomutil.h b/src/dutil/inc/atomutil.h deleted file mode 100644 index 9acfc1d5..00000000 --- a/src/dutil/inc/atomutil.h +++ /dev/null @@ -1,146 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseAtomFeed(p) if (p) { AtomFreeFeed(p); } -#define ReleaseNullAtomFeed(p) if (p) { AtomFreeFeed(p); p = NULL; } - - -struct ATOM_UNKNOWN_ATTRIBUTE -{ - LPWSTR wzNamespace; - LPWSTR wzAttribute; - LPWSTR wzValue; - - ATOM_UNKNOWN_ATTRIBUTE* pNext; -}; - -struct ATOM_UNKNOWN_ELEMENT -{ - LPWSTR wzNamespace; - LPWSTR wzElement; - LPWSTR wzValue; - - ATOM_UNKNOWN_ATTRIBUTE* pAttributes; - ATOM_UNKNOWN_ELEMENT* pNext; -}; - -struct ATOM_LINK -{ - LPWSTR wzRel; - LPWSTR wzTitle; - LPWSTR wzType; - LPWSTR wzUrl; - LPWSTR wzValue; - DWORD64 dw64Length; - - ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttributes; - ATOM_UNKNOWN_ELEMENT* pUnknownElements; -}; - -struct ATOM_CONTENT -{ - LPWSTR wzType; - LPWSTR wzUrl; - LPWSTR wzValue; - - ATOM_UNKNOWN_ELEMENT* pUnknownElements; -}; - -struct ATOM_AUTHOR -{ - LPWSTR wzName; - LPWSTR wzEmail; - LPWSTR wzUrl; -}; - -struct ATOM_CATEGORY -{ - LPWSTR wzLabel; - LPWSTR wzScheme; - LPWSTR wzTerm; - - ATOM_UNKNOWN_ELEMENT* pUnknownElements; -}; - -struct ATOM_ENTRY -{ - LPWSTR wzId; - LPWSTR wzSummary; - LPWSTR wzTitle; - FILETIME ftPublished; - FILETIME ftUpdated; - - ATOM_CONTENT* pContent; - - DWORD cAuthors; - ATOM_AUTHOR* rgAuthors; - - DWORD cCategories; - ATOM_CATEGORY* rgCategories; - - DWORD cLinks; - ATOM_LINK* rgLinks; - - IXMLDOMNode* pixn; - ATOM_UNKNOWN_ELEMENT* pUnknownElements; -}; - -struct ATOM_FEED -{ - LPWSTR wzGenerator; - LPWSTR wzIcon; - LPWSTR wzId; - LPWSTR wzLogo; - LPWSTR wzSubtitle; - LPWSTR wzTitle; - FILETIME ftUpdated; - - DWORD cAuthors; - ATOM_AUTHOR* rgAuthors; - - DWORD cCategories; - ATOM_CATEGORY* rgCategories; - - DWORD cEntries; - ATOM_ENTRY* rgEntries; - - DWORD cLinks; - ATOM_LINK* rgLinks; - - IXMLDOMNode* pixn; - ATOM_UNKNOWN_ELEMENT* pUnknownElements; -}; - -HRESULT DAPI AtomInitialize( - ); - -void DAPI AtomUninitialize( - ); - -HRESULT DAPI AtomParseFromString( - __in_z LPCWSTR wzAtomString, - __out ATOM_FEED **ppFeed - ); - -HRESULT DAPI AtomParseFromFile( - __in_z LPCWSTR wzAtomFile, - __out ATOM_FEED **ppFeed - ); - -HRESULT DAPI AtomParseFromDocument( - __in IXMLDOMDocument* pixdDocument, - __out ATOM_FEED **ppFeed - ); - -void DAPI AtomFreeFeed( - __in_xcount(pFeed->cItems) ATOM_FEED* pFeed - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/buffutil.h b/src/dutil/inc/buffutil.h deleted file mode 100644 index 322209e6..00000000 --- a/src/dutil/inc/buffutil.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - - -// macro definitions - -#define ReleaseBuffer ReleaseMem -#define ReleaseNullBuffer ReleaseNullMem -#define BuffFree MemFree - - -// function declarations - -HRESULT BuffReadNumber( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __out DWORD* pdw - ); -HRESULT BuffReadNumber64( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __out DWORD64* pdw64 - ); -HRESULT BuffReadPointer( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __out DWORD_PTR* pdw -); -HRESULT BuffReadString( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __deref_out_z LPWSTR* pscz - ); -HRESULT BuffReadStringAnsi( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __deref_out_z LPSTR* pscz - ); -HRESULT BuffReadStream( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer, - __deref_inout_bcount(*pcbStream) BYTE** ppbStream, - __out SIZE_T* pcbStream - ); - -HRESULT BuffWriteNumber( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in DWORD dw - ); -HRESULT BuffWriteNumber64( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in DWORD64 dw64 - ); -HRESULT BuffWritePointer( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in DWORD_PTR dw -); -HRESULT BuffWriteString( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in_z_opt LPCWSTR scz - ); -HRESULT BuffWriteStringAnsi( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in_z_opt LPCSTR scz - ); -HRESULT BuffWriteStream( - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer, - __in_bcount(cbStream) const BYTE* pbStream, - __in SIZE_T cbStream - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/butil.h b/src/dutil/inc/butil.h deleted file mode 100644 index d1ec73bc..00000000 --- a/src/dutil/inc/butil.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -enum BUNDLE_INSTALL_CONTEXT -{ - BUNDLE_INSTALL_CONTEXT_MACHINE, - BUNDLE_INSTALL_CONTEXT_USER, -}; - - -/******************************************************************** -BundleGetBundleInfo - Queries the bundle installation metadata for a given property - -RETURNS: - E_INVALIDARG - An invalid parameter was passed to the function. - HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) - The bundle is not installed - HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) - The property is unrecognized - HRESULT_FROM_WIN32(ERROR_MORE_DATA) - A buffer is too small to hold the requested data. - E_NOTIMPL: - Tried to read a bundle attribute for a type which has not been implemented - - All other returns are unexpected returns from other dutil methods. -********************************************************************/ -HRESULT DAPI BundleGetBundleInfo( - __in_z LPCWSTR szBundleId, // Bundle code - __in_z LPCWSTR szAttribute, // attribute name - __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, // returned value, NULL if not desired - __inout_opt LPDWORD pcchValueBuf // in/out buffer character count - ); - -/******************************************************************** -BundleEnumRelatedBundle - Queries the bundle installation metadata for installs with the given upgrade code - -NOTE: lpBundleIdBuff is a buffer to receive the bundle GUID. This buffer must be 39 characters long. - The first 38 characters are for the GUID, and the last character is for the terminating null character. -RETURNS: - E_INVALIDARG - An invalid parameter was passed to the function. - - All other returns are unexpected returns from other dutil methods. -********************************************************************/ -HRESULT DAPI BundleEnumRelatedBundle( - __in_z LPCWSTR lpUpgradeCode, - __in BUNDLE_INSTALL_CONTEXT context, - __inout PDWORD pdwStartIndex, - __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/cabcutil.h b/src/dutil/inc/cabcutil.h deleted file mode 100644 index 4f0c7b13..00000000 --- a/src/dutil/inc/cabcutil.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include -#include - -// Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method -// First argument is the name of splitting cabinet without extension e.g. "cab1" -// Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab" -// Third argument is the file token of the first file present in the splitting cabinet -typedef void (__stdcall * FileSplitCabNamesCallback)(LPWSTR, LPWSTR, LPWSTR); - -#define CAB_MAX_SIZE 0x7FFFFFFF // (see KB: Q174866) - -#ifdef __cplusplus -extern "C" { -#endif - -extern const int CABC_HANDLE_BYTES; - -// time vs. space trade-off -typedef enum COMPRESSION_TYPE -{ - COMPRESSION_TYPE_NONE, // fastest - COMPRESSION_TYPE_LOW, - COMPRESSION_TYPE_MEDIUM, - COMPRESSION_TYPE_HIGH, // smallest - COMPRESSION_TYPE_MSZIP -} COMPRESSION_TYPE; - -// functions -HRESULT DAPI CabCBegin( - __in_z LPCWSTR wzCab, - __in_z LPCWSTR wzCabDir, - __in DWORD dwMaxFiles, - __in DWORD dwMaxSize, - __in DWORD dwMaxThresh, - __in COMPRESSION_TYPE ct, - __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext - ); -HRESULT DAPI CabCNextCab( - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext - ); -HRESULT DAPI CabCAddFile( - __in_z LPCWSTR wzFile, - __in_z_opt LPCWSTR wzToken, - __in_opt PMSIFILEHASHINFO pmfHash, - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext - ); -HRESULT DAPI CabCFinish( - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext, - __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback - ); -void DAPI CabCCancel( - __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/cabutil.h b/src/dutil/inc/cabutil.h deleted file mode 100644 index 0bedba80..00000000 --- a/src/dutil/inc/cabutil.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// structs - - -// callback function prototypes -typedef HRESULT (*CAB_CALLBACK_OPEN_FILE)(LPCWSTR wzFile, INT_PTR* ppFile); -typedef HRESULT (*CAB_CALLBACK_READ_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead); -typedef HRESULT (*CAB_CALLBACK_WRITE_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead); -typedef LONG (*CAB_CALLBACK_SEEK_FILE)(INT_PTR pFile, DWORD dwMove, DWORD dwMoveMethod); -typedef HRESULT (*CAB_CALLBACK_CLOSE_FILE)(INT_PTR pFile); - -typedef HRESULT (*CAB_CALLBACK_BEGIN_FILE)(LPCWSTR wzFileId, FILETIME* pftFileTime, DWORD cbFileSize, LPVOID pvContext, INT_PTR* ppFile); -typedef HRESULT (*CAB_CALLBACK_END_FILE)(LPCWSTR wzFileId, LPVOID pvContext, INT_PTR pFile); -typedef HRESULT (*CAB_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext); - -// function type with calling convention of __stdcall that .NET 1.1 understands only -// .NET 2.0 will not need this -typedef INT_PTR (FAR __stdcall *STDCALL_PFNFDINOTIFY)(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin); - - -// functions -HRESULT DAPI CabInitialize( - __in BOOL fDelayLoad - ); -void DAPI CabUninitialize( - ); - -HRESULT DAPI CabExtract( - __in_z LPCWSTR wzCabinet, - __in_z LPCWSTR wzExtractFile, - __in_z LPCWSTR wzExtractDir, - __in_opt CAB_CALLBACK_PROGRESS pfnProgress, - __in_opt LPVOID pvContext, - __in DWORD64 dw64EmbeddedOffset - ); - -HRESULT DAPI CabEnumerate( - __in_z LPCWSTR wzCabinet, - __in_z LPCWSTR wzEnumerateFile, - __in STDCALL_PFNFDINOTIFY pfnNotify, - __in DWORD64 dw64EmbeddedOffset - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/certutil.h b/src/dutil/inc/certutil.h deleted file mode 100644 index 8565c1cf..00000000 --- a/src/dutil/inc/certutil.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#define ReleaseCertStore(p) if (p) { ::CertCloseStore(p, 0); p = NULL; } -#define ReleaseCertContext(p) if (p) { ::CertFreeCertificateContext(p); p = NULL; } -#define ReleaseCertChain(p) if (p) { ::CertFreeCertificateChain(p); p = NULL; } - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI CertReadProperty( - __in PCCERT_CONTEXT pCertContext, - __in DWORD dwProperty, - __out_bcount(*pcbValue) LPVOID pvValue, - __out_opt DWORD* pcbValue - ); - -HRESULT DAPI CertGetAuthenticodeSigningTimestamp( - __in CMSG_SIGNER_INFO* pSignerInfo, - __out FILETIME* pft - ); - -HRESULT DAPI GetCryptProvFromCert( - __in_opt HWND hwnd, - __in PCCERT_CONTEXT pCert, - __out HCRYPTPROV *phCryptProv, - __out DWORD *pdwKeySpec, - __in BOOL *pfDidCryptAcquire, - __deref_opt_out LPWSTR *ppwszTmpContainer, - __deref_opt_out LPWSTR *ppwszProviderName, - __out DWORD *pdwProviderType - ); - -HRESULT DAPI FreeCryptProvFromCert( - __in BOOL fAcquired, - __in HCRYPTPROV hProv, - __in_opt LPWSTR pwszCapiProvider, - __in DWORD dwProviderType, - __in_opt LPWSTR pwszTmpContainer - ); - -HRESULT DAPI GetProvSecurityDesc( - __in HCRYPTPROV hProv, - __deref_out SECURITY_DESCRIPTOR** pSecurity - ); - -HRESULT DAPI SetProvSecurityDesc( - __in HCRYPTPROV hProv, - __in SECURITY_DESCRIPTOR* pSecurity - ); - -BOOL DAPI CertHasPrivateKey( - __in PCCERT_CONTEXT pCertContext, - __out_opt DWORD* pdwKeySpec - ); - -HRESULT DAPI CertInstallSingleCertificate( - __in HCERTSTORE hStore, - __in PCCERT_CONTEXT pCertContext, - __in LPCWSTR wzName - ); -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/conutil.h b/src/dutil/inc/conutil.h deleted file mode 100644 index 38aaea84..00000000 --- a/src/dutil/inc/conutil.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ConsoleExitOnFailureSource(d, x, c, f, ...) if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } -#define ConsoleExitOnLastErrorSource(d, x, c, f, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } } -#define ConsoleExitOnNullSource(d, p, x, e, c, f, ...) if (NULL == p) { x = e; ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } -#define ConsoleExitOnNullWithLastErrorSource(d, p, x, c, f, ...) if (NULL == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } -#define ConsoleExitWithLastErrorSource(d, x, c, f, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } - - -#define ConsoleExitOnFailure(x, c, f, ...) ConsoleExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) -#define ConsoleExitOnLastError(x, c, f, ...) ConsoleExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) -#define ConsoleExitOnNull(p, x, e, c, f, ...) ConsoleExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, c, f, __VA_ARGS__) -#define ConsoleExitOnNullWithLastError(p, x, c, f, ...) ConsoleExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, c, f, __VA_ARGS__) -#define ConsoleExitWithLastError(x, c, f, ...) ConsoleExitWithLastErrorSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) - -// enums -typedef enum CONSOLE_COLOR { CONSOLE_COLOR_NORMAL, CONSOLE_COLOR_RED, CONSOLE_COLOR_YELLOW, CONSOLE_COLOR_GREEN } CONSOLE_COLOR; - -// structs - -// functions -HRESULT DAPI ConsoleInitialize(); -void DAPI ConsoleUninitialize(); - -void DAPI ConsoleGreen(); -void DAPI ConsoleRed(); -void DAPI ConsoleYellow(); -void DAPI ConsoleNormal(); - -HRESULT DAPI ConsoleWrite( - CONSOLE_COLOR cc, - __in_z __format_string LPCSTR szFormat, - ... - ); -HRESULT DAPI ConsoleWriteLine( - CONSOLE_COLOR cc, - __in_z __format_string LPCSTR szFormat, - ... - ); -HRESULT DAPI ConsoleWriteError( - HRESULT hrError, - CONSOLE_COLOR cc, - __in_z __format_string LPCSTR szFormat, - ... - ); - -HRESULT DAPI ConsoleReadW( - __deref_out_z LPWSTR* ppwzBuffer - ); - -HRESULT DAPI ConsoleReadStringA( - __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* szCharBuffer, - CONST DWORD cchCharBuffer, - __out DWORD* pcchNumCharReturn - ); -HRESULT DAPI ConsoleReadStringW( - __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* szCharBuffer, - CONST DWORD cchCharBuffer, - __out DWORD* pcchNumCharReturn - ); - -HRESULT DAPI ConsoleReadNonBlockingW( - __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, - __out DWORD* pcchSize, - BOOL fReadLine - ); - -HRESULT DAPI ConsoleSetReadHidden(void); -HRESULT DAPI ConsoleSetReadNormal(void); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/cryputil.h b/src/dutil/inc/cryputil.h deleted file mode 100644 index 02492d8a..00000000 --- a/src/dutil/inc/cryputil.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#define ReleaseCryptMsg(p) if (p) { ::CryptMsgClose(p); p = NULL; } - -#ifdef __cplusplus -extern "C" { -#endif - - -// Use CRYPTPROTECTMEMORY_BLOCK_SIZE, because it's larger and thus more restrictive than RTL_ENCRYPT_MEMORY_SIZE. -#define CRYP_ENCRYPT_MEMORY_SIZE CRYPTPROTECTMEMORY_BLOCK_SIZE -#define MD5_HASH_LEN 16 -#define SHA1_HASH_LEN 20 -#define SHA256_HASH_LEN 32 -#define SHA512_HASH_LEN 64 - -typedef NTSTATUS (APIENTRY *PFN_RTLENCRYPTMEMORY)( - __inout PVOID Memory, - __in ULONG MemoryLength, - __in ULONG OptionFlags - ); - -typedef NTSTATUS (APIENTRY *PFN_RTLDECRYPTMEMORY)( - __inout PVOID Memory, - __in ULONG MemoryLength, - __in ULONG OptionFlags - ); - -typedef BOOL (APIENTRY *PFN_CRYPTPROTECTMEMORY)( - __inout LPVOID pData, - __in DWORD cbData, - __in DWORD dwFlags - ); - -typedef BOOL (APIENTRY *PFN_CRYPTUNPROTECTMEMORY)( - __inout LPVOID pData, - __in DWORD cbData, - __in DWORD dwFlags - ); - -// function declarations - -HRESULT DAPI CrypInitialize(); -void DAPI CrypUninitialize(); - -HRESULT DAPI CrypDecodeObject( - __in_z LPCSTR szStructType, - __in_ecount(cbData) const BYTE* pbData, - __in DWORD cbData, - __in DWORD dwFlags, - __out LPVOID* ppvObject, - __out_opt DWORD* pcbObject - ); - -HRESULT DAPI CrypMsgGetParam( - __in HCRYPTMSG hCryptMsg, - __in DWORD dwType, - __in DWORD dwIndex, - __out LPVOID* ppvData, - __out_opt DWORD* pcbData - ); - -HRESULT DAPI CrypHashFile( - __in_z LPCWSTR wzFilePath, - __in DWORD dwProvType, - __in ALG_ID algid, - __out_bcount(cbHash) BYTE* pbHash, - __in DWORD cbHash, - __out_opt DWORD64* pqwBytesHashed - ); - -HRESULT DAPI CrypHashFileHandle( - __in HANDLE hFile, - __in DWORD dwProvType, - __in ALG_ID algid, - __out_bcount(cbHash) BYTE* pbHash, - __in DWORD cbHash, - __out_opt DWORD64* pqwBytesHashed - ); - -HRESULT DAPI CrypHashBuffer( - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __in DWORD dwProvType, - __in ALG_ID algid, - __out_bcount(cbHash) BYTE* pbHash, - __in DWORD cbHash - ); - -HRESULT DAPI CrypEncryptMemory( - __inout LPVOID pData, - __in DWORD cbData, - __in DWORD dwFlags - ); - -HRESULT DAPI CrypDecryptMemory( - __inout LPVOID pData, - __in DWORD cbData, - __in DWORD dwFlags - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/deputil.h b/src/dutil/inc/deputil.h deleted file mode 100644 index bfe235f3..00000000 --- a/src/dutil/inc/deputil.h +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseDependencyArray(rg, c) if (rg) { DepDependencyArrayFree(rg, c); } -#define ReleaseNullDependencyArray(rg, c) if (rg) { DepDependencyArrayFree(rg, c); rg = NULL; } - -typedef struct _DEPENDENCY -{ - LPWSTR sczKey; - LPWSTR sczName; -} DEPENDENCY; - - -/*************************************************************************** - DepGetProviderInformation - gets the various pieces of data registered - with a dependency. - - Note: Returns E_NOTFOUND if the dependency was not found. -***************************************************************************/ -DAPI_(HRESULT) DepGetProviderInformation( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __deref_out_z_opt LPWSTR* psczId, - __deref_out_z_opt LPWSTR* psczName, - __deref_out_z_opt LPWSTR* psczVersion - ); - -/*************************************************************************** - DepCheckDependency - Checks that the dependency is registered and within - the proper version range. - - Note: Returns E_NOTFOUND if the dependency was not found. -***************************************************************************/ -DAPI_(HRESULT) DepCheckDependency( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __in_z_opt LPCWSTR wzMinVersion, - __in_z_opt LPCWSTR wzMaxVersion, - __in int iAttributes, - __in STRINGDICT_HANDLE sdDependencies, - __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, - __inout LPUINT pcDependencies - ); - -/*************************************************************************** - DepCheckDependents - Checks if any dependents are still installed for the - given provider key. - -***************************************************************************/ -DAPI_(HRESULT) DepCheckDependents( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __reserved int iAttributes, - __in C_STRINGDICT_HANDLE sdIgnoredDependents, - __deref_inout_ecount_opt(*pcDependents) DEPENDENCY** prgDependents, - __inout LPUINT pcDependents - ); - -/*************************************************************************** - DepRegisterDependency - Registers the dependency provider. - -***************************************************************************/ -DAPI_(HRESULT) DepRegisterDependency( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey, - __in_z LPCWSTR wzVersion, - __in_z LPCWSTR wzDisplayName, - __in_z_opt LPCWSTR wzId, - __in int iAttributes - ); - -/*************************************************************************** - DepDependentExists - Determines if a dependent is registered. - - Note: Returns S_OK if dependent is registered. - Returns E_FILENOTFOUND if dependent is not registered -***************************************************************************/ -DAPI_(HRESULT) DepDependentExists( - __in HKEY hkHive, - __in_z LPCWSTR wzDependencyProviderKey, - __in_z LPCWSTR wzProviderKey - ); - -/*************************************************************************** - DepRegisterDependent - Registers a dependent under the dependency provider. - -***************************************************************************/ -DAPI_(HRESULT) DepRegisterDependent( - __in HKEY hkHive, - __in_z LPCWSTR wzDependencyProviderKey, - __in_z LPCWSTR wzProviderKey, - __in_z_opt LPCWSTR wzMinVersion, - __in_z_opt LPCWSTR wzMaxVersion, - __in int iAttributes - ); - -/*************************************************************************** - DepUnregisterDependency - Removes the dependency provider. - - Note: Caller should call CheckDependents prior to remove a dependency. - Returns E_FILENOTFOUND if the dependency is not registered. -***************************************************************************/ -DAPI_(HRESULT) DepUnregisterDependency( - __in HKEY hkHive, - __in_z LPCWSTR wzProviderKey - ); - -/*************************************************************************** - DepUnregisterDependent - Removes a dependent under the dependency provider. - - Note: Returns E_FILENOTFOUND if neither the dependency or dependent are - registered. - ***************************************************************************/ -DAPI_(HRESULT) DepUnregisterDependent( - __in HKEY hkHive, - __in_z LPCWSTR wzDependencyProviderKey, - __in_z LPCWSTR wzProviderKey - ); - -/*************************************************************************** - DependencyArrayAlloc - Allocates or expands an array of DEPENDENCY structs. - -***************************************************************************/ -DAPI_(HRESULT) DepDependencyArrayAlloc( - __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, - __inout LPUINT pcDependencies, - __in_z LPCWSTR wzKey, - __in_z_opt LPCWSTR wzName - ); - -/*************************************************************************** - DepDependencyArrayFree - Frees an array of DEPENDENCY structs. - -***************************************************************************/ -DAPI_(void) DepDependencyArrayFree( - __in_ecount(cDependencies) DEPENDENCY* rgDependencies, - __in UINT cDependencies - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/dictutil.h b/src/dutil/inc/dictutil.h deleted file mode 100644 index f0a3bb5a..00000000 --- a/src/dutil/inc/dictutil.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseDict(sdh) if (sdh) { DictDestroy(sdh); } -#define ReleaseNullDict(sdh) if (sdh) { DictDestroy(sdh); sdh = NULL; } - -typedef void* STRINGDICT_HANDLE; -typedef const void* C_STRINGDICT_HANDLE; - -extern const int STRINGDICT_HANDLE_BYTES; - -typedef enum DICT_FLAG -{ - DICT_FLAG_NONE = 0, - DICT_FLAG_CASEINSENSITIVE = 1 -} DICT_FLAG; - -HRESULT DAPI DictCreateWithEmbeddedKey( - __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, - __in DWORD dwNumExpectedItems, - __in_opt void **ppvArray, - __in size_t cByteOffset, - __in DICT_FLAG dfFlags - ); -HRESULT DAPI DictCreateStringList( - __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, - __in DWORD dwNumExpectedItems, - __in DICT_FLAG dfFlags - ); -HRESULT DAPI DictCreateStringListFromArray( - __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, - __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, - __in const DWORD cStringArray, - __in DICT_FLAG dfFlags - ); -HRESULT DAPI DictCompareStringListToArray( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList, - __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, - __in const DWORD cStringArray - ); -HRESULT DAPI DictAddKey( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, - __in_z LPCWSTR szString - ); -HRESULT DAPI DictAddValue( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, - __in void *pvValue - ); -HRESULT DAPI DictKeyExists( - __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, - __in_z LPCWSTR szString - ); -HRESULT DAPI DictGetValue( - __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, - __in_z LPCWSTR szString, - __out void **ppvValue - ); -void DAPI DictDestroy( - __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/dirutil.h b/src/dutil/inc/dirutil.h deleted file mode 100644 index 539b3a73..00000000 --- a/src/dutil/inc/dirutil.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -typedef enum DIR_DELETE -{ - DIR_DELETE_FILES = 1, - DIR_DELETE_RECURSE = 2, - DIR_DELETE_SCHEDULE = 4, -} DIR_DELETE; - -#ifdef __cplusplus -extern "C" { -#endif - -BOOL DAPI DirExists( - __in_z LPCWSTR wzPath, - __out_opt DWORD *pdwAttributes - ); - -HRESULT DAPI DirCreateTempPath( - __in_z LPCWSTR wzPrefix, - __out_ecount_z(cchPath) LPWSTR wzPath, - __in DWORD cchPath - ); - -HRESULT DAPI DirEnsureExists( - __in_z LPCWSTR wzPath, - __in_opt LPSECURITY_ATTRIBUTES psa - ); - -HRESULT DAPI DirEnsureDelete( - __in_z LPCWSTR wzPath, - __in BOOL fDeleteFiles, - __in BOOL fRecurse - ); - -HRESULT DAPI DirEnsureDeleteEx( - __in_z LPCWSTR wzPath, - __in DWORD dwFlags - ); - -DWORD DAPI DirDeleteEmptyDirectoriesToRoot( - __in_z LPCWSTR wzPath, - __in DWORD dwFlags - ); - -HRESULT DAPI DirGetCurrent( - __deref_out_z LPWSTR* psczCurrentDirectory - ); - -HRESULT DAPI DirSetCurrent( - __in_z LPCWSTR wzDirectory - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/dlutil.h b/src/dutil/inc/dlutil.h deleted file mode 100644 index 3e95103a..00000000 --- a/src/dutil/inc/dlutil.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef HRESULT (WINAPI *LPAUTHENTICATION_ROUTINE)( - __in LPVOID pVoid, - __in HINTERNET hUrl, - __in long lHttpCode, - __out BOOL* pfRetrySend, - __out BOOL* pfRetry - ); - -typedef int (WINAPI *LPCANCEL_ROUTINE)( - __in HRESULT hrError, - __in_z_opt LPCWSTR wzError, - __in BOOL fAllowRetry, - __in_opt LPVOID pvContext - ); - -// structs -typedef struct _DOWNLOAD_SOURCE -{ - LPWSTR sczUrl; - LPWSTR sczUser; - LPWSTR sczPassword; -} DOWNLOAD_SOURCE; - -typedef struct _DOWNLOAD_CACHE_CALLBACK -{ - LPPROGRESS_ROUTINE pfnProgress; - LPCANCEL_ROUTINE pfnCancel; - LPVOID pv; -} DOWNLOAD_CACHE_CALLBACK; - -typedef struct _DOWNLOAD_AUTHENTICATION_CALLBACK -{ - LPAUTHENTICATION_ROUTINE pfnAuthenticate; - LPVOID pv; -} DOWNLOAD_AUTHENTICATION_CALLBACK; - - -// functions - -HRESULT DAPI DownloadUrl( - __in DOWNLOAD_SOURCE* pDownloadSource, - __in DWORD64 dw64AuthoredDownloadSize, - __in LPCWSTR wzDestinationPath, - __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, - __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate - ); - - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/dpiutil.h b/src/dutil/inc/dpiutil.h deleted file mode 100644 index b30e2332..00000000 --- a/src/dutil/inc/dpiutil.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -// from WinUser.h -#ifndef WM_DPICHANGED -#define WM_DPICHANGED 0x02E0 -#endif -#ifndef USER_DEFAULT_SCREEN_DPI -#define USER_DEFAULT_SCREEN_DPI 96 -#endif - -typedef enum DPIU_AWARENESS -{ - DPIU_AWARENESS_NONE = 0x0, - DPIU_AWARENESS_SYSTEM = 0x1, - DPIU_AWARENESS_PERMONITOR = 0x2, - DPIU_AWARENESS_PERMONITORV2 = 0x4, - DPIU_AWARENESS_GDISCALED = 0x8, -} DPIU_PROCESS_AWARENESS; - -typedef struct _DPIU_MONITOR_CONTEXT -{ - UINT nDpi; - MONITORINFOEXW mi; -} DPIU_MONITOR_CONTEXT; - -typedef struct _DPIU_WINDOW_CONTEXT -{ - UINT nDpi; -} DPIU_WINDOW_CONTEXT; - -typedef BOOL (APIENTRY* PFN_ADJUSTWINDOWRECTEXFORDPI)( - __in LPRECT lpRect, - __in DWORD dwStyle, - __in BOOL bMenu, - __in DWORD dwExStyle, - __in UINT dpi - ); -typedef UINT (APIENTRY *PFN_GETDPIFORWINDOW)( - __in HWND hwnd - ); -typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARE)(); -typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARENESSCONTEXT)( - __in DPI_AWARENESS_CONTEXT value - ); - -#ifdef DPI_ENUMS_DECLARED -typedef HRESULT(APIENTRY* PFN_GETDPIFORMONITOR)( - __in HMONITOR hmonitor, - __in MONITOR_DPI_TYPE dpiType, - __in UINT* dpiX, - __in UINT* dpiY - ); -typedef HRESULT(APIENTRY* PFN_SETPROCESSDPIAWARENESS)( - __in PROCESS_DPI_AWARENESS value - ); -#endif - -void DAPI DpiuInitialize(); -void DAPI DpiuUninitialize(); - -/******************************************************************** - DpiuAdjustWindowRect - calculate the required size of the window rectangle, - based on the desired size of the client rectangle - and the provided DPI. - -*******************************************************************/ -void DAPI DpiuAdjustWindowRect( - __in RECT* pWindowRect, - __in DWORD dwStyle, - __in BOOL fMenu, - __in DWORD dwExStyle, - __in UINT nDpi - ); - -/******************************************************************** - DpiuGetMonitorContextFromPoint - get the DPI context of the monitor from the given point. - -*******************************************************************/ -HRESULT DAPI DpiuGetMonitorContextFromPoint( - __in const POINT* pt, - __out DPIU_MONITOR_CONTEXT** ppMonitorContext - ); - -/******************************************************************** - DpiuGetWindowContext - get the DPI context of the given window. - -*******************************************************************/ -void DAPI DpiuGetWindowContext( - __in HWND hWnd, - __in DPIU_WINDOW_CONTEXT* pWindowContext - ); - -/******************************************************************** - DpiuScaleValue - scale the value to the target DPI. - -*******************************************************************/ -int DAPI DpiuScaleValue( - __in int nDefaultDpiValue, - __in UINT nTargetDpi - ); - -/******************************************************************** - DpiuSetProcessDpiAwareness - set the process DPI awareness. The ranking is - PERMONITORV2 > PERMONITOR > SYSTEM > GDISCALED > NONE. - -*******************************************************************/ -HRESULT DAPI DpiuSetProcessDpiAwareness( - __in DPIU_AWARENESS supportedAwareness, - __in_opt DPIU_AWARENESS* pSelectedAwareness - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/dutil.h b/src/dutil/inc/dutil.h deleted file mode 100644 index fc9ec0f4..00000000 --- a/src/dutil/inc/dutil.h +++ /dev/null @@ -1,190 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "dutilsources.h" - -#define DAPI __stdcall -#define DAPIV __cdecl // used only for functions taking variable length arguments - -#define DAPI_(type) EXTERN_C type DAPI -#define DAPIV_(type) EXTERN_C type DAPIV - - -// asserts and traces -typedef BOOL (DAPI *DUTIL_ASSERTDISPLAYFUNCTION)(__in_z LPCSTR sz); - -typedef void (CALLBACK *DUTIL_CALLBACK_TRACEERROR)( - __in_z LPCSTR szFile, - __in int iLine, - __in REPORT_LEVEL rl, - __in UINT source, - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ); - -#ifdef __cplusplus -extern "C" { -#endif - -/******************************************************************** - DutilInitialize - initialize dutil. - -*******************************************************************/ -HRESULT DAPI DutilInitialize( - __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback - ); - -/******************************************************************** - DutilUninitialize - uninitialize dutil. - -*******************************************************************/ -void DAPI DutilUninitialize(); - -void DAPI Dutil_SetAssertModule(__in HMODULE hAssertModule); -void DAPI Dutil_SetAssertDisplayFunction(__in DUTIL_ASSERTDISPLAYFUNCTION pfn); -void DAPI Dutil_Assert(__in_z LPCSTR szFile, __in int iLine); -void DAPI Dutil_AssertSz(__in_z LPCSTR szFile, __in int iLine, __in_z __format_string LPCSTR szMessage); - -void DAPI Dutil_TraceSetLevel(__in REPORT_LEVEL ll, __in BOOL fTraceFilenames); -REPORT_LEVEL DAPI Dutil_TraceGetLevel(); -void DAPIV Dutil_Trace(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in_z __format_string LPCSTR szMessage, ...); -void DAPIV Dutil_TraceError(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in HRESULT hr, __in_z __format_string LPCSTR szMessage, ...); -void DAPIV Dutil_TraceErrorSource(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in UINT source, __in HRESULT hr, __in_z __format_string LPCSTR szMessage, ...); -void DAPI Dutil_RootFailure(__in_z LPCSTR szFile, __in int iLine, __in HRESULT hrError); - -#ifdef __cplusplus -} -#endif - - -#ifdef DEBUG - -#define AssertSetModule(m) (void)Dutil_SetAssertModule(m) -#define AssertSetDisplayFunction(pfn) (void)Dutil_SetAssertDisplayFunction(pfn) -#define Assert(f) ((f) ? (void)0 : (void)Dutil_Assert(__FILE__, __LINE__)) -#define AssertSz(f, sz) ((f) ? (void)0 : (void)Dutil_AssertSz(__FILE__, __LINE__, sz)) - -#define TraceSetLevel(l, f) (void)Dutil_TraceSetLevel(l, f) -#define TraceGetLevel() (REPORT_LEVEL)Dutil_TraceGetLevel() -#define Trace(l, f, ...) (void)Dutil_Trace(__FILE__, __LINE__, l, f, __VA_ARGS__) -#define TraceError(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_ERROR, x, f, __VA_ARGS__) -#define TraceErrorDebug(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_DEBUG, x, f, __VA_ARGS__) - -#else // !DEBUG - -#define AssertSetModule(m) -#define AssertSetDisplayFunction(pfn) -#define Assert(f) -#define AssertSz(f, sz) - -#define TraceSetLevel(l, f) -#define Trace(l, f, ...) -#define TraceError(x, f, ...) -#define TraceErrorDebug(x, f, ...) - -#endif // DEBUG - -// DUTIL_SOURCE_DEFAULT can be overriden -#ifndef DUTIL_SOURCE_DEFAULT -#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_UNKNOWN -#endif - -// Exit macros -#define ExitFunction() { goto LExit; } -#define ExitFunction1(x) { x; goto LExit; } - -#define ExitFunctionWithLastError(x) { x = HRESULT_FROM_WIN32(::GetLastError()); goto LExit; } - -#define ExitTraceSource(d, x, s, ...) { TraceError(x, s, __VA_ARGS__); (void)Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_ERROR, d, x, s, __VA_ARGS__); } -#define ExitTraceDebugSource(d, x, s, ...) { TraceErrorDebug(x, s, __VA_ARGS__); (void)Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_DEBUG, d, x, s, __VA_ARGS__); } - -#define ExitOnLastErrorSource(d, x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } } -#define ExitOnLastErrorDebugTraceSource(d, x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceDebugSource(d, x, s, __VA_ARGS__); goto LExit; } } -#define ExitWithLastErrorSource(d, x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } -#define ExitOnFailureSource(d, x, s, ...) if (FAILED(x)) { ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } -#define ExitOnRootFailureSource(d, x, s, ...) if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } -#define ExitOnFailureDebugTraceSource(d, x, s, ...) if (FAILED(x)) { ExitTraceDebugSource(d, x, s, __VA_ARGS__); goto LExit; } -#define ExitOnNullSource(d, p, x, e, s, ...) if (NULL == p) { x = e; Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } -#define ExitOnNullWithLastErrorSource(d, p, x, s, ...) if (NULL == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } -#define ExitOnNullDebugTraceSource(d, p, x, e, s, ...) if (NULL == p) { x = e; Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceDebugSource(d, x, s, __VA_ARGS__); goto LExit; } -#define ExitOnInvalidHandleWithLastErrorSource(d, p, x, s, ...) if (INVALID_HANDLE_VALUE == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } -#define ExitOnWin32ErrorSource(d, e, x, s, ...) if (ERROR_SUCCESS != e) { x = HRESULT_FROM_WIN32(e); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } - -#define ExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) -#define ExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) -#define ExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) -#define ExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) -#define ExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) -#define ExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) -#define ExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, s, __VA_ARGS__) -#define ExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, s, __VA_ARGS__) -#define ExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DEFAULT, p, x, e, s, __VA_ARGS__) -#define ExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, s, __VA_ARGS__) -#define ExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEFAULT, e, x, s, __VA_ARGS__) - -// release macros -#define ReleaseObject(x) if (x) { x->Release(); } -#define ReleaseObjectArray(prg, cel) if (prg) { for (DWORD Dutil_ReleaseObjectArrayIndex = 0; Dutil_ReleaseObjectArrayIndex < cel; ++Dutil_ReleaseObjectArrayIndex) { ReleaseObject(prg[Dutil_ReleaseObjectArrayIndex]); } ReleaseMem(prg); } -#define ReleaseVariant(x) { ::VariantClear(&x); } -#define ReleaseNullObject(x) if (x) { (x)->Release(); x = NULL; } -#define ReleaseCertificate(x) if (x) { ::CertFreeCertificateContext(x); x=NULL; } -#define ReleaseHandle(x) if (x) { ::CloseHandle(x); x = NULL; } - - -// useful defines and macros -#define Unused(x) ((void)x) - -#ifndef countof -#if 1 -#define countof(ary) (sizeof(ary) / sizeof(ary[0])) -#else -#ifndef __cplusplus -#define countof(ary) (sizeof(ary) / sizeof(ary[0])) -#else -template static char countofVerify(void const *, T) throw() { return 0; } -template static void countofVerify(T *const, T *const *) throw() {}; -#define countof(arr) (sizeof(countofVerify(arr,&(arr))) * sizeof(arr)/sizeof(*(arr))) -#endif -#endif -#endif - -#define roundup(x, n) roundup_typed(x, n, DWORD) -#define roundup_typed(x, n, t) (((t)(x) + ((t)(n) - 1)) & ~((t)(n) - 1)) - -#define HRESULT_FROM_RPC(x) ((HRESULT) ((x) | FACILITY_RPC)) - -#ifndef MAXSIZE_T -#define MAXSIZE_T ((SIZE_T)~((SIZE_T)0)) -#endif - -typedef const BYTE* LPCBYTE; - -#define E_FILENOTFOUND HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) -#define E_PATHNOTFOUND HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) -#define E_INVALIDDATA HRESULT_FROM_WIN32(ERROR_INVALID_DATA) -#define E_INVALIDSTATE HRESULT_FROM_WIN32(ERROR_INVALID_STATE) -#define E_INSUFFICIENT_BUFFER HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) -#define E_MOREDATA HRESULT_FROM_WIN32(ERROR_MORE_DATA) -#define E_NOMOREITEMS HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) -#define E_NOTFOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND) -#define E_MODNOTFOUND HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND) -#define E_BADCONFIGURATION HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION) - -#define AddRefAndRelease(x) { x->AddRef(); x->Release(); } - -#define MAKEDWORD(lo, hi) ((DWORD)MAKELONG(lo, hi)) -#define MAKEQWORDVERSION(mj, mi, b, r) (((DWORD64)MAKELONG(r, b)) | (((DWORD64)MAKELONG(mi, mj)) << 32)) - - -#ifdef __cplusplus -extern "C" { -#endif - -// other functions -HRESULT DAPI LoadSystemLibrary(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule); -HRESULT DAPI LoadSystemLibraryWithPath(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule, __deref_out_z_opt LPWSTR* psczPath); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/dutilsources.h b/src/dutil/inc/dutilsources.h deleted file mode 100644 index 7d512cb3..00000000 --- a/src/dutil/inc/dutilsources.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -typedef enum DUTIL_SOURCE -{ - DUTIL_SOURCE_UNKNOWN, - DUTIL_SOURCE_ACLUTIL, - DUTIL_SOURCE_APPUTIL, - DUTIL_SOURCE_APUPUTIL, - DUTIL_SOURCE_ATOMUTIL, - DUTIL_SOURCE_BUFFUTIL, - DUTIL_SOURCE_BUTIL, - DUTIL_SOURCE_CABCUTIL, - DUTIL_SOURCE_CABUTIL, - DUTIL_SOURCE_CERTUTIL, - DUTIL_SOURCE_CONUTIL, - DUTIL_SOURCE_CRYPUTIL, - DUTIL_SOURCE_DEPUTIL, - DUTIL_SOURCE_DICTUTIL, - DUTIL_SOURCE_DIRUTIL, - DUTIL_SOURCE_DLUTIL, - DUTIL_SOURCE_DPIUTIL, - DUTIL_SOURCE_DUTIL, - DUTIL_SOURCE_ESEUTIL, - DUTIL_SOURCE_FILEUTIL, - DUTIL_SOURCE_GDIPUTIL, - DUTIL_SOURCE_GUIDUTIL, - DUTIL_SOURCE_IIS7UTIL, - DUTIL_SOURCE_INETUTIL, - DUTIL_SOURCE_INIUTIL, - DUTIL_SOURCE_JSONUTIL, - DUTIL_SOURCE_LOCUTIL, - DUTIL_SOURCE_LOGUTIL, - DUTIL_SOURCE_MEMUTIL, - DUTIL_SOURCE_METAUTIL, - DUTIL_SOURCE_MONUTIL, - DUTIL_SOURCE_OSUTIL, - DUTIL_SOURCE_PATHUTIL, - DUTIL_SOURCE_PERFUTIL, - DUTIL_SOURCE_POLCUTIL, - DUTIL_SOURCE_PROCUTIL, - DUTIL_SOURCE_REGUTIL, - DUTIL_SOURCE_RESRUTIL, - DUTIL_SOURCE_RESWUTIL, - DUTIL_SOURCE_REXUTIL, - DUTIL_SOURCE_RMUTIL, - DUTIL_SOURCE_RSSUTIL, - DUTIL_SOURCE_SCEUTIL, - DUTIL_SOURCE_SCZUTIL, - DUTIL_SOURCE_SHELUTIL, - DUTIL_SOURCE_SQLUTIL, - DUTIL_SOURCE_SRPUTIL, - DUTIL_SOURCE_STRUTIL, - DUTIL_SOURCE_SVCUTIL, - DUTIL_SOURCE_THMUTIL, - DUTIL_SOURCE_TIMEUTIL, - DUTIL_SOURCE_UNCUTIL, - DUTIL_SOURCE_URIUTIL, - DUTIL_SOURCE_USERUTIL, - DUTIL_SOURCE_WIUTIL, - DUTIL_SOURCE_WUAUTIL, - DUTIL_SOURCE_XMLUTIL, - DUTIL_SOURCE_VERUTIL, - - DUTIL_SOURCE_EXTERNAL = 256, -} DUTIL_SOURCE; - -typedef enum REPORT_LEVEL -{ - REPORT_NONE, // turns off report (only valid for XXXSetLevel()) - REPORT_WARNING, // written if want only warnings or reporting is on in general - REPORT_STANDARD, // written if reporting is on - REPORT_VERBOSE, // written only if verbose reporting is on - REPORT_DEBUG, // reporting useful when debugging code - REPORT_ERROR, // always gets reported, but can never be specified -} REPORT_LEVEL; diff --git a/src/dutil/inc/eseutil.h b/src/dutil/inc/eseutil.h deleted file mode 100644 index bea47b2b..00000000 --- a/src/dutil/inc/eseutil.h +++ /dev/null @@ -1,223 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); } -#define ReleaseNullEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); pqh = NULL; } - -struct ESE_COLUMN_SCHEMA -{ - JET_COLUMNID jcColumn; - LPCWSTR pszName; - JET_COLTYP jcColumnType; - BOOL fKey; // If this column is part of the key of the table - BOOL fFixed; - BOOL fNullable; - BOOL fAutoIncrement; -}; - -struct ESE_TABLE_SCHEMA -{ - JET_TABLEID jtTable; - LPCWSTR pszName; - DWORD dwColumns; - ESE_COLUMN_SCHEMA *pcsColumns; -}; - -struct ESE_DATABASE_SCHEMA -{ - DWORD dwTables; - ESE_TABLE_SCHEMA *ptsTables; -}; - -typedef enum ESE_QUERY_TYPE -{ - ESE_QUERY_EXACT, - ESE_QUERY_FROM_TOP, - ESE_QUERY_FROM_BOTTOM -} ESE_QUERY_TYPE; - -typedef void* ESE_QUERY_HANDLE; - -HRESULT DAPI EseBeginSession( - __out JET_INSTANCE *pjiInstance, - __out JET_SESID *pjsSession, - __in_z LPCWSTR pszInstance, - __in_z LPCWSTR pszPath - ); -HRESULT DAPI EseEndSession( - __in JET_INSTANCE jiInstance, - __in JET_SESID jsSession - ); -HRESULT DAPI EseEnsureDatabase( - __in JET_SESID jsSession, - __in_z LPCWSTR pszFile, - __in ESE_DATABASE_SCHEMA *pdsSchema, - __out JET_DBID* pjdbDb, - __in BOOL fExclusive, - __in BOOL fReadonly - ); -HRESULT DAPI EseCloseDatabase( - __in JET_SESID jsSession, - __in JET_DBID jdbDb - ); -HRESULT DAPI EseCreateTable( - __in JET_SESID jsSession, - __in JET_DBID jdbDb, - __in_z LPCWSTR pszTable, - __out JET_TABLEID *pjtTable - ); -HRESULT DAPI EseOpenTable( - __in JET_SESID jsSession, - __in JET_DBID jdbDb, - __in_z LPCWSTR pszTable, - __out JET_TABLEID *pjtTable - ); -HRESULT DAPI EseCloseTable( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable - ); -HRESULT DAPI EseEnsureColumn( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in_z LPCWSTR pszColumnName, - __in JET_COLTYP jcColumnType, - __in ULONG ulColumnSize, - __in BOOL fFixed, - __in BOOL fNullable, - __out_opt JET_COLUMNID *pjcColumn - ); -HRESULT DAPI EseGetColumn( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in_z LPCWSTR pszColumnName, - __out JET_COLUMNID *pjcColumn - ); -HRESULT DAPI EseMoveCursor( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in LONG lRow - ); -HRESULT DAPI EseDeleteRow( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable - ); -HRESULT DAPI EseBeginTransaction( - __in JET_SESID jsSession - ); -HRESULT DAPI EseRollbackTransaction( - __in JET_SESID jsSession, - __in BOOL fAll - ); -HRESULT DAPI EseCommitTransaction( - __in JET_SESID jsSession - ); -HRESULT DAPI EsePrepareUpdate( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in ULONG ulPrep - ); -HRESULT DAPI EseFinishUpdate( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in BOOL fSeekToInsertedRecord - ); -HRESULT DAPI EseSetColumnBinary( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer - ); -HRESULT DAPI EseSetColumnDword( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in DWORD dwValue - ); -HRESULT DAPI EseSetColumnBool( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in BOOL fValue - ); -HRESULT DAPI EseSetColumnString( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __in_z LPCWSTR pszValue - ); -HRESULT DAPI EseSetColumnEmpty( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn - ); -HRESULT DAPI EseGetColumnBinary( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, - __inout SIZE_T* piBuffer - ); -HRESULT DAPI EseGetColumnDword( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __out DWORD *pdwValue - ); -HRESULT DAPI EseGetColumnBool( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __out BOOL *pfValue - ); -HRESULT DAPI EseGetColumnString( - __in JET_SESID jsSession, - __in ESE_TABLE_SCHEMA tsTable, - __in DWORD dwColumn, - __out LPWSTR *ppszValue - ); - -// Call this once for each key column in the table -HRESULT DAPI EseBeginQuery( - __in JET_SESID jsSession, - __in JET_TABLEID jtTable, - __in ESE_QUERY_TYPE qtQueryType, - __out ESE_QUERY_HANDLE *peqhHandle - ); -HRESULT DAPI EseSetQueryColumnBinary( - __in ESE_QUERY_HANDLE eqhHandle, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" - ); -HRESULT DAPI EseSetQueryColumnDword( - __in ESE_QUERY_HANDLE eqhHandle, - __in DWORD dwData, - __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" - ); -HRESULT DAPI EseSetQueryColumnBool( - __in ESE_QUERY_HANDLE eqhHandle, - __in BOOL fValue, - __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" - ); -HRESULT DAPI EseSetQueryColumnString( - __in ESE_QUERY_HANDLE eqhHandle, - __in_z LPCWSTR pszString, - __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" - ); -HRESULT DAPI EseFinishQuery( - __in ESE_QUERY_HANDLE eqhHandle - ); -// Once all columns have been set up, call this and read the result -HRESULT DAPI EseRunQuery( - __in ESE_QUERY_HANDLE eqhHandle - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/fileutil.h b/src/dutil/inc/fileutil.h deleted file mode 100644 index d3e326f7..00000000 --- a/src/dutil/inc/fileutil.h +++ /dev/null @@ -1,247 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseFile(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; } -#define ReleaseFileHandle(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; } -#define ReleaseFileFindHandle(h) if (INVALID_HANDLE_VALUE != h) { ::FindClose(h); h = INVALID_HANDLE_VALUE; } - -#define FILEMAKEVERSION(major, minor, build, revision) static_cast((static_cast(major & 0xFFFF) << 48) \ - | (static_cast(minor & 0xFFFF) << 32) \ - | (static_cast(build & 0xFFFF) << 16) \ - | (static_cast(revision & 0xFFFF))) - -typedef enum FILE_ARCHITECTURE -{ - FILE_ARCHITECTURE_UNKNOWN, - FILE_ARCHITECTURE_X86, - FILE_ARCHITECTURE_X64, - FILE_ARCHITECTURE_IA64, -} FILE_ARCHITECTURE; - -typedef enum FILE_ENCODING -{ - FILE_ENCODING_UNSPECIFIED = 0, - // TODO: distinguish between non-BOM utf-8 and ANSI in the future? - FILE_ENCODING_UTF8, - FILE_ENCODING_UTF8_WITH_BOM, - FILE_ENCODING_UTF16, - FILE_ENCODING_UTF16_WITH_BOM, -} FILE_ENCODING; - - -LPWSTR DAPI FileFromPath( - __in_z LPCWSTR wzPath - ); -HRESULT DAPI FileResolvePath( - __in_z LPCWSTR wzRelativePath, - __out LPWSTR *ppwzFullPath - ); -HRESULT DAPI FileStripExtension( - __in_z LPCWSTR wzFileName, - __out LPWSTR *ppwzFileNameNoExtension - ); -HRESULT DAPI FileChangeExtension( - __in_z LPCWSTR wzFileName, - __in_z LPCWSTR wzNewExtension, - __out LPWSTR *ppwzFileNameNewExtension - ); -HRESULT DAPI FileAddSuffixToBaseName( - __in_z LPCWSTR wzFileName, - __in_z LPCWSTR wzSuffix, - __out_z LPWSTR* psczNewFileName - ); -HRESULT DAPI FileVersionFromString( - __in_z LPCWSTR wzVersion, - __out DWORD *pdwVerMajor, - __out DWORD* pdwVerMinor - ); -HRESULT DAPI FileVersionFromStringEx( - __in_z LPCWSTR wzVersion, - __in SIZE_T cchVersion, - __out DWORD64* pqwVersion - ); -HRESULT DAPI FileVersionToStringEx( - __in DWORD64 qwVersion, - __out LPWSTR* psczVersion - ); -HRESULT DAPI FileSetPointer( - __in HANDLE hFile, - __in DWORD64 dw64Move, - __out_opt DWORD64* pdw64NewPosition, - __in DWORD dwMoveMethod - ); -HRESULT DAPI FileSize( - __in_z LPCWSTR pwzFileName, - __out LONGLONG* pllSize - ); -HRESULT DAPI FileSizeByHandle( - __in HANDLE hFile, - __out LONGLONG* pllSize - ); -BOOL DAPI FileExistsEx( - __in_z LPCWSTR wzPath, - __out_opt DWORD *pdwAttributes - ); -BOOL DAPI FileExistsAfterRestart( - __in_z LPCWSTR wzPath, - __out_opt DWORD *pdwAttributes - ); -HRESULT DAPI FileRemoveFromPendingRename( - __in_z LPCWSTR wzPath - ); -HRESULT DAPI FileRead( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath - ); -HRESULT DAPI FileReadEx( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in DWORD dwShareMode - ); -HRESULT DAPI FileReadUntil( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in DWORD cbMaxRead - ); -HRESULT DAPI FileReadPartial( - __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in BOOL fSeek, - __in DWORD cbStartPosition, - __in DWORD cbMaxRead, - __in BOOL fPartialOK - ); -HRESULT DAPI FileReadPartialEx( - __deref_inout_bcount_full(*pcbDest) LPBYTE* ppbDest, - __out_range(<=, cbMaxRead) SIZE_T* pcbDest, - __in_z LPCWSTR wzSrcPath, - __in BOOL fSeek, - __in DWORD cbStartPosition, - __in DWORD cbMaxRead, - __in BOOL fPartialOK, - __in DWORD dwShareMode - ); -HRESULT DAPI FileReadHandle( - __in HANDLE hFile, - __in_bcount(cbDest) LPBYTE pbDest, - __in SIZE_T cbDest - ); -HRESULT DAPI FileWrite( - __in_z LPCWSTR pwzFileName, - __in DWORD dwFlagsAndAttributes, - __in_bcount_opt(cbData) LPCBYTE pbData, - __in SIZE_T cbData, - __out_opt HANDLE* pHandle - ); -HRESULT DAPI FileWriteHandle( - __in HANDLE hFile, - __in_bcount_opt(cbData) LPCBYTE pbData, - __in SIZE_T cbData - ); -HRESULT DAPI FileCopyUsingHandles( - __in HANDLE hSource, - __in HANDLE hTarget, - __in DWORD64 cbCopy, - __out_opt DWORD64* pcbCopied - ); -HRESULT DAPI FileCopyUsingHandlesWithProgress( - __in HANDLE hSource, - __in HANDLE hTarget, - __in DWORD64 cbCopy, - __in_opt LPPROGRESS_ROUTINE lpProgressRoutine, - __in_opt LPVOID lpData - ); -HRESULT DAPI FileEnsureCopy( - __in_z LPCWSTR wzSource, - __in_z LPCWSTR wzTarget, - __in BOOL fOverwrite - ); -HRESULT DAPI FileEnsureCopyWithRetry( - __in LPCWSTR wzSource, - __in LPCWSTR wzTarget, - __in BOOL fOverwrite, - __in DWORD cRetry, - __in DWORD dwWaitMilliseconds - ); -HRESULT DAPI FileEnsureMove( - __in_z LPCWSTR wzSource, - __in_z LPCWSTR wzTarget, - __in BOOL fOverwrite, - __in BOOL fAllowCopy - ); -HRESULT DAPI FileEnsureMoveWithRetry( - __in LPCWSTR wzSource, - __in LPCWSTR wzTarget, - __in BOOL fOverwrite, - __in BOOL fAllowCopy, - __in DWORD cRetry, - __in DWORD dwWaitMilliseconds - ); -HRESULT DAPI FileCreateTemp( - __in_z LPCWSTR wzPrefix, - __in_z LPCWSTR wzExtension, - __deref_opt_out_z LPWSTR* ppwzTempFile, - __out_opt HANDLE* phTempFile - ); -HRESULT DAPI FileCreateTempW( - __in_z LPCWSTR wzPrefix, - __in_z LPCWSTR wzExtension, - __deref_opt_out_z LPWSTR* ppwzTempFile, - __out_opt HANDLE* phTempFile - ); -HRESULT DAPI FileVersion( - __in_z LPCWSTR wzFilename, - __out DWORD *pdwVerMajor, - __out DWORD* pdwVerMinor - ); -HRESULT DAPI FileIsSame( - __in_z LPCWSTR wzFile1, - __in_z LPCWSTR wzFile2, - __out LPBOOL lpfSameFile - ); -HRESULT DAPI FileEnsureDelete( - __in_z LPCWSTR wzFile - ); -HRESULT DAPI FileGetTime( - __in_z LPCWSTR wzFile, - __out_opt LPFILETIME lpCreationTime, - __out_opt LPFILETIME lpLastAccessTime, - __out_opt LPFILETIME lpLastWriteTime - ); -HRESULT DAPI FileSetTime( - __in_z LPCWSTR wzFile, - __in_opt const FILETIME *lpCreationTime, - __in_opt const FILETIME *lpLastAccessTime, - __in_opt const FILETIME *lpLastWriteTime - ); -HRESULT DAPI FileResetTime( - __in_z LPCWSTR wzFile - ); -HRESULT DAPI FileExecutableArchitecture( - __in_z LPCWSTR wzFile, - __out FILE_ARCHITECTURE *pArchitecture - ); -HRESULT DAPI FileToString( - __in_z LPCWSTR wzFile, - __out LPWSTR *psczString, - __out_opt FILE_ENCODING *pfeEncoding - ); -HRESULT DAPI FileFromString( - __in_z LPCWSTR wzFile, - __in DWORD dwFlagsAndAttributes, - __in_z LPCWSTR sczString, - __in FILE_ENCODING feEncoding - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/gdiputil.h b/src/dutil/inc/gdiputil.h deleted file mode 100644 index f2145828..00000000 --- a/src/dutil/inc/gdiputil.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#define ExitOnGdipFailureSource(d, g, x, s, ...) { x = GdipHresultFromStatus(g); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } } -#define ExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DEFAULT, g, x, s, __VA_ARGS__) - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI GdipInitialize( - __in const Gdiplus::GdiplusStartupInput* pInput, - __out ULONG_PTR* pToken, - __out_opt Gdiplus::GdiplusStartupOutput *pOutput - ); - -void DAPI GdipUninitialize( - __in ULONG_PTR token - ); - -HRESULT DAPI GdipBitmapFromResource( - __in_opt HINSTANCE hinst, - __in_z LPCSTR szId, - __out Gdiplus::Bitmap **ppBitmap - ); - -HRESULT DAPI GdipBitmapFromFile( - __in_z LPCWSTR wzFileName, - __out Gdiplus::Bitmap **ppBitmap - ); - -HRESULT DAPI GdipHresultFromStatus( - __in Gdiplus::Status gs - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/guidutil.h b/src/dutil/inc/guidutil.h deleted file mode 100644 index 478a464f..00000000 --- a/src/dutil/inc/guidutil.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define GUID_STRING_LENGTH 39 - -HRESULT DAPI GuidFixedCreate( - _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid - ); - -HRESULT DAPI GuidCreate( - __deref_out_z LPWSTR* psczGuid - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/iis7util.h b/src/dutil/inc/iis7util.h deleted file mode 100644 index 3572b4e9..00000000 --- a/src/dutil/inc/iis7util.h +++ /dev/null @@ -1,222 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -// IIS Config schema names -#define IIS_CONFIG_ADD L"add" -#define IIS_CONFIG_ALLOWED L"allowed" -#define IIS_CONFIG_APPHOST_ROOT L"MACHINE/WEBROOT/APPHOST" -#define IIS_CONFIG_APPLICATION L"application" -#define IIS_CONFIG_APPPOOL L"applicationPool" -#define IIS_CONFIG_APPPOOL_AUTO L"autoStart" -#define IIS_CONFIG_APPPOOL_SECTION L"system.applicationHost/applicationPools" -#define IIS_CONFIG_AUTOSTART L"serverAutoStart" -#define IIS_CONFIG_BINDING L"binding" -#define IIS_CONFIG_BINDINGINFO L"bindingInformation" -#define IIS_CONFIG_BINDINGS L"bindings" -#define IIS_CONFIG_DESC L"description" -#define IIS_CONFIG_EXECUTABLE L"scriptProcessor" -#define IIS_CONFIG_ENABLED L"enabled" -#define IIS_CONFIG_ENABLE32 L"enable32BitAppOnWin64" -#define IIS_CONFIG_FILEEXT L"fileExtension" -#define IIS_CONFIG_FILTER L"filter" -#define IIS_CONFIG_GROUPID L"groupId" -#define IIS_CONFIG_HEADERS L"customHeaders" -#define IIS_CONFIG_HTTPERRORS_SECTION L"system.webServer/httpErrors" -#define IIS_CONFIG_ID L"id" -#define IIS_CONFIG_ISAPI_SECTION L"system.webServer/isapiFilters" -#define IIS_CONFIG_HTTPPROTO_SECTION L"system.webServer/httpProtocol" -#define IIS_CONFIG_LOG_SECTION L"system.applicationHost/log" -#define IIS_CONFIG_LOG_UTF8 L"logInUTF8" -#define IIS_CONFIG_LIMITS L"limits" -#define IIS_CONFIG_PIPELINEMODE L"managedPipelineMode" -#define IIS_CONFIG_MANAGEDRUNTIMEVERSION L"managedRuntimeVersion" -#define IIS_CONFIG_WEBLOG L"logFile" -#define IIS_CONFIG_LOGFORMAT L"logFormat" -#define IIS_CONFIG_MIMEMAP L"mimeMap" -#define IIS_CONFIG_MIMETYPE L"mimeType" -#define IIS_CONFIG_MODULES L"modules" -#define IIS_CONFIG_NAME L"name" -#define IIS_CONFIG_PATH L"path" -#define IIS_CONFIG_PHYSPATH L"physicalPath" -#define IIS_CONFIG_PROTOCOL L"protocol" -#define IIS_CONFIG_RESTRICTION_SECTION L"system.webServer/security/isapiCgiRestriction" -#define IIS_CONFIG_SITE L"site" -#define IIS_CONFIG_SITE_ID L"id" -#define IIS_CONFIG_SITES_SECTION L"system.applicationHost/sites" -#define IIS_CONFIG_CONNECTTIMEOUT L"connectionTimeout" -#define IIS_CONFIG_VDIR L"virtualDirectory" -#define IIS_CONFIG_VALUE L"value" -#define IIS_CONFIG_VERBS L"verb" -#define IIS_CONFIG_WEBLIMITS_SECTION L"system.applicationHost/webLimits" -#define IIS_CONFIG_WEBLIMITS_MAXBAND L"maxGlobalBandwidth" -#define IIS_CONFIG_TRUE L"true" -#define IIS_CONFIG_FALSE L"false" -#define IIS_CONFIG_ERROR L"error" -#define IIS_CONFIG_STATUSCODE L"statusCode" -#define IIS_CONFIG_SUBSTATUS L"subStatusCode" -#define IIS_CONFIG_LANGPATH L"prefixLanguageFilePath" -#define IIS_CONFIG_RESPMODE L"responseMode" -#define IIS_CONFIG_CLEAR L"clear" -#define IIS_CONFIG_RECYCLING L"recycling" -#define IIS_CONFIG_PEROIDRESTART L"periodicRestart" -#define IIS_CONFIG_TIME L"time" -#define IIS_CONFIG_REQUESTS L"requests" -#define IIS_CONFIG_SCHEDULE L"schedule" -#define IIS_CONFIG_MEMORY L"memory" -#define IIS_CONFIG_PRIVMEMORY L"privateMemory" -#define IIS_CONFIG_PROCESSMODEL L"processModel" -#define IIS_CONFIG_IDLETIMEOUT L"idleTimeout" -#define IIS_CONFIG_QUEUELENGTH L"queueLength" -#define IIS_CONFIG_IDENITITYTYPE L"identityType" -#define IIS_CONFIG_LOCALSYSTEM L"LocalSystem" -#define IIS_CONFIG_LOCALSERVICE L"LocalService" -#define IIS_CONFIG_NETWORKSERVICE L"NetworkService" -#define IIS_CONFIG_SPECIFICUSER L"SpecificUser" -#define IIS_CONFIG_APPLICATIONPOOLIDENTITY L"ApplicationPoolIdentity" -#define IIS_CONFIG_USERNAME L"userName" -#define IIS_CONFIG_PASSWORD L"password" -#define IIS_CONFIG_CPU L"cpu" -#define IIS_CONFIG_LIMIT L"limit" -#define IIS_CONFIG_CPU_ACTION L"action" -#define IIS_CONFIG_KILLW3WP L"KillW3wp" -#define IIS_CONFIG_NOACTION L"NoAction" -#define IIS_CONFIG_RESETINTERVAL L"resetInterval" -#define IIS_CONFIG_MAXWRKPROCESSES L"maxProcesses" -#define IIS_CONFIG_HANDLERS_SECTION L"system.webServer/handlers" -#define IIS_CONFIG_DEFAULTDOC_SECTION L"system.webServer/defaultDocument" -#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp" -#define IIS_CONFIG_SCRIPTERROR L"scriptErrorSentToBrowser" -#define IIS_CONFIG_STATICCONTENT_SECTION L"system.webServer/staticContent" -#define IIS_CONFIG_HTTPEXPIRES L"httpExpires" -#define IIS_CONFIG_MAXAGE L"cacheControlMaxAge" -#define IIS_CONFIG_CLIENTCACHE L"clientCache" -#define IIS_CONFIG_CACHECONTROLMODE L"cacheControlMode" -#define IIS_CONFIG_USEMAXAGE L"UseMaxAge" -#define IIS_CONFIG_USEEXPIRES L"UseExpires" -#define IIS_CONFIG_CACHECUST L"cacheControlCustom" -#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp" -#define IIS_CONFIG_SESSION L"session" -#define IIS_CONFIG_ALLOWSTATE L"allowSessionState" -#define IIS_CONFIG_TIMEOUT L"timeout" -#define IIS_CONFIG_BUFFERING L"bufferingOn" -#define IIS_CONFIG_PARENTPATHS L"enableParentPaths" -#define IIS_CONFIG_SCRIPTLANG L"scriptLanguage" -#define IIS_CONFIG_SCRIPTTIMEOUT L"scriptTimeout" -#define IIS_CONFIG_LIMITS L"limits" -#define IIS_CONFIG_ALLOWDEBUG L"appAllowDebugging" -#define IIS_CONFIG_ALLOWCLIENTDEBUG L"appAllowClientDebug" -#define IIS_CONFIG_CERTIFICATEHASH L"certificateHash" -#define IIS_CONFIG_CERTIFICATESTORENAME L"certificateStoreName" -#define IIS_CONFIG_HTTPLOGGING_SECTION L"system.webServer/httpLogging" -#define IIS_CONFIG_DONTLOG L"dontLog" - -typedef BOOL (CALLBACK* ENUMAPHOSTELEMENTPROC)(IAppHostElement*, LPVOID); -typedef BOOL (CALLBACK* VARIANTCOMPARATORPROC)(VARIANT*, VARIANT*); - -HRESULT DAPI Iis7PutPropertyVariant( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in VARIANT vtPut - ); - -HRESULT DAPI Iis7PutPropertyInteger( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in DWORD dValue - ); - -HRESULT DAPI Iis7PutPropertyString( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in LPCWSTR wzString - ); - -HRESULT DAPI Iis7PutPropertyBool( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in BOOL fValue); - -HRESULT DAPI Iis7GetPropertyVariant( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in VARIANT* vtGet - ); - -HRESULT DAPI Iis7GetPropertyString( - __in IAppHostElement *pElement, - __in LPCWSTR wzPropName, - __in LPWSTR* psczGet - ); - -struct IIS7_APPHOSTELEMENTCOMPARISON -{ - LPCWSTR sczElementName; - LPCWSTR sczAttributeName; - VARIANT* pvAttributeValue; - VARIANTCOMPARATORPROC pComparator; -}; - -BOOL DAPI Iis7IsMatchingAppHostElement( - __in IAppHostElement *pElement, - __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison - ); - -HRESULT DAPI Iis7FindAppHostElementString( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in LPCWSTR wzAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ); - -HRESULT DAPI Iis7FindAppHostElementPath( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in LPCWSTR wzAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ); - -HRESULT DAPI Iis7FindAppHostElementInteger( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in DWORD dwAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ); - -HRESULT DAPI Iis7FindAppHostElementVariant( - __in IAppHostElementCollection *pCollection, - __in LPCWSTR wzElementName, - __in LPCWSTR wzAttributeName, - __in VARIANT* pvAttributeValue, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ); - -HRESULT DAPI Iis7EnumAppHostElements( - __in IAppHostElementCollection *pCollection, - __in ENUMAPHOSTELEMENTPROC pCallback, - __in LPVOID pContext, - __out IAppHostElement** ppElement, - __out DWORD* pdwIndex - ); - -HRESULT DAPI Iis7FindAppHostMethod( - __in IAppHostMethodCollection *pCollection, - __in LPCWSTR wzMethodName, - __out IAppHostMethod** ppMethod, - __out DWORD* pdwIndex - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/inetutil.h b/src/dutil/inc/inetutil.h deleted file mode 100644 index 19ace88b..00000000 --- a/src/dutil/inc/inetutil.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; } -#define ReleaseNullInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; } - - -// functions -HRESULT DAPI InternetGetSizeByHandle( - __in HINTERNET hiFile, - __out LONGLONG* pllSize - ); - -HRESULT DAPI InternetGetCreateTimeByHandle( - __in HINTERNET hiFile, - __out LPFILETIME pft - ); - -HRESULT DAPI InternetQueryInfoString( - __in HINTERNET h, - __in DWORD dwInfo, - __deref_out_z LPWSTR* psczValue - ); - -HRESULT DAPI InternetQueryInfoNumber( - __in HINTERNET h, - __in DWORD dwInfo, - __inout LONG* plInfo - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/iniutil.h b/src/dutil/inc/iniutil.h deleted file mode 100644 index c8503155..00000000 --- a/src/dutil/inc/iniutil.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseIni(ih) if (ih) { IniUninitialize(ih); } -#define ReleaseNullIni(ih) if (ih) { IniUninitialize(ih); ih = NULL; } - -typedef void* INI_HANDLE; -typedef const void* C_INI_HANDLE; - -extern const int INI_HANDLE_BYTES; - -struct INI_VALUE -{ - LPCWSTR wzName; - LPCWSTR wzValue; - - DWORD dwLineNumber; -}; - -HRESULT DAPI IniInitialize( - __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle - ); -void DAPI IniUninitialize( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle - ); -HRESULT DAPI IniSetOpenTag( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzOpenTagPrefix, - __in_z_opt LPCWSTR wzOpenTagPostfix - ); -HRESULT DAPI IniSetValueStyle( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzValuePrefix, - __in_z_opt LPCWSTR wzValueSeparator - ); -HRESULT DAPI IniSetValueSeparatorException( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z LPCWSTR wzValueNamePrefix - ); -HRESULT DAPI IniSetCommentStyle( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzLinePrefix - ); -HRESULT DAPI IniParse( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in LPCWSTR wzPath, - __out_opt FILE_ENCODING *pfeEncodingFound - ); -// Gets the full value array, this includes values that may have been deleted -// (their value will be NULL) -HRESULT DAPI IniGetValueList( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues, - __out DWORD *pcValues - ); -HRESULT DAPI IniGetValue( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in LPCWSTR wzValueName, - __deref_out_z LPWSTR* psczValue - ); -HRESULT DAPI IniSetValue( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in LPCWSTR wzValueName, - __in_z_opt LPCWSTR wzValue - ); -HRESULT DAPI IniWriteFile( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzPath, - __in FILE_ENCODING feOverrideEncoding - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/jsonutil.h b/src/dutil/inc/jsonutil.h deleted file mode 100644 index b05e9970..00000000 --- a/src/dutil/inc/jsonutil.h +++ /dev/null @@ -1,112 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum JSON_TOKEN -{ - JSON_TOKEN_NONE, - JSON_TOKEN_ARRAY_START, - JSON_TOKEN_ARRAY_VALUE, - JSON_TOKEN_ARRAY_END, - JSON_TOKEN_OBJECT_START, - JSON_TOKEN_OBJECT_KEY, - JSON_TOKEN_OBJECT_VALUE, - JSON_TOKEN_OBJECT_END, - JSON_TOKEN_VALUE, -} JSON_TOKEN; - -typedef struct _JSON_VALUE -{ -} JSON_VALUE; - -typedef struct _JSON_READER -{ - CRITICAL_SECTION cs; - LPWSTR sczJson; - - LPWSTR pwz; - JSON_TOKEN token; -} JSON_READER; - -typedef struct _JSON_WRITER -{ - CRITICAL_SECTION cs; - LPWSTR sczJson; - - JSON_TOKEN* rgTokenStack; - DWORD cTokens; - DWORD cMaxTokens; -} JSON_WRITER; - - -DAPI_(HRESULT) JsonInitializeReader( - __in_z LPCWSTR wzJson, - __in JSON_READER* pReader - ); - -DAPI_(void) JsonUninitializeReader( - __in JSON_READER* pReader - ); - -DAPI_(HRESULT) JsonReadNext( - __in JSON_READER* pReader, - __out JSON_TOKEN* pToken, - __out JSON_VALUE* pValue - ); - -DAPI_(HRESULT) JsonReadValue( - __in JSON_READER* pReader, - __in JSON_VALUE* pValue - ); - -DAPI_(HRESULT) JsonInitializeWriter( - __in JSON_WRITER* pWriter - ); - -DAPI_(void) JsonUninitializeWriter( - __in JSON_WRITER* pWriter - ); - -DAPI_(HRESULT) JsonWriteBool( - __in JSON_WRITER* pWriter, - __in BOOL fValue - ); - -DAPI_(HRESULT) JsonWriteNumber( - __in JSON_WRITER* pWriter, - __in DWORD dwValue - ); - -DAPI_(HRESULT) JsonWriteString( - __in JSON_WRITER* pWriter, - __in_z LPCWSTR wzValue - ); - -DAPI_(HRESULT) JsonWriteArrayStart( - __in JSON_WRITER* pWriter - ); - -DAPI_(HRESULT) JsonWriteArrayEnd( - __in JSON_WRITER* pWriter - ); - -DAPI_(HRESULT) JsonWriteObjectStart( - __in JSON_WRITER* pWriter - ); - -DAPI_(HRESULT) JsonWriteObjectKey( - __in JSON_WRITER* pWriter, - __in_z LPCWSTR wzKey - ); - -DAPI_(HRESULT) JsonWriteObjectEnd( - __in JSON_WRITER* pWriter - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/locutil.h b/src/dutil/inc/locutil.h deleted file mode 100644 index 38ddda20..00000000 --- a/src/dutil/inc/locutil.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -struct LOC_STRING -{ - LPWSTR wzId; - LPWSTR wzText; - BOOL bOverridable; -}; - -const int LOC_CONTROL_NOT_SET = INT_MAX; - -struct LOC_CONTROL -{ - LPWSTR wzControl; - int nX; - int nY; - int nWidth; - int nHeight; - LPWSTR wzText; -}; - -const int WIX_LOCALIZATION_LANGUAGE_NOT_SET = INT_MAX; - -struct WIX_LOCALIZATION -{ - DWORD dwLangId; - - DWORD cLocStrings; - LOC_STRING* rgLocStrings; - - DWORD cLocControls; - LOC_CONTROL* rgLocControls; -}; - -/******************************************************************** - LocProbeForFile - Searches for a localization file on disk. - -*******************************************************************/ -HRESULT DAPI LocProbeForFile( - __in_z LPCWSTR wzBasePath, - __in_z LPCWSTR wzLocFileName, - __in_z_opt LPCWSTR wzLanguage, - __inout LPWSTR* psczPath - ); - -/******************************************************************** - LocLoadFromFile - Loads a localization file - -*******************************************************************/ -HRESULT DAPI LocLoadFromFile( - __in_z LPCWSTR wzWxlFile, - __out WIX_LOCALIZATION** ppWixLoc - ); - -/******************************************************************** - LocLoadFromResource - loads a localization file from a module's data - resource. - - NOTE: The resource data must be UTF-8 encoded. -*******************************************************************/ -HRESULT DAPI LocLoadFromResource( - __in HMODULE hModule, - __in_z LPCSTR szResource, - __out WIX_LOCALIZATION** ppWixLoc - ); - -/******************************************************************** - LocFree - free memory allocated when loading a localization file - -*******************************************************************/ -void DAPI LocFree( - __in_opt WIX_LOCALIZATION* pWixLoc - ); - -/******************************************************************** - LocLocalizeString - replace any #(loc.id) in a string with the - correct sub string -*******************************************************************/ -HRESULT DAPI LocLocalizeString( - __in const WIX_LOCALIZATION* pWixLoc, - __inout LPWSTR* psczInput - ); - -/******************************************************************** - LocGetControl - returns a control's localization information -*******************************************************************/ -HRESULT DAPI LocGetControl( - __in const WIX_LOCALIZATION* pWixLoc, - __in_z LPCWSTR wzId, - __out LOC_CONTROL** ppLocControl - ); - -/******************************************************************** -LocGetString - returns a string's localization information -*******************************************************************/ -extern "C" HRESULT DAPI LocGetString( - __in const WIX_LOCALIZATION* pWixLoc, - __in_z LPCWSTR wzId, - __out LOC_STRING** ppLocString - ); - -/******************************************************************** -LocAddString - adds a localization string -*******************************************************************/ -extern "C" HRESULT DAPI LocAddString( - __in WIX_LOCALIZATION* pWixLoc, - __in_z LPCWSTR wzId, - __in_z LPCWSTR wzLocString, - __in BOOL bOverridable - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/logutil.h b/src/dutil/inc/logutil.h deleted file mode 100644 index 426506ee..00000000 --- a/src/dutil/inc/logutil.h +++ /dev/null @@ -1,192 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define LogExitOnFailureSource(d, x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } -#define LogExitOnRootFailureSource(d, x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } - -#define LogExitOnFailure(x, i, f, ...) LogExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, i, f, __VA_ARGS__) -#define LogExitOnRootFailure(x, i, f, ...) LogExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, i, f, __VA_ARGS__) - -typedef HRESULT (DAPI *PFN_LOGSTRINGWORKRAW)( - __in_z LPCSTR szString, - __in_opt LPVOID pvContext - ); - -// enums - -// structs - -// functions -BOOL DAPI IsLogInitialized(); - -BOOL DAPI IsLogOpen(); - -void DAPI LogInitialize( - __in HMODULE hModule - ); - -HRESULT DAPI LogOpen( - __in_z_opt LPCWSTR wzDirectory, - __in_z LPCWSTR wzLog, - __in_z_opt LPCWSTR wzPostfix, - __in_z_opt LPCWSTR wzExt, - __in BOOL fAppend, - __in BOOL fHeader, - __out_z_opt LPWSTR* psczLogPath - ); - -void DAPI LogDisable(); - -void DAPI LogRedirect( - __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw, - __in_opt LPVOID pvContext - ); - -HRESULT DAPI LogRename( - __in_z LPCWSTR wzNewPath - ); - -void DAPI LogClose( - __in BOOL fFooter - ); - -void DAPI LogUninitialize( - __in BOOL fFooter - ); - -BOOL DAPI LogIsOpen(); - -HRESULT DAPI LogSetSpecialParams( - __in_z_opt LPCWSTR wzSpecialBeginLine, - __in_z_opt LPCWSTR wzSpecialAfterTimeStamp, - __in_z_opt LPCWSTR wzSpecialEndLine - ); - -REPORT_LEVEL DAPI LogSetLevel( - __in REPORT_LEVEL rl, - __in BOOL fLogChange - ); - -REPORT_LEVEL DAPI LogGetLevel(); - -HRESULT DAPI LogGetPath( - __out_ecount_z(cchLogPath) LPWSTR pwzLogPath, - __in DWORD cchLogPath - ); - -HANDLE DAPI LogGetHandle(); - -HRESULT DAPIV LogString( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - ... - ); - -HRESULT DAPI LogStringArgs( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ); - -HRESULT DAPIV LogStringLine( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - ... - ); - -HRESULT DAPI LogStringLineArgs( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ); - -HRESULT DAPI LogIdModuleArgs( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - __in_opt HMODULE hModule, - __in va_list args - ); - -/* - * Wraps LogIdModuleArgs, so inline to save the function call - */ - -inline HRESULT LogId( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - ... - ) -{ - HRESULT hr = S_OK; - va_list args; - - va_start(args, dwLogId); - hr = LogIdModuleArgs(rl, dwLogId, NULL, args); - va_end(args); - - return hr; -} - - -/* - * Wraps LogIdModuleArgs, so inline to save the function call - */ - -inline HRESULT LogIdArgs( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - __in va_list args - ) -{ - return LogIdModuleArgs(rl, dwLogId, NULL, args); -} - -HRESULT DAPIV LogErrorString( - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - ... - ); - -HRESULT DAPI LogErrorStringArgs( - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ); - -HRESULT DAPI LogErrorIdModule( - __in HRESULT hrError, - __in DWORD dwLogId, - __in_opt HMODULE hModule, - __in_z_opt LPCWSTR wzString1, - __in_z_opt LPCWSTR wzString2, - __in_z_opt LPCWSTR wzString3 - ); - -inline HRESULT LogErrorId( - __in HRESULT hrError, - __in DWORD dwLogId, - __in_z_opt LPCWSTR wzString1 = NULL, - __in_z_opt LPCWSTR wzString2 = NULL, - __in_z_opt LPCWSTR wzString3 = NULL - ) -{ - return LogErrorIdModule(hrError, dwLogId, NULL, wzString1, wzString2, wzString3); -} - -HRESULT DAPI LogHeader(); - -HRESULT DAPI LogFooter(); - -HRESULT LogStringWorkRaw( - __in_z LPCSTR szLogData - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/memutil.h b/src/dutil/inc/memutil.h deleted file mode 100644 index 49f86e0a..00000000 --- a/src/dutil/inc/memutil.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseMem(p) if (p) { MemFree(p); } -#define ReleaseNullMem(p) if (p) { MemFree(p); p = NULL; } - -HRESULT DAPI MemInitialize(); -void DAPI MemUninitialize(); - -LPVOID DAPI MemAlloc( - __in SIZE_T cbSize, - __in BOOL fZero - ); -LPVOID DAPI MemReAlloc( - __in LPVOID pv, - __in SIZE_T cbSize, - __in BOOL fZero - ); -HRESULT DAPI MemReAllocSecure( - __in LPVOID pv, - __in SIZE_T cbSize, - __in BOOL fZero, - __deref_out LPVOID* ppvNew - ); -HRESULT DAPI MemAllocArray( - __inout LPVOID* ppvArray, - __in SIZE_T cbArrayType, - __in DWORD dwItemCount - ); -HRESULT DAPI MemReAllocArray( - __inout LPVOID* ppvArray, - __in DWORD cArray, - __in SIZE_T cbArrayType, - __in DWORD dwNewItemCount - ); -HRESULT DAPI MemEnsureArraySize( - __deref_inout_bcount(cArray * cbArrayType) LPVOID* ppvArray, - __in DWORD cArray, - __in SIZE_T cbArrayType, - __in DWORD dwGrowthCount - ); -HRESULT DAPI MemInsertIntoArray( - __deref_inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, - __in DWORD dwInsertIndex, - __in DWORD cInsertItems, - __in DWORD cExistingArray, - __in SIZE_T cbArrayType, - __in DWORD dwGrowthCount - ); -void DAPI MemRemoveFromArray( - __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, - __in DWORD dwRemoveIndex, - __in DWORD cRemoveItems, - __in DWORD cExistingArray, - __in SIZE_T cbArrayType, - __in BOOL fPreserveOrder - ); -void DAPI MemArraySwapItems( - __inout_bcount(cbArrayType) LPVOID pvArray, - __in DWORD dwIndex1, - __in DWORD dwIndex2, - __in SIZE_T cbArrayType - ); - -HRESULT DAPI MemFree( - __in LPVOID pv - ); -SIZE_T DAPI MemSize( - __in LPCVOID pv - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/metautil.h b/src/dutil/inc/metautil.h deleted file mode 100644 index 31f9ff5c..00000000 --- a/src/dutil/inc/metautil.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// structs - -// prototypes -HRESULT DAPI MetaFindWebBase( - __in IMSAdminBaseW* piMetabase, - __in_z LPCWSTR wzIP, - __in int iPort, - __in_z LPCWSTR wzHeader, - __in BOOL fSecure, - __out_ecount(cchWebBase) LPWSTR wzWebBase, - __in DWORD cchWebBase - ); -HRESULT DAPI MetaFindFreeWebBase( - __in IMSAdminBaseW* piMetabase, - __out_ecount(cchWebBase) LPWSTR wzWebBase, - __in DWORD cchWebBase - ); - -HRESULT DAPI MetaOpenKey( - __in IMSAdminBaseW* piMetabase, - __in METADATA_HANDLE mhKey, - __in_z LPCWSTR wzKey, - __in DWORD dwAccess, - __in DWORD cRetries, - __out METADATA_HANDLE* pmh - ); -HRESULT DAPI MetaGetValue( - __in IMSAdminBaseW* piMetabase, - __in METADATA_HANDLE mhKey, - __in_z LPCWSTR wzKey, - __inout METADATA_RECORD* pmr - ); -void DAPI MetaFreeValue( - __in METADATA_RECORD* pmr - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/monutil.h b/src/dutil/inc/monutil.h deleted file mode 100644 index f644e205..00000000 --- a/src/dutil/inc/monutil.h +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseMon(mh) if (mh) { MonDestroy(mh); } -#define ReleaseNullMon(mh) if (mh) { MonDestroy(mh); mh = NULL; } - -typedef void* MON_HANDLE; -typedef const void* C_MON_HANDLE; - -// Defined in regutil.h -enum REG_KEY_BITNESS; - -extern const int MON_HANDLE_BYTES; - -// Note: callbacks must be implemented in a thread-safe manner. They will be called asynchronously by a MonUtil-spawned thread. -// They must also be written to return as soon as possible - they are called from the waiter thread -typedef void (*PFN_MONGENERAL)( - __in HRESULT hr, - __in_opt LPVOID pvContext - ); -// This callback is not specific to any wait - it will notify client of any drive status changes, such as removable drive insertion / removal -typedef void (*PFN_MONDRIVESTATUS)( - __in WCHAR chDrive, - __in BOOL fArriving, - __in_opt LPVOID pvContext - ); -// Note if these fire with a failed result it means an error has occurred with the wait, and so the wait will stay in the list and be retried. When waits start succeeding again, -// MonUtil will notify of changes, because it may have not noticed changes during the interval for which the wait had failed. This behavior can result in false positive notifications, -// so all consumers of MonUtil should be designed with this in mind. -typedef void (*PFN_MONDIRECTORY)( - __in HRESULT hr, - __in_z LPCWSTR wzPath, - __in BOOL fRecursive, - __in_opt LPVOID pvContext, - __in_opt LPVOID pvDirectoryContext - ); -typedef void (*PFN_MONREGKEY)( - __in HRESULT hr, - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fRecursive, - __in_opt LPVOID pvContext, - __in_opt LPVOID pvRegKeyContext - ); - -// Silence period allows you to avoid lots of notifications when a lot of writes are going on in a directory -// MonUtil will wait until the directory has been "silent" for at least dwSilencePeriodInMs milliseconds -// The drawback to setting this to a value higher than zero is that even single write notifications -// are delayed by this amount -HRESULT DAPI MonCreate( - __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle, - __in PFN_MONGENERAL vpfMonGeneral, - __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus, - __in_opt PFN_MONDIRECTORY vpfMonDirectory, - __in_opt PFN_MONREGKEY vpfMonRegKey, - __in_opt LPVOID pvContext - ); -// Don't add multiple identical waits! Not only is it wasteful and will cause multiple fires for the exact same change, it will also -// result in slightly odd behavior when you remove a duplicated wait (removing a wait may or may not remove multiple waits) -// This is due to the way coordinator thread and waiter threads handle removing, and while it is possible to solve, doing so would complicate the code. -// So instead, de-dupe your wait requests before sending them to MonUtil. -// Special notes for network waits: MonUtil can send false positive notifications (i.e. notifications when nothing had changed) if connection -// to the share is lost and reconnected, because MonUtil can't know for sure whether changes occurred while the connection was lost. -// Also, MonUtil will very every 20 minutes retry even successful network waits, because the underlying Win32 API cannot notify us if a remote server -// had its network cable unplugged or similar sudden failure. When we retry the successful network waits, we will also send a false positive notification, -// because it's impossible for MonUtil to detect if we're reconnecting to a server that had died and come back to life, or if we're reconnecting to a server that had -// been up all along. For both of the above reasons, clients of MonUtil must be written to do very, very little work in the case of false positive network waits. -HRESULT DAPI MonAddDirectory( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in_z LPCWSTR wzPath, - __in BOOL fRecursive, - __in DWORD dwSilencePeriodInMs, - __in_opt LPVOID pvDirectoryContext - ); -HRESULT DAPI MonAddRegKey( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fRecursive, - __in DWORD dwSilencePeriodInMs, - __in_opt LPVOID pvRegKeyContext - ); -HRESULT DAPI MonRemoveDirectory( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in_z LPCWSTR wzPath, - __in BOOL fRecursive - ); -HRESULT DAPI MonRemoveRegKey( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fRecursive - ); -void DAPI MonDestroy( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/osutil.h b/src/dutil/inc/osutil.h deleted file mode 100644 index 2cce6f63..00000000 --- a/src/dutil/inc/osutil.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum OS_VERSION -{ - OS_VERSION_UNKNOWN, - OS_VERSION_WINNT, - OS_VERSION_WIN2000, - OS_VERSION_WINXP, - OS_VERSION_WIN2003, - OS_VERSION_VISTA, - OS_VERSION_WIN2008, - OS_VERSION_WIN7, - OS_VERSION_WIN2008_R2, - OS_VERSION_FUTURE -} OS_VERSION; - -void DAPI OsGetVersion( - __out OS_VERSION* pVersion, - __out DWORD* pdwServicePack - ); -HRESULT DAPI OsCouldRunPrivileged( - __out BOOL* pfPrivileged - ); -HRESULT DAPI OsIsRunningPrivileged( - __out BOOL* pfPrivileged - ); -HRESULT DAPI OsIsUacEnabled( - __out BOOL* pfUacEnabled - ); -HRESULT DAPI OsRtlGetVersion( - __inout RTL_OSVERSIONINFOEXW* pOvix - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/pathutil.h b/src/dutil/inc/pathutil.h deleted file mode 100644 index 579b8454..00000000 --- a/src/dutil/inc/pathutil.h +++ /dev/null @@ -1,252 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum PATH_EXPAND -{ - PATH_EXPAND_ENVIRONMENT = 0x0001, - PATH_EXPAND_FULLPATH = 0x0002, -} PATH_EXPAND; - - -/******************************************************************* - PathCommandLineAppend - appends a command line argument on to a - string such that ::CommandLineToArgv() will shred them correctly - (i.e. quote arguments with spaces in them). -********************************************************************/ -DAPI_(HRESULT) PathCommandLineAppend( - __deref_inout_z LPWSTR* psczCommandLine, - __in_z LPCWSTR wzArgument - ); - -/******************************************************************* - PathFile - returns a pointer to the file part of the path. -********************************************************************/ -DAPI_(LPWSTR) PathFile( - __in_z LPCWSTR wzPath - ); - -/******************************************************************* - PathExtension - returns a pointer to the extension part of the path - (including the dot). -********************************************************************/ -DAPI_(LPCWSTR) PathExtension( - __in_z LPCWSTR wzPath - ); - -/******************************************************************* - PathGetDirectory - extracts the directory from a path. -********************************************************************/ -DAPI_(HRESULT) PathGetDirectory( - __in_z LPCWSTR wzPath, - __out_z LPWSTR *psczDirectory - ); - -/******************************************************************* -PathGetParentPath - extracts the parent directory from a full path. -********************************************************************/ -DAPI_(HRESULT) PathGetParentPath( - __in_z LPCWSTR wzPath, - __out_z LPWSTR *psczDirectory - ); - -/******************************************************************* - PathExpand - gets the full path to a file resolving environment - variables along the way. -********************************************************************/ -DAPI_(HRESULT) PathExpand( - __out LPWSTR *psczFullPath, - __in_z LPCWSTR wzRelativePath, - __in DWORD dwResolveFlags - ); - -/******************************************************************* - PathPrefix - prefixes a full path with \\?\ or \\?\UNC as - appropriate. -********************************************************************/ -DAPI_(HRESULT) PathPrefix( - __inout LPWSTR *psczFullPath - ); - -/******************************************************************* - PathFixedBackslashTerminate - appends a \ if path does not have it - already, but fails if the buffer is - insufficient. -********************************************************************/ -DAPI_(HRESULT) PathFixedBackslashTerminate( - __inout_ecount_z(cchPath) LPWSTR wzPath, - __in SIZE_T cchPath - ); - -/******************************************************************* - PathBackslashTerminate - appends a \ if path does not have it - already. -********************************************************************/ -DAPI_(HRESULT) PathBackslashTerminate( - __inout LPWSTR* psczPath - ); - -/******************************************************************* - PathForCurrentProcess - gets the full path to the currently executing - process or (optionally) a module inside the process. -********************************************************************/ -DAPI_(HRESULT) PathForCurrentProcess( - __inout LPWSTR *psczFullPath, - __in_opt HMODULE hModule - ); - -/******************************************************************* - PathRelativeToModule - gets the name of a file in the same - directory as the current process or (optionally) a module inside - the process -********************************************************************/ -DAPI_(HRESULT) PathRelativeToModule( - __inout LPWSTR *psczFullPath, - __in_opt LPCWSTR wzFileName, - __in_opt HMODULE hModule - ); - -/******************************************************************* - PathCreateTempFile - - Note: if wzDirectory is null, ::GetTempPath() will be used instead. - if wzFileNameTemplate is null, GetTempFileName() will be used instead. -*******************************************************************/ -DAPI_(HRESULT) PathCreateTempFile( - __in_opt LPCWSTR wzDirectory, - __in_opt __format_string LPCWSTR wzFileNameTemplate, - __in DWORD dwUniqueCount, - __in DWORD dwFileAttributes, - __out_opt LPWSTR* psczTempFile, - __out_opt HANDLE* phTempFile - ); - -/******************************************************************* - PathCreateTimeBasedTempFile - creates an empty temp file based on current - system time -********************************************************************/ -DAPI_(HRESULT) PathCreateTimeBasedTempFile( - __in_z_opt LPCWSTR wzDirectory, - __in_z LPCWSTR wzPrefix, - __in_z_opt LPCWSTR wzPostfix, - __in_z LPCWSTR wzExtension, - __deref_opt_out_z LPWSTR* psczTempFile, - __out_opt HANDLE* phTempFile - ); - -/******************************************************************* - PathCreateTempDirectory - - Note: if wzDirectory is null, ::GetTempPath() will be used instead. -*******************************************************************/ -DAPI_(HRESULT) PathCreateTempDirectory( - __in_opt LPCWSTR wzDirectory, - __in __format_string LPCWSTR wzDirectoryNameTemplate, - __in DWORD dwUniqueCount, - __out LPWSTR* psczTempDirectory - ); - -/******************************************************************* - PathGetKnownFolder - returns the path to a well-known shell folder - -*******************************************************************/ -DAPI_(HRESULT) PathGetKnownFolder( - __in int csidl, - __out LPWSTR* psczKnownFolder - ); - -/******************************************************************* - PathIsAbsolute - returns true if the path is absolute; false - otherwise. -*******************************************************************/ -DAPI_(BOOL) PathIsAbsolute( - __in_z LPCWSTR wzPath - ); - -/******************************************************************* - PathConcat - like .NET's Path.Combine, lets you build up a path - one piece -- file or directory -- at a time. -*******************************************************************/ -DAPI_(HRESULT) PathConcat( - __in_opt LPCWSTR wzPath1, - __in_opt LPCWSTR wzPath2, - __deref_out_z LPWSTR* psczCombined - ); - -/******************************************************************* - PathConcatCch - like .NET's Path.Combine, lets you build up a path - one piece -- file or directory -- at a time. -*******************************************************************/ -DAPI_(HRESULT) PathConcatCch( - __in_opt LPCWSTR wzPath1, - __in SIZE_T cchPath1, - __in_opt LPCWSTR wzPath2, - __in SIZE_T cchPath2, - __deref_out_z LPWSTR* psczCombined - ); - -/******************************************************************* - PathEnsureQuoted - ensures that a path is quoted; optionally, - this function also terminates a directory with a backslash - if it is not already. -*******************************************************************/ -DAPI_(HRESULT) PathEnsureQuoted( - __inout LPWSTR* ppszPath, - __in BOOL fDirectory - ); - -/******************************************************************* - PathCompare - compares the fully expanded path of the two paths using - ::CompareStringW(). -*******************************************************************/ -DAPI_(HRESULT) PathCompare( - __in_z LPCWSTR wzPath1, - __in_z LPCWSTR wzPath2, - __out int* pnResult - ); - -/******************************************************************* - PathCompress - sets the compression state on an existing file or - directory. A no-op on file systems that don't - support compression. -*******************************************************************/ -DAPI_(HRESULT) PathCompress( - __in_z LPCWSTR wzPath - ); - -/******************************************************************* - PathGetHierarchyArray - allocates an array containing, - in order, every parent directory of the specified path, - ending with the actual input path - This function also works with registry subkeys -*******************************************************************/ -DAPI_(HRESULT) PathGetHierarchyArray( - __in_z LPCWSTR wzPath, - __deref_inout_ecount_opt(*pcPathArray) LPWSTR **prgsczPathArray, - __inout LPUINT pcPathArray - ); - -/******************************************************************* - PathCanonicalizePath - wrapper around PathCanonicalizeW. -*******************************************************************/ -DAPI_(HRESULT) PathCanonicalizePath( - __in_z LPCWSTR wzPath, - __deref_out_z LPWSTR* psczCanonicalized - ); - -/******************************************************************* -PathDirectoryContainsPath - checks if wzPath is located inside - wzDirectory. -*******************************************************************/ -DAPI_(HRESULT) PathDirectoryContainsPath( - __in_z LPCWSTR wzDirectory, - __in_z LPCWSTR wzPath - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/perfutil.h b/src/dutil/inc/perfutil.h deleted file mode 100644 index 7557511d..00000000 --- a/src/dutil/inc/perfutil.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -// structs - - -// functions -void DAPI PerfInitialize( - ); -void DAPI PerfClickTime( - __out_opt LARGE_INTEGER* pliElapsed - ); -double DAPI PerfConvertToSeconds( - __in const LARGE_INTEGER* pli - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/polcutil.h b/src/dutil/inc/polcutil.h deleted file mode 100644 index 13618043..00000000 --- a/src/dutil/inc/polcutil.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -const LPCWSTR POLICY_BURN_REGISTRY_PATH = L"WiX\\Burn"; - -/******************************************************************** -PolcReadNumber - reads a number from policy. - -NOTE: S_FALSE returned if policy not set. -NOTE: out is set to default on S_FALSE or any error. -********************************************************************/ -HRESULT DAPI PolcReadNumber( - __in_z LPCWSTR wzPolicyPath, - __in_z LPCWSTR wzPolicyName, - __in DWORD dwDefault, - __out DWORD* pdw - ); - -/******************************************************************** -PolcReadString - reads a string from policy. - -NOTE: S_FALSE returned if policy not set. -NOTE: out is set to default on S_FALSE or any error. -********************************************************************/ -HRESULT DAPI PolcReadString( - __in_z LPCWSTR wzPolicyPath, - __in_z LPCWSTR wzPolicyName, - __in_z_opt LPCWSTR wzDefault, - __deref_out_z LPWSTR* pscz - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/procutil.h b/src/dutil/inc/procutil.h deleted file mode 100644 index 00f3f358..00000000 --- a/src/dutil/inc/procutil.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -// structs -typedef struct _PROC_FILESYSTEMREDIRECTION -{ - BOOL fDisabled; - LPVOID pvRevertState; -} PROC_FILESYSTEMREDIRECTION; - -HRESULT DAPI ProcElevated( - __in HANDLE hProcess, - __out BOOL* pfElevated - ); - -HRESULT DAPI ProcWow64( - __in HANDLE hProcess, - __out BOOL* pfWow64 - ); -HRESULT DAPI ProcDisableWowFileSystemRedirection( - __in PROC_FILESYSTEMREDIRECTION* pfsr - ); -HRESULT DAPI ProcRevertWowFileSystemRedirection( - __in PROC_FILESYSTEMREDIRECTION* pfsr - ); - -HRESULT DAPI ProcExec( - __in_z LPCWSTR wzExecutablePath, - __in_z_opt LPCWSTR wzCommandLine, - __in int nCmdShow, - __out HANDLE *phProcess - ); -HRESULT DAPI ProcExecute( - __in_z LPWSTR wzCommand, - __out HANDLE *phProcess, - __out_opt HANDLE *phChildStdIn, - __out_opt HANDLE *phChildStdOutErr - ); -HRESULT DAPI ProcWaitForCompletion( - __in HANDLE hProcess, - __in DWORD dwTimeout, - __out DWORD *pReturnCode - ); -HRESULT DAPI ProcWaitForIds( - __in_ecount(cProcessIds) const DWORD* pdwProcessIds, - __in DWORD cProcessIds, - __in DWORD dwMilliseconds - ); -HRESULT DAPI ProcCloseIds( - __in_ecount(cProcessIds) const DWORD* pdwProcessIds, - __in DWORD cProcessIds - ); - -// following code in proc2utl.cpp due to dependency on PSAPI.DLL. -HRESULT DAPI ProcFindAllIdsFromExeName( - __in_z LPCWSTR wzExeName, - __out DWORD** ppdwProcessIds, - __out DWORD* pcProcessIds - ); - -// following code in proc3utl.cpp due to dependency on Wtsapi32.DLL. -HRESULT DAPI ProcExecuteAsInteractiveUser( - __in_z LPCWSTR wzExecutablePath, - __in_z LPCWSTR wzCommand, - __out HANDLE *phProcess - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/regutil.h b/src/dutil/inc/regutil.h deleted file mode 100644 index 75284940..00000000 --- a/src/dutil/inc/regutil.h +++ /dev/null @@ -1,246 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - - -#define ReleaseRegKey(h) if (h) { ::RegCloseKey(h); h = NULL; } - -typedef enum REG_KEY_BITNESS -{ - REG_KEY_DEFAULT = 0, - REG_KEY_32BIT = 1, - REG_KEY_64BIT = 2 -} REG_KEY_BITNESS; - -typedef LSTATUS (APIENTRY *PFN_REGCREATEKEYEXW)( - __in HKEY hKey, - __in LPCWSTR lpSubKey, - __reserved DWORD Reserved, - __in_opt LPWSTR lpClass, - __in DWORD dwOptions, - __in REGSAM samDesired, - __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, - __out PHKEY phkResult, - __out_opt LPDWORD lpdwDisposition - ); -typedef LSTATUS (APIENTRY *PFN_REGOPENKEYEXW)( - __in HKEY hKey, - __in_opt LPCWSTR lpSubKey, - __reserved DWORD ulOptions, - __in REGSAM samDesired, - __out PHKEY phkResult - ); -typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYEXW)( - __in HKEY hKey, - __in LPCWSTR lpSubKey, - __in REGSAM samDesired, - __reserved DWORD Reserved - ); -typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYW)( - __in HKEY hKey, - __in LPCWSTR lpSubKey - ); -typedef LSTATUS (APIENTRY *PFN_REGENUMKEYEXW)( - __in HKEY hKey, - __in DWORD dwIndex, - __out LPWSTR lpName, - __inout LPDWORD lpcName, - __reserved LPDWORD lpReserved, - __inout_opt LPWSTR lpClass, - __inout_opt LPDWORD lpcClass, - __out_opt PFILETIME lpftLastWriteTime - ); -typedef LSTATUS (APIENTRY *PFN_REGENUMVALUEW)( - __in HKEY hKey, - __in DWORD dwIndex, - __out LPWSTR lpValueName, - __inout LPDWORD lpcchValueName, - __reserved LPDWORD lpReserved, - __out_opt LPDWORD lpType, - __out_opt LPBYTE lpData, - __out_opt LPDWORD lpcbData - ); -typedef LSTATUS (APIENTRY *PFN_REGQUERYINFOKEYW)( - __in HKEY hKey, - __out_opt LPWSTR lpClass, - __inout_opt LPDWORD lpcClass, - __reserved LPDWORD lpReserved, - __out_opt LPDWORD lpcSubKeys, - __out_opt LPDWORD lpcMaxSubKeyLen, - __out_opt LPDWORD lpcMaxClassLen, - __out_opt LPDWORD lpcValues, - __out_opt LPDWORD lpcMaxValueNameLen, - __out_opt LPDWORD lpcMaxValueLen, - __out_opt LPDWORD lpcbSecurityDescriptor, - __out_opt PFILETIME lpftLastWriteTime - ); -typedef LSTATUS (APIENTRY *PFN_REGQUERYVALUEEXW)( - __in HKEY hKey, - __in_opt LPCWSTR lpValueName, - __reserved LPDWORD lpReserved, - __out_opt LPDWORD lpType, - __out_bcount_part_opt(*lpcbData, *lpcbData) __out_data_source(REGISTRY) LPBYTE lpData, - __inout_opt LPDWORD lpcbData - ); -typedef LSTATUS (APIENTRY *PFN_REGSETVALUEEXW)( - __in HKEY hKey, - __in_opt LPCWSTR lpValueName, - __reserved DWORD Reserved, - __in DWORD dwType, - __in_bcount_opt(cbData) CONST BYTE* lpData, - __in DWORD cbData - ); -typedef LSTATUS (APIENTRY *PFN_REGDELETEVALUEW)( - __in HKEY hKey, - __in_opt LPCWSTR lpValueName - ); - -HRESULT DAPI RegInitialize(); -void DAPI RegUninitialize(); - -void DAPI RegFunctionOverride( - __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW, - __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW, - __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW, - __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW, - __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW, - __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, - __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, - __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, - __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW - ); -HRESULT DAPI RegCreate( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in DWORD dwAccess, - __out HKEY* phk - ); -HRESULT DAPI RegCreateEx( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in DWORD dwAccess, - __in BOOL fVolatile, - __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes, - __out HKEY* phk, - __out_opt BOOL* pfCreated - ); -HRESULT DAPI RegOpen( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in DWORD dwAccess, - __out HKEY* phk - ); -HRESULT DAPI RegDelete( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fDeleteTree - ); -HRESULT DAPI RegKeyEnum( - __in HKEY hk, - __in DWORD dwIndex, - __deref_out_z LPWSTR* psczKey - ); -HRESULT DAPI RegValueEnum( - __in HKEY hk, - __in DWORD dwIndex, - __deref_out_z LPWSTR* psczName, - __out_opt DWORD *pdwType - ); -HRESULT DAPI RegGetType( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD *pdwType - ); -HRESULT DAPI RegReadBinary( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, - __out SIZE_T *pcbBuffer - ); -HRESULT DAPI RegReadString( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __deref_out_z LPWSTR* psczValue - ); -HRESULT DAPI RegReadStringArray( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings, - __out DWORD *pcStrings - ); -HRESULT DAPI RegReadVersion( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD64* pdw64Version - ); -HRESULT DAPI RegReadNumber( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD* pdwValue - ); -HRESULT DAPI RegReadQword( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD64* pqwValue - ); -HRESULT DAPI RegWriteBinary( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_bcount(cbBuffer) const BYTE *pbBuffer, - __in DWORD cbBuffer - ); -HRESULT DAPI RegWriteString( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_z_opt LPCWSTR wzValue - ); -HRESULT DAPI RegWriteStringArray( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_ecount(cStrings) LPWSTR *rgwzStrings, - __in DWORD cStrings - ); -HRESULT DAPI RegWriteStringFormatted( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in __format_string LPCWSTR szFormat, - ... - ); -HRESULT DAPI RegWriteNumber( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in DWORD dwValue - ); -HRESULT DAPI RegWriteQword( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in DWORD64 qwValue - ); -HRESULT DAPI RegQueryKey( - __in HKEY hk, - __out_opt DWORD* pcSubKeys, - __out_opt DWORD* pcValues - ); -HRESULT DAPI RegKeyReadNumber( - __in HKEY hk, - __in_z LPCWSTR wzSubKey, - __in_z_opt LPCWSTR wzName, - __in BOOL f64Bit, - __out DWORD* pdwValue - ); -BOOL DAPI RegValueExists( - __in HKEY hk, - __in_z LPCWSTR wzSubKey, - __in_z_opt LPCWSTR wzName, - __in BOOL f64Bit - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/resrutil.h b/src/dutil/inc/resrutil.h deleted file mode 100644 index 1f4d8e17..00000000 --- a/src/dutil/inc/resrutil.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI ResGetStringLangId( - __in_opt LPCWSTR wzPath, - __in UINT uID, - __out WORD *pwLangId - ); - -HRESULT DAPI ResReadString( - __in HINSTANCE hinst, - __in UINT uID, - __deref_out_z LPWSTR* ppwzString - ); - -HRESULT DAPI ResReadStringAnsi( - __in HINSTANCE hinst, - __in UINT uID, - __deref_out_z LPSTR* ppszString - ); - -HRESULT DAPI ResReadData( - __in_opt HINSTANCE hinst, - __in_z LPCSTR szDataName, - __deref_out_bcount(*pcb) PVOID *ppv, - __out DWORD *pcb - ); - -HRESULT DAPI ResExportDataToFile( - __in_z LPCSTR szDataName, - __in_z LPCWSTR wzTargetFile, - __in DWORD dwCreationDisposition - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/reswutil.h b/src/dutil/inc/reswutil.h deleted file mode 100644 index 31435ae2..00000000 --- a/src/dutil/inc/reswutil.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI ResWriteString( - __in_z LPCWSTR wzResourceFile, - __in DWORD dwDataId, - __in_z LPCWSTR wzData, - __in WORD wLangId - ); - -HRESULT DAPI ResWriteData( - __in_z LPCWSTR wzResourceFile, - __in_z LPCSTR szDataName, - __in PVOID pData, - __in DWORD cbData - ); - -HRESULT DAPI ResImportDataFromFile( - __in_z LPCWSTR wzTargetFile, - __in_z LPCWSTR wzSourceFile, - __in_z LPCSTR szDataName - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/rexutil.h b/src/dutil/inc/rexutil.h deleted file mode 100644 index 77f5604a..00000000 --- a/src/dutil/inc/rexutil.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// defines -#define FILETABLESIZE 40 - -// structs -struct MEM_FILE -{ - LPCBYTE vpStart; - UINT uiCurrent; - UINT uiLength; -}; - -typedef enum FAKE_FILE_TYPE { NORMAL_FILE, MEMORY_FILE } FAKE_FILE_TYPE; - -typedef HRESULT (*REX_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext); -typedef VOID (*REX_CALLBACK_WRITE)(UINT cb); - - -struct FAKE_FILE // used __in internal file table -{ - BOOL fUsed; - FAKE_FILE_TYPE fftType; - MEM_FILE mfFile; // State for memory file - HANDLE hFile; // Handle for disk file -}; - -// functions -HRESULT RexInitialize(); -void RexUninitialize(); - -HRESULT RexExtract( - __in_z LPCSTR szResource, - __in_z LPCWSTR wzExtractId, - __in_z LPCWSTR wzExtractDir, - __in_z LPCWSTR wzExtractName, - __in REX_CALLBACK_PROGRESS pfnProgress, - __in REX_CALLBACK_WRITE pfnWrite, - __in LPVOID pvContext - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/rmutil.h b/src/dutil/inc/rmutil.h deleted file mode 100644 index ce7bf254..00000000 --- a/src/dutil/inc/rmutil.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct _RMU_SESSION *PRMU_SESSION; - -HRESULT DAPI RmuJoinSession( - __out PRMU_SESSION *ppSession, - __in_z LPCWSTR wzSessionKey - ); - -HRESULT DAPI RmuAddFile( - __in PRMU_SESSION pSession, - __in_z LPCWSTR wzPath - ); - -HRESULT DAPI RmuAddProcessById( - __in PRMU_SESSION pSession, - __in DWORD dwProcessId - ); - -HRESULT DAPI RmuAddProcessesByName( - __in PRMU_SESSION pSession, - __in_z LPCWSTR wzProcessName - ); - -HRESULT DAPI RmuAddService( - __in PRMU_SESSION pSession, - __in_z LPCWSTR wzServiceName - ); - -HRESULT DAPI RmuRegisterResources( - __in PRMU_SESSION pSession - ); - -HRESULT DAPI RmuEndSession( - __in PRMU_SESSION pSession - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/rssutil.h b/src/dutil/inc/rssutil.h deleted file mode 100644 index 064ab147..00000000 --- a/src/dutil/inc/rssutil.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseRssChannel(p) if (p) { RssFreeChannel(p); } -#define ReleaseNullRssChannel(p) if (p) { RssFreeChannel(p); p = NULL; } - - -struct RSS_UNKNOWN_ATTRIBUTE -{ - LPWSTR wzNamespace; - LPWSTR wzAttribute; - LPWSTR wzValue; - - RSS_UNKNOWN_ATTRIBUTE* pNext; -}; - -struct RSS_UNKNOWN_ELEMENT -{ - LPWSTR wzNamespace; - LPWSTR wzElement; - LPWSTR wzValue; - - RSS_UNKNOWN_ATTRIBUTE* pAttributes; - RSS_UNKNOWN_ELEMENT* pNext; -}; - -struct RSS_ITEM -{ - LPWSTR wzTitle; - LPWSTR wzLink; - LPWSTR wzDescription; - - LPWSTR wzGuid; - FILETIME ftPublished; - - LPWSTR wzEnclosureUrl; - DWORD dwEnclosureSize; - LPWSTR wzEnclosureType; - - RSS_UNKNOWN_ELEMENT* pUnknownElements; -}; - -struct RSS_CHANNEL -{ - LPWSTR wzTitle; - LPWSTR wzLink; - LPWSTR wzDescription; - DWORD dwTimeToLive; - - RSS_UNKNOWN_ELEMENT* pUnknownElements; - - DWORD cItems; - RSS_ITEM rgItems[1]; -}; - -HRESULT DAPI RssInitialize( - ); - -void DAPI RssUninitialize( - ); - -HRESULT DAPI RssParseFromString( - __in_z LPCWSTR wzRssString, - __out RSS_CHANNEL **ppChannel - ); - -HRESULT DAPI RssParseFromFile( - __in_z LPCWSTR wzRssFile, - __out RSS_CHANNEL **ppChannel - ); - -// Adding this until we have the updated specstrings.h -#ifndef __in_xcount -#define __in_xcount(size) -#endif - -void DAPI RssFreeChannel( - __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/sceutil.h b/src/dutil/inc/sceutil.h deleted file mode 100644 index 9d14eecf..00000000 --- a/src/dutil/inc/sceutil.h +++ /dev/null @@ -1,273 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -typedef void* SCE_DATABASE_HANDLE; -typedef void* SCE_ROW_HANDLE; -typedef void* SCE_QUERY_HANDLE; -typedef void* SCE_QUERY_RESULTS_HANDLE; - -extern const int SCE_ROW_HANDLE_BYTES; -extern const int SCE_QUERY_HANDLE_BYTES; -extern const int SCE_QUERY_RESULTS_HANDLE_BYTES; - -#define ReleaseSceRow(prrh) if (prrh) { SceFreeRow(prrh); } -#define ReleaseNullSceRow(prrh) if (prrh) { SceFreeRow(prrh); prrh = NULL; } -#define ReleaseSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); } -#define ReleaseNullSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); pqh = NULL; } -#define ReleaseSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); } -#define ReleaseNullSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); pqh = NULL; } - -struct SCE_COLUMN_SCHEMA -{ - LPCWSTR wzName; - DBTYPE dbtColumnType; - DWORD dwLength; - BOOL fPrimaryKey; // If this column is the primary key - BOOL fNullable; - BOOL fAutoIncrement; - BOOL fDescending; // If this column should be descending when used in an index (default is ascending) - - LPWSTR wzRelationName; - DWORD dwForeignKeyTable; - DWORD dwForeignKeyColumn; -}; - -struct SCE_INDEX_SCHEMA -{ - LPWSTR wzName; - - DWORD *rgColumns; - DWORD cColumns; -}; - -struct SCE_TABLE_SCHEMA -{ - LPCWSTR wzName; - DWORD cColumns; - SCE_COLUMN_SCHEMA *rgColumns; - - DWORD cIndexes; - SCE_INDEX_SCHEMA *rgIndexes; - - // Internal to SCEUtil - consumers shouldn't access or modify - // TODO: enforce / hide in a handle of some sort? - IRowset *pIRowset; - IRowsetChange *pIRowsetChange; -}; - -struct SCE_DATABASE_SCHEMA -{ - DWORD cTables; - SCE_TABLE_SCHEMA *rgTables; -}; - -struct SCE_DATABASE -{ - SCE_DATABASE_HANDLE sdbHandle; - SCE_DATABASE_SCHEMA *pdsSchema; -}; - -HRESULT DAPI SceCreateDatabase( - __in_z LPCWSTR sczFile, - __in_z_opt LPCWSTR wzSqlCeDllPath, - __deref_out SCE_DATABASE **ppDatabase - ); -HRESULT DAPI SceOpenDatabase( - __in_z LPCWSTR sczFile, - __in_z_opt LPCWSTR wzSqlCeDllPath, - __in LPCWSTR wzSchemaType, - __in DWORD dwExpectedVersion, - __deref_out SCE_DATABASE **ppDatabase, - __in BOOL fReadOnly - ); -HRESULT DAPI SceEnsureDatabase( - __in_z LPCWSTR sczFile, - __in_z_opt LPCWSTR wzSqlCeDllPath, - __in LPCWSTR wzSchemaType, - __in DWORD dwExpectedVersion, - __in SCE_DATABASE_SCHEMA *pdsSchema, - __deref_out SCE_DATABASE **ppDatabase - ); -HRESULT DAPI SceIsTableEmpty( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __out BOOL *pfEmpty - ); -HRESULT DAPI SceGetFirstRow( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ); -HRESULT DAPI SceGetNextRow( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ); -HRESULT DAPI SceBeginTransaction( - __in SCE_DATABASE *pDatabase - ); -HRESULT DAPI SceCommitTransaction( - __in SCE_DATABASE *pDatabase - ); -HRESULT DAPI SceRollbackTransaction( - __in SCE_DATABASE *pDatabase - ); -HRESULT DAPI SceDeleteRow( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ); -HRESULT DAPI ScePrepareInsert( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ); -HRESULT DAPI SceFinishUpdate( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle - ); -HRESULT DAPI SceSetColumnBinary( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer - ); -HRESULT DAPI SceSetColumnDword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const DWORD dwValue - ); -HRESULT DAPI SceSetColumnQword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const DWORD64 qwValue - ); -HRESULT DAPI SceSetColumnBool( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const BOOL fValue - ); -HRESULT DAPI SceSetColumnString( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in_z_opt LPCWSTR wzValue - ); -HRESULT DAPI SceSetColumnSystemTime( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const SYSTEMTIME *pst - ); -HRESULT DAPI SceSetColumnNull( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex - ); -HRESULT DAPI SceGetColumnBinary( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out_opt BYTE **ppbBuffer, - __inout SIZE_T *pcbBuffer - ); -HRESULT DAPI SceGetColumnDword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out DWORD *pdwValue - ); -HRESULT DAPI SceGetColumnQword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out DWORD64 *pqwValue - ); -HRESULT DAPI SceGetColumnBool( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out BOOL *pfValue - ); -HRESULT DAPI SceGetColumnString( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out_z LPWSTR *psczValue - ); -HRESULT DAPI SceGetColumnSystemTime( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out SYSTEMTIME *pst - ); -HRESULT DAPI SceBeginQuery( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __in DWORD dwIndex, - __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle - ); -HRESULT DAPI SceSetQueryColumnBinary( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer - ); -HRESULT DAPI SceSetQueryColumnDword( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const DWORD dwValue - ); -HRESULT DAPI SceSetQueryColumnQword( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const DWORD64 qwValue - ); -HRESULT DAPI SceSetQueryColumnBool( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const BOOL fValue - ); -HRESULT DAPI SceSetQueryColumnString( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in_z_opt LPCWSTR wzString - ); -HRESULT DAPI SceSetQueryColumnSystemTime( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in const SYSTEMTIME *pst - ); -HRESULT DAPI SceSetQueryColumnEmpty( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle - ); -HRESULT DAPI SceRunQueryExact( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, - __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ); -HRESULT DAPI SceRunQueryRange( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, - __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle - ); -HRESULT DAPI SceGetNextResultRow( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle, - __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ); -void DAPI SceCloseTable( - __in SCE_TABLE_SCHEMA *pTable - ); -// Returns whether the data in the database changed. Ignores schema changes. -BOOL DAPI SceDatabaseChanged( - __in SCE_DATABASE *pDatabase - ); -// Resets the database changed flag -void DAPI SceResetDatabaseChanged( - __in SCE_DATABASE *pDatabase - ); -HRESULT DAPI SceCloseDatabase( - __in SCE_DATABASE *pDatabase - ); -void DAPI SceFreeRow( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle - ); -void DAPI SceFreeQuery( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle - ); -void DAPI SceFreeQueryResults( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/sczutil.h b/src/dutil/inc/sczutil.h deleted file mode 100644 index fcfbd13a..00000000 --- a/src/dutil/inc/sczutil.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -class PSCZ -{ -public: - PSCZ() : m_scz(NULL) { } - - ~PSCZ() { ReleaseNullStr(m_scz); } - - operator LPWSTR() { return m_scz; } - - operator LPCWSTR() { return m_scz; } - - operator bool() { return NULL != m_scz; } - - LPWSTR* operator &() { return &m_scz; } - - bool operator !() { return !m_scz; } - - WCHAR operator *() { return *m_scz; } - - LPWSTR Detach() { LPWSTR scz = m_scz; m_scz = NULL; return scz; } - -private: - LPWSTR m_scz; -}; -#endif //__cplusplus diff --git a/src/dutil/inc/shelutil.h b/src/dutil/inc/shelutil.h deleted file mode 100644 index 0b9f539d..00000000 --- a/src/dutil/inc/shelutil.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifndef REFKNOWNFOLDERID -#define REFKNOWNFOLDERID REFGUID -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef BOOL (STDAPICALLTYPE *PFN_SHELLEXECUTEEXW)( - __inout LPSHELLEXECUTEINFOW lpExecInfo - ); - -void DAPI ShelFunctionOverride( - __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW - ); -HRESULT DAPI ShelExec( - __in_z LPCWSTR wzTargetPath, - __in_z_opt LPCWSTR wzParameters, - __in_z_opt LPCWSTR wzVerb, - __in_z_opt LPCWSTR wzWorkingDirectory, - __in int nShowCmd, - __in_opt HWND hwndParent, - __out_opt HANDLE* phProcess - ); -HRESULT DAPI ShelExecUnelevated( - __in_z LPCWSTR wzTargetPath, - __in_z_opt LPCWSTR wzParameters, - __in_z_opt LPCWSTR wzVerb, - __in_z_opt LPCWSTR wzWorkingDirectory, - __in int nShowCmd - ); -HRESULT DAPI ShelGetFolder( - __out_z LPWSTR* psczFolderPath, - __in int csidlFolder - ); -HRESULT DAPI ShelGetKnownFolder( - __out_z LPWSTR* psczFolderPath, - __in REFKNOWNFOLDERID rfidFolder - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/sqlutil.h b/src/dutil/inc/sqlutil.h deleted file mode 100644 index ddf09323..00000000 --- a/src/dutil/inc/sqlutil.h +++ /dev/null @@ -1,136 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif - -// Adding this until the SQL annotations are published to specstrings.h -#ifndef __sql_command -#define __sql_command -#endif - -// structs -struct SQL_FILESPEC -{ - WCHAR wzName[MAX_PATH]; - WCHAR wzFilename[MAX_PATH]; - WCHAR wzSize[MAX_PATH]; - WCHAR wzMaxSize[MAX_PATH]; - WCHAR wzGrow[MAX_PATH]; -}; - - -// functions -HRESULT DAPI SqlConnectDatabase( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __out IDBCreateSession** ppidbSession - ); -HRESULT DAPI SqlStartTransaction( - __in IDBCreateSession* pidbSession, - __out IDBCreateCommand** ppidbCommand, - __out ITransaction** ppit - ); -HRESULT DAPI SqlEndTransaction( - __in ITransaction* pit, - __in BOOL fCommit - ); -HRESULT DAPI SqlDatabaseExists( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlSessionDatabaseExists( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlDatabaseEnsureExists( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlSessionDatabaseEnsureExists( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlCreateDatabase( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlSessionCreateDatabase( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlDropDatabase( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlSessionDropDatabase( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlSessionExecuteQuery( - __in IDBCreateSession* pidbSession, - __in __sql_command LPCWSTR wzSql, - __out_opt IRowset** ppirs, - __out_opt DBROWCOUNT* pcRows, - __out_opt BSTR* pbstrErrorDescription - ); -HRESULT DAPI SqlCommandExecuteQuery( - __in IDBCreateCommand* pidbCommand, - __in __sql_command LPCWSTR wzSql, - __out IRowset** ppirs, - __out DBROWCOUNT* pcRows - ); -HRESULT DAPI SqlGetErrorInfo( - __in IUnknown* pObjectWithError, - __in REFIID IID_InterfaceWithError, - __in DWORD dwLocaleId, - __out_opt BSTR* pbstrErrorSource, - __out_opt BSTR* pbstrErrorDescription - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/srputil.h b/src/dutil/inc/srputil.h deleted file mode 100644 index 95e96231..00000000 --- a/src/dutil/inc/srputil.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - - -typedef enum SRP_ACTION -{ - SRP_ACTION_UNKNOWN, - SRP_ACTION_UNINSTALL, - SRP_ACTION_INSTALL, - SRP_ACTION_MODIFY, -} SRP_ACTION; - - -/******************************************************************** - SrpInitialize - initializes system restore point functionality. - -*******************************************************************/ -DAPI_(HRESULT) SrpInitialize( - __in BOOL fInitializeComSecurity - ); - -/******************************************************************** - SrpUninitialize - uninitializes system restore point functionality. - -*******************************************************************/ -DAPI_(void) SrpUninitialize(); - -/******************************************************************** - SrpCreateRestorePoint - creates a system restore point. - -*******************************************************************/ -DAPI_(HRESULT) SrpCreateRestorePoint( - __in_z LPCWSTR wzApplicationName, - __in SRP_ACTION action - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/strutil.h b/src/dutil/inc/strutil.h deleted file mode 100644 index 1cff9ab8..00000000 --- a/src/dutil/inc/strutil.h +++ /dev/null @@ -1,316 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseStr(pwz) if (pwz) { StrFree(pwz); } -#define ReleaseNullStr(pwz) if (pwz) { StrFree(pwz); pwz = NULL; } -#define ReleaseBSTR(bstr) if (bstr) { ::SysFreeString(bstr); } -#define ReleaseNullBSTR(bstr) if (bstr) { ::SysFreeString(bstr); bstr = NULL; } -#define ReleaseStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); } } -#define ReleaseNullStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); c = 0; rg = NULL; } } -#define ReleaseNullStrSecure(pwz) if (pwz) { StrSecureZeroFreeString(pwz); pwz = NULL; } - -#define DeclareConstBSTR(bstr_const, wz) const WCHAR bstr_const[] = { 0x00, 0x00, sizeof(wz)-sizeof(WCHAR), 0x00, wz } -#define UseConstBSTR(bstr_const) const_cast(bstr_const + 4) - -HRESULT DAPI StrAlloc( - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in SIZE_T cch - ); -HRESULT DAPI StrAllocSecure( - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in SIZE_T cch - ); -HRESULT DAPI StrTrimCapacity( - __deref_out_z LPWSTR* ppwz - ); -HRESULT DAPI StrTrimWhitespace( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource - ); -HRESULT DAPI StrAnsiAlloc( - __deref_out_ecount_part(cch, 0) LPSTR* ppz, - __in SIZE_T cch - ); -HRESULT DAPI StrAnsiTrimCapacity( - __deref_out_z LPSTR* ppz - ); -HRESULT DAPI StrAnsiTrimWhitespace( - __deref_out_z LPSTR* ppz, - __in_z LPCSTR szSource - ); -HRESULT DAPI StrAllocString( - __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); -HRESULT DAPI StrAllocStringSecure( - __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); -HRESULT DAPI StrAnsiAllocString( - __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in UINT uiCodepage - ); -HRESULT DAPI StrAllocStringAnsi( - __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, - __in_z LPCSTR szSource, - __in SIZE_T cchSource, - __in UINT uiCodepage - ); -HRESULT DAPI StrAnsiAllocStringAnsi( - __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, - __in_z LPCSTR szSource, - __in SIZE_T cchSource - ); -HRESULT DAPI StrAllocPrefix( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzPrefix, - __in SIZE_T cchPrefix - ); -HRESULT DAPI StrAllocConcat( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); -HRESULT DAPI StrAllocConcatSecure( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); -HRESULT DAPI StrAnsiAllocConcat( - __deref_out_z LPSTR* ppz, - __in_z LPCSTR pzSource, - __in SIZE_T cchSource - ); -HRESULT __cdecl StrAllocFormatted( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ); -HRESULT __cdecl StrAllocConcatFormatted( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ); -HRESULT __cdecl StrAllocConcatFormattedSecure( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ); -HRESULT __cdecl StrAllocFormattedSecure( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ); -HRESULT __cdecl StrAnsiAllocFormatted( - __deref_out_z LPSTR* ppsz, - __in __format_string LPCSTR szFormat, - ... - ); -HRESULT DAPI StrAllocFormattedArgs( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - __in va_list args - ); -HRESULT DAPI StrAllocFormattedArgsSecure( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - __in va_list args - ); -HRESULT DAPI StrAnsiAllocFormattedArgs( - __deref_out_z LPSTR* ppsz, - __in __format_string LPCSTR szFormat, - __in va_list args - ); -HRESULT DAPI StrAllocFromError( - __inout LPWSTR *ppwzMessage, - __in HRESULT hrError, - __in_opt HMODULE hModule, - ... - ); - -HRESULT DAPI StrMaxLength( - __in LPCVOID p, - __out SIZE_T* pcbch - ); -HRESULT DAPI StrSize( - __in LPCVOID p, - __out SIZE_T* pcbb - ); - -HRESULT DAPI StrFree( - __in LPVOID p - ); - - -HRESULT DAPI StrReplaceStringAll( - __inout LPWSTR* ppwzOriginal, - __in_z LPCWSTR wzOldSubString, - __in_z LPCWSTR wzNewSubString - ); -HRESULT DAPI StrReplaceString( - __inout LPWSTR* ppwzOriginal, - __inout DWORD* pdwStartIndex, - __in_z LPCWSTR wzOldSubString, - __in_z LPCWSTR wzNewSubString - ); - -HRESULT DAPI StrHexEncode( - __in_ecount(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __out_ecount(cchDest) LPWSTR wzDest, - __in SIZE_T cchDest - ); -HRESULT DAPI StrAllocHexEncode( - __in_ecount(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest - ); -HRESULT DAPI StrHexDecode( - __in_z LPCWSTR wzSource, - __out_bcount(cbDest) BYTE* pbDest, - __in SIZE_T cbDest - ); -HRESULT DAPI StrAllocHexDecode( - __in_z LPCWSTR wzSource, - __out_bcount(*pcbDest) BYTE** ppbDest, - __out_opt DWORD* pcbDest - ); - -HRESULT DAPI StrAllocBase85Encode( - __in_bcount_opt(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __deref_out_z LPWSTR* pwzDest - ); -HRESULT DAPI StrAllocBase85Decode( - __in_z LPCWSTR wzSource, - __deref_out_bcount(*pcbDest) BYTE** ppbDest, - __out SIZE_T* pcbDest -); - -HRESULT DAPI MultiSzLen( - __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz, - __out SIZE_T* pcch - ); -HRESULT DAPI MultiSzPrepend( - __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz, - __inout_opt SIZE_T* pcchMultiSz, - __in __nullnullterminated LPCWSTR pwzInsert - ); -HRESULT DAPI MultiSzFindSubstring( - __in __nullnullterminated LPCWSTR pwzMultiSz, - __in __nullnullterminated LPCWSTR pwzSubstring, - __out_opt DWORD_PTR* pdwIndex, - __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn - ); -HRESULT DAPI MultiSzFindString( - __in __nullnullterminated LPCWSTR pwzMultiSz, - __in __nullnullterminated LPCWSTR pwzString, - __out_opt DWORD_PTR* pdwIndex, - __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound - ); -HRESULT DAPI MultiSzRemoveString( - __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, - __in DWORD_PTR dwIndex - ); -HRESULT DAPI MultiSzInsertString( - __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, - __inout_opt SIZE_T* pcchMultiSz, - __in DWORD_PTR dwIndex, - __in_z LPCWSTR pwzInsert - ); -HRESULT DAPI MultiSzReplaceString( - __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, - __in DWORD_PTR dwIndex, - __in_z LPCWSTR pwzString - ); - -LPCWSTR DAPI wcsistr( - __in_z LPCWSTR wzString, - __in_z LPCWSTR wzCharSet - ); - -HRESULT DAPI StrStringToInt16( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out SHORT* psOut - ); -HRESULT DAPI StrStringToUInt16( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out USHORT* pusOut - ); -HRESULT DAPI StrStringToInt32( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out INT* piOut - ); -HRESULT DAPI StrStringToUInt32( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out UINT* puiOut - ); -HRESULT DAPI StrStringToInt64( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out LONGLONG* pllOut - ); -HRESULT DAPI StrStringToUInt64( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out ULONGLONG* pullOut - ); -void DAPI StrStringToUpper( - __inout_z LPWSTR wzIn - ); -void DAPI StrStringToLower( - __inout_z LPWSTR wzIn - ); -HRESULT DAPI StrAllocStringToUpperInvariant( - __deref_out_z LPWSTR* pscz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); -HRESULT DAPI StrAllocStringToLowerInvariant( - __deref_out_z LPWSTR* pscz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); - -HRESULT DAPI StrArrayAllocString( - __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, - __inout LPUINT pcStrArray, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ); - -HRESULT DAPI StrArrayFree( - __in_ecount(cStrArray) LPWSTR *rgsczStrArray, - __in UINT cStrArray - ); - -HRESULT DAPI StrSplitAllocArray( - __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, - __inout LPUINT pcStrArray, - __in_z LPCWSTR wzSource, - __in_z LPCWSTR wzDelim - ); - -HRESULT DAPI StrSecureZeroString( - __in LPWSTR pwz - ); -HRESULT DAPI StrSecureZeroFreeString( - __in LPWSTR pwz - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/svcutil.h b/src/dutil/inc/svcutil.h deleted file mode 100644 index 80d6326c..00000000 --- a/src/dutil/inc/svcutil.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - - -#define ReleaseServiceHandle(h) if (h) { ::CloseServiceHandle(h); h = NULL; } - - -HRESULT DAPI SvcQueryConfig( - __in SC_HANDLE sch, - __out QUERY_SERVICE_CONFIGW** ppConfig - ); - - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h deleted file mode 100644 index d3dd6d21..00000000 --- a/src/dutil/inc/thmutil.h +++ /dev/null @@ -1,765 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseTheme(p) if (p) { ThemeFree(p); p = NULL; } - -typedef HRESULT(CALLBACK *PFNTHM_EVALUATE_VARIABLE_CONDITION)( - __in_z LPCWSTR wzCondition, - __out BOOL* pf, - __in_opt LPVOID pvContext - ); -typedef HRESULT(CALLBACK *PFNTHM_FORMAT_VARIABLE_STRING)( - __in_z LPCWSTR wzFormat, - __inout LPWSTR* psczOut, - __in_opt LPVOID pvContext - ); -typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_NUMERIC)( - __in_z LPCWSTR wzVariable, - __out LONGLONG* pllValue, - __in_opt LPVOID pvContext - ); -typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_NUMERIC)( - __in_z LPCWSTR wzVariable, - __in LONGLONG llValue, - __in_opt LPVOID pvContext - ); -typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_STRING)( - __in_z LPCWSTR wzVariable, - __inout LPWSTR* psczValue, - __in_opt LPVOID pvContext - ); -typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_STRING)( - __in_z LPCWSTR wzVariable, - __in_z_opt LPCWSTR wzValue, - __in BOOL fFormatted, - __in_opt LPVOID pvContext - ); - -typedef enum THEME_ACTION_TYPE -{ - THEME_ACTION_TYPE_BROWSE_DIRECTORY, - THEME_ACTION_TYPE_CHANGE_PAGE, - THEME_ACTION_TYPE_CLOSE_WINDOW, -} THEME_ACTION_TYPE; - -typedef enum THEME_CONTROL_DATA -{ - THEME_CONTROL_DATA_HOVER = 1, -} THEME_CONTROL_DATA; - -typedef enum THEME_CONTROL_TYPE -{ - THEME_CONTROL_TYPE_UNKNOWN, - THEME_CONTROL_TYPE_BILLBOARD, - THEME_CONTROL_TYPE_BUTTON, - THEME_CONTROL_TYPE_CHECKBOX, - THEME_CONTROL_TYPE_COMBOBOX, - THEME_CONTROL_TYPE_COMMANDLINK, - THEME_CONTROL_TYPE_EDITBOX, - THEME_CONTROL_TYPE_HYPERLINK, - THEME_CONTROL_TYPE_HYPERTEXT, - THEME_CONTROL_TYPE_IMAGE, - THEME_CONTROL_TYPE_LABEL, - THEME_CONTROL_TYPE_PANEL, - THEME_CONTROL_TYPE_PROGRESSBAR, - THEME_CONTROL_TYPE_RADIOBUTTON, - THEME_CONTROL_TYPE_RICHEDIT, - THEME_CONTROL_TYPE_STATIC, - THEME_CONTROL_TYPE_LISTVIEW, - THEME_CONTROL_TYPE_TREEVIEW, - THEME_CONTROL_TYPE_TAB, -} THEME_CONTROL_TYPE; - -typedef enum THEME_SHOW_PAGE_REASON -{ - THEME_SHOW_PAGE_REASON_DEFAULT, - THEME_SHOW_PAGE_REASON_CANCEL, - THEME_SHOW_PAGE_REASON_REFRESH, -} THEME_SHOW_PAGE_REASON; - -typedef enum THEME_WINDOW_INITIAL_POSITION -{ - THEME_WINDOW_INITIAL_POSITION_DEFAULT, - THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES, -} THEME_WINDOW_INITIAL_POSITION; - - -struct THEME_COLUMN -{ - LPWSTR pszName; - UINT uStringId; - int nDefaultDpiBaseWidth; - int nBaseWidth; - int nWidth; - BOOL fExpands; -}; - - -struct THEME_TAB -{ - LPWSTR pszName; - UINT uStringId; -}; - -struct THEME_ACTION -{ - LPWSTR sczCondition; - THEME_ACTION_TYPE type; - union - { - struct - { - LPWSTR sczVariableName; - } BrowseDirectory; - struct - { - LPWSTR sczPageName; - BOOL fCancel; - } ChangePage; - }; -}; - -struct THEME_CONDITIONAL_TEXT -{ - LPWSTR sczCondition; - LPWSTR sczText; -}; - -// THEME_ASSIGN_CONTROL_ID - Used to apply a specific id to a named control (usually -// to set the WM_COMMAND). -struct THEME_ASSIGN_CONTROL_ID -{ - WORD wId; // id to apply to control - LPCWSTR wzName; // name of control to match -}; - -const DWORD THEME_FIRST_ASSIGN_CONTROL_ID = 1024; // Recommended first control id to be assigned. - -struct THEME_CONTROL -{ - THEME_CONTROL_TYPE type; - - WORD wId; - WORD wPageId; - - LPWSTR sczName; // optional name for control, used to apply control id and link the control to a variable. - LPWSTR sczText; - LPWSTR sczTooltip; - LPWSTR sczNote; // optional text for command link - int nDefaultDpiX; - int nDefaultDpiY; - int nDefaultDpiHeight; - int nDefaultDpiWidth; - int nX; - int nY; - int nHeight; - int nWidth; - int nSourceX; - int nSourceY; - UINT uStringId; - - LPWSTR sczEnableCondition; - LPWSTR sczVisibleCondition; - BOOL fDisableVariableFunctionality; - - HBITMAP hImage; - HICON hIcon; - - // Don't free these; it's just a handle to the central image lists stored in THEME. The handle is freed once, there. - HIMAGELIST rghImageList[4]; - - DWORD dwStyle; - DWORD dwExtendedStyle; - DWORD dwInternalStyle; - - DWORD dwFontId; - - // child controls - DWORD cControls; - THEME_CONTROL* rgControls; - - // Used by billboard controls - WORD wBillboardInterval; - BOOL fBillboardLoops; - - // Used by button and command link controls - THEME_ACTION* rgActions; - DWORD cActions; - THEME_ACTION* pDefaultAction; - - // Used by hyperlink and owner-drawn button controls - DWORD dwFontHoverId; - DWORD dwFontSelectedId; - - // Used by listview controls - THEME_COLUMN *ptcColumns; - DWORD cColumns; - - // Used by radio button controls - BOOL fLastRadioButton; - LPWSTR sczValue; - LPWSTR sczVariable; - - // Used by tab controls - THEME_TAB *pttTabs; - DWORD cTabs; - - // Used by controls that have text - DWORD cConditionalText; - THEME_CONDITIONAL_TEXT* rgConditionalText; - - // Used by command link controls - DWORD cConditionalNotes; - THEME_CONDITIONAL_TEXT* rgConditionalNotes; - - // state variables that should be ignored - HWND hWnd; - DWORD dwData; // type specific data -}; - - -struct THEME_IMAGELIST -{ - LPWSTR sczName; - - HIMAGELIST hImageList; -}; - -struct THEME_SAVEDVARIABLE -{ - LPWSTR wzName; - LPWSTR sczValue; -}; - -struct THEME_PAGE -{ - WORD wId; - LPWSTR sczName; - - DWORD cControlIndices; - - DWORD cSavedVariables; - THEME_SAVEDVARIABLE* rgSavedVariables; -}; - -struct THEME_FONT_INSTANCE -{ - UINT nDpi; - HFONT hFont; -}; - -struct THEME_FONT -{ - LONG lfHeight; - LONG lfWeight; - BYTE lfUnderline; - BYTE lfQuality; - LPWSTR sczFaceName; - - COLORREF crForeground; - HBRUSH hForeground; - COLORREF crBackground; - HBRUSH hBackground; - - DWORD cFontInstances; - THEME_FONT_INSTANCE* rgFontInstances; -}; - - -struct THEME -{ - WORD wId; - - BOOL fAutoResize; - BOOL fForceResize; - - DWORD dwStyle; - DWORD dwFontId; - HANDLE hIcon; - LPWSTR sczCaption; - int nDefaultDpiHeight; - int nDefaultDpiMinimumHeight; - int nDefaultDpiWidth; - int nDefaultDpiMinimumWidth; - int nHeight; - int nMinimumHeight; - int nWidth; - int nMinimumWidth; - int nWindowHeight; - int nWindowWidth; - int nSourceX; - int nSourceY; - UINT uStringId; - - HBITMAP hImage; - - DWORD cFonts; - THEME_FONT* rgFonts; - - DWORD cPages; - THEME_PAGE* rgPages; - - DWORD cImageLists; - THEME_IMAGELIST* rgImageLists; - - DWORD cControls; - THEME_CONTROL* rgControls; - - // internal state variables -- do not use outside ThmUtil.cpp - HWND hwndParent; // parent for loaded controls - HWND hwndHover; // current hwnd hovered over - DWORD dwCurrentPageId; - HWND hwndTooltip; - - UINT nDpi; - - // callback functions - PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition; - PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString; - PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable; - PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable; - PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable; - PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable; - - LPVOID pvVariableContext; -}; - - -/******************************************************************** - ThemeInitialize - initialized theme management. - -*******************************************************************/ -HRESULT DAPI ThemeInitialize( - __in_opt HMODULE hModule - ); - -/******************************************************************** - ThemeUninitialize - uninitialize theme management. - -*******************************************************************/ -void DAPI ThemeUninitialize(); - -/******************************************************************** - ThemeLoadFromFile - loads a theme from a loose file. - - *******************************************************************/ -HRESULT DAPI ThemeLoadFromFile( - __in_z LPCWSTR wzThemeFile, - __out THEME** ppTheme - ); - -/******************************************************************** - ThemeLoadFromResource - loads a theme from a module's data resource. - - NOTE: The resource data must be UTF-8 encoded. -*******************************************************************/ -HRESULT DAPI ThemeLoadFromResource( - __in_opt HMODULE hModule, - __in_z LPCSTR szResource, - __out THEME** ppTheme - ); - -/******************************************************************** - ThemeFree - frees any memory associated with a theme. - -*******************************************************************/ -void DAPI ThemeFree( - __in THEME* pTheme - ); - -/******************************************************************** -ThemeRegisterVariableCallbacks - registers a context and callbacks - for working with variables. - -*******************************************************************/ -HRESULT DAPI ThemeRegisterVariableCallbacks( - __in THEME* pTheme, - __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition, - __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString, - __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable, - __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable, - __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable, - __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable, - __in_opt LPVOID pvContext - ); - -/******************************************************************** - ThemeCreateParentWindow - creates a parent window for the theme. - -*******************************************************************/ -HRESULT DAPI ThemeCreateParentWindow( - __in THEME* pTheme, - __in DWORD dwExStyle, - __in LPCWSTR szClassName, - __in LPCWSTR szWindowName, - __in DWORD dwStyle, - __in int x, - __in int y, - __in_opt HWND hwndParent, - __in_opt HINSTANCE hInstance, - __in_opt LPVOID lpParam, - __in THEME_WINDOW_INITIAL_POSITION initialPosition, - __out_opt HWND* phWnd - ); - -/******************************************************************** - ThemeLoadControls - creates the windows for all the theme controls - using the window created in ThemeCreateParentWindow. - -*******************************************************************/ -HRESULT DAPI ThemeLoadControls( - __in THEME* pTheme, - __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, - __in DWORD cAssignControlIds - ); - -/******************************************************************** - ThemeUnloadControls - resets all the theme control windows so the theme - controls can be reloaded. - -*******************************************************************/ -void DAPI ThemeUnloadControls( - __in THEME* pTheme - ); - -/******************************************************************** - ThemeLocalize - Localizes all of the strings in the theme. - -*******************************************************************/ -HRESULT DAPI ThemeLocalize( - __in THEME *pTheme, - __in const WIX_LOCALIZATION *pLocStringSet - ); - -HRESULT DAPI ThemeLoadStrings( - __in THEME* pTheme, - __in HMODULE hResModule - ); - -/******************************************************************** - ThemeLoadRichEditFromFile - Attach a richedit control to a RTF file. - - *******************************************************************/ -HRESULT DAPI ThemeLoadRichEditFromFile( - __in THEME* pTheme, - __in DWORD dwControl, - __in_z LPCWSTR wzFileName, - __in HMODULE hModule - ); - -/******************************************************************** - ThemeLoadRichEditFromResource - Attach a richedit control to resource data. - - *******************************************************************/ -HRESULT DAPI ThemeLoadRichEditFromResource( - __in THEME* pTheme, - __in DWORD dwControl, - __in_z LPCSTR szResourceName, - __in HMODULE hModule - ); - -/******************************************************************** - ThemeLoadRichEditFromResourceToHWnd - Attach a richedit control (by - HWND) to resource data. - - *******************************************************************/ -HRESULT DAPI ThemeLoadRichEditFromResourceToHWnd( - __in HWND hWnd, - __in_z LPCSTR szResourceName, - __in HMODULE hModule - ); - -/******************************************************************** - ThemeHandleKeyboardMessage - will translate the message using the active - accelerator table. - -*******************************************************************/ -BOOL DAPI ThemeHandleKeyboardMessage( - __in_opt THEME* pTheme, - __in HWND hWnd, - __in MSG* pMsg - ); - -/******************************************************************** - ThemeDefWindowProc - replacement for DefWindowProc() when using theme. - -*******************************************************************/ -LRESULT CALLBACK ThemeDefWindowProc( - __in_opt THEME* pTheme, - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ); - -/******************************************************************** - ThemeGetPageIds - gets the page ids for the theme via page names. - -*******************************************************************/ -void DAPI ThemeGetPageIds( - __in const THEME* pTheme, - __in_ecount(cGetPages) LPCWSTR* rgwzFindNames, - __inout_ecount(cGetPages) DWORD* rgdwPageIds, - __in DWORD cGetPages - ); - -/******************************************************************** - ThemeGetPage - gets a theme page by id. - - *******************************************************************/ -THEME_PAGE* DAPI ThemeGetPage( - __in const THEME* pTheme, - __in DWORD dwPage - ); - -/******************************************************************** - ThemeShowPage - shows or hides all of the controls in the page at one time. - - *******************************************************************/ -HRESULT DAPI ThemeShowPage( - __in THEME* pTheme, - __in DWORD dwPage, - __in int nCmdShow - ); - -/******************************************************************** -ThemeShowPageEx - shows or hides all of the controls in the page at one time. - When using variables, TSPR_CANCEL reverts any changes made. - TSPR_REFRESH forces reevaluation of conditions. - It is expected that the current page is hidden before - showing a new page. - -*******************************************************************/ -HRESULT DAPI ThemeShowPageEx( - __in THEME* pTheme, - __in DWORD dwPage, - __in int nCmdShow, - __in THEME_SHOW_PAGE_REASON reason - ); - - -/******************************************************************** -ThemeShowChild - shows a control's specified child control, hiding the rest. - -*******************************************************************/ -void DAPI ThemeShowChild( - __in THEME* pTheme, - __in THEME_CONTROL* pParentControl, - __in DWORD dwIndex - ); - -/******************************************************************** - ThemeControlExists - check if a control with the specified id exists. - - *******************************************************************/ -BOOL DAPI ThemeControlExists( - __in const THEME* pTheme, - __in DWORD dwControl - ); - -/******************************************************************** - ThemeControlEnable - enables/disables a control. - - *******************************************************************/ -void DAPI ThemeControlEnable( - __in THEME* pTheme, - __in DWORD dwControl, - __in BOOL fEnable - ); - -/******************************************************************** - ThemeControlEnabled - returns whether a control is enabled/disabled. - - *******************************************************************/ -BOOL DAPI ThemeControlEnabled( - __in THEME* pTheme, - __in DWORD dwControl - ); - -/******************************************************************** - ThemeControlElevates - sets/removes the shield icon on a control. - - *******************************************************************/ -void DAPI ThemeControlElevates( - __in THEME* pTheme, - __in DWORD dwControl, - __in BOOL fElevates - ); - -/******************************************************************** - ThemeShowControl - shows/hides a control. - - *******************************************************************/ -void DAPI ThemeShowControl( - __in THEME* pTheme, - __in DWORD dwControl, - __in int nCmdShow - ); - -/******************************************************************** -ThemeShowControlEx - shows/hides a control with support for -conditional text and notes. - -*******************************************************************/ -void DAPI ThemeShowControlEx( - __in THEME* pTheme, - __in DWORD dwControl, - __in int nCmdShow - ); - -/******************************************************************** - ThemeControlVisible - returns whether a control is visible. - - *******************************************************************/ -BOOL DAPI ThemeControlVisible( - __in THEME* pTheme, - __in DWORD dwControl - ); - -BOOL DAPI ThemePostControlMessage( - __in THEME* pTheme, - __in DWORD dwControl, - __in UINT Msg, - __in WPARAM wParam, - __in LPARAM lParam - ); - -LRESULT DAPI ThemeSendControlMessage( - __in const THEME* pTheme, - __in DWORD dwControl, - __in UINT Msg, - __in WPARAM wParam, - __in LPARAM lParam - ); - -/******************************************************************** - ThemeDrawBackground - draws the theme background. - -*******************************************************************/ -HRESULT DAPI ThemeDrawBackground( - __in THEME* pTheme, - __in PAINTSTRUCT* pps - ); - -/******************************************************************** - ThemeDrawControl - draw an owner drawn control. - -*******************************************************************/ -HRESULT DAPI ThemeDrawControl( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis - ); - -/******************************************************************** - ThemeHoverControl - mark a control as hover. - -*******************************************************************/ -BOOL DAPI ThemeHoverControl( - __in THEME* pTheme, - __in HWND hwndParent, - __in HWND hwndControl - ); - -/******************************************************************** - ThemeIsControlChecked - gets whether a control is checked. Only - really useful for checkbox controls. - -*******************************************************************/ -BOOL DAPI ThemeIsControlChecked( - __in THEME* pTheme, - __in DWORD dwControl - ); - -/******************************************************************** - ThemeSetControlColor - sets the color of text for a control. - -*******************************************************************/ -BOOL DAPI ThemeSetControlColor( - __in THEME* pTheme, - __in HDC hdc, - __in HWND hWnd, - __out HBRUSH* phBackgroundBrush - ); - -/******************************************************************** - ThemeSetProgressControl - sets the current percentage complete in a - progress bar control. - -*******************************************************************/ -HRESULT DAPI ThemeSetProgressControl( - __in THEME* pTheme, - __in DWORD dwControl, - __in DWORD dwProgressPercentage - ); - -/******************************************************************** - ThemeSetProgressControlColor - sets the current color of a - progress bar control. - -*******************************************************************/ -HRESULT DAPI ThemeSetProgressControlColor( - __in THEME* pTheme, - __in DWORD dwControl, - __in DWORD dwColorIndex - ); - -/******************************************************************** - ThemeSetTextControl - sets the text of a control. - -*******************************************************************/ -HRESULT DAPI ThemeSetTextControl( - __in const THEME* pTheme, - __in DWORD dwControl, - __in_z_opt LPCWSTR wzText - ); - -/******************************************************************** -ThemeSetTextControl - sets the text of a control and optionally - invalidates the control. - -*******************************************************************/ -HRESULT DAPI ThemeSetTextControlEx( - __in const THEME* pTheme, - __in DWORD dwControl, - __in BOOL fUpdate, - __in_z_opt LPCWSTR wzText - ); - -/******************************************************************** - ThemeGetTextControl - gets the text of a control. - -*******************************************************************/ -HRESULT DAPI ThemeGetTextControl( - __in const THEME* pTheme, - __in DWORD dwControl, - __inout_z LPWSTR* psczText - ); - -/******************************************************************** - ThemeUpdateCaption - updates the caption in the theme. - -*******************************************************************/ -HRESULT DAPI ThemeUpdateCaption( - __in THEME* pTheme, - __in_z LPCWSTR wzCaption - ); - -/******************************************************************** - ThemeSetFocus - set the focus to the control supplied or the next - enabled control if it is disabled. - -*******************************************************************/ -void DAPI ThemeSetFocus( - __in THEME* pTheme, - __in DWORD dwControl - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/timeutil.h b/src/dutil/inc/timeutil.h deleted file mode 100644 index 3655c00a..00000000 --- a/src/dutil/inc/timeutil.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI TimeFromString( - __in_z LPCWSTR wzTime, - __out FILETIME* pFileTime - ); -HRESULT DAPI TimeFromString3339( - __in_z LPCWSTR wzTime, - __out FILETIME* pFileTime - ); -HRESULT DAPI TimeCurrentTime( - __deref_out_z LPWSTR* ppwz, - __in BOOL fGMT - ); -HRESULT DAPI TimeCurrentDateTime( - __deref_out_z LPWSTR* ppwz, - __in BOOL fGMT - ); -HRESULT DAPI TimeSystemDateTime( - __deref_out_z LPWSTR* ppwz, - __in const SYSTEMTIME *pst, - __in BOOL fGMT - ); -HRESULT DAPI TimeSystemToDateTimeString( - __deref_out_z LPWSTR* ppwz, - __in const SYSTEMTIME *pst, - __in LCID locale - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/uncutil.h b/src/dutil/inc/uncutil.h deleted file mode 100644 index 6516a801..00000000 --- a/src/dutil/inc/uncutil.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -/******************************************************************* - UncConvertFromMountedDrive - Converts the string in-place from a - mounted drive path to a UNC path -*******************************************************************/ -DAPI_(HRESULT) UncConvertFromMountedDrive( - __inout LPWSTR *psczUNCPath, - __in LPCWSTR sczMountedDrivePath - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/uriutil.h b/src/dutil/inc/uriutil.h deleted file mode 100644 index d6dfdd6b..00000000 --- a/src/dutil/inc/uriutil.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include "wininet.h" - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum URI_PROTOCOL -{ - URI_PROTOCOL_UNKNOWN, - URI_PROTOCOL_FILE, - URI_PROTOCOL_FTP, - URI_PROTOCOL_HTTP, - URI_PROTOCOL_HTTPS, - URI_PROTOCOL_LOCAL, - URI_PROTOCOL_UNC -} URI_PROTOCOL; - -typedef struct _URI_INFO -{ - INTERNET_SCHEME scheme; - LPWSTR sczHostName; - INTERNET_PORT port; - LPWSTR sczUser; - LPWSTR sczPassword; - LPWSTR sczPath; - LPWSTR sczQueryString; -} URI_INFO; - - -HRESULT DAPI UriCanonicalize( - __inout_z LPWSTR* psczUri - ); - -HRESULT DAPI UriCrack( - __in_z LPCWSTR wzUri, - __out_opt INTERNET_SCHEME* pScheme, - __deref_opt_out_z LPWSTR* psczHostName, - __out_opt INTERNET_PORT* pPort, - __deref_opt_out_z LPWSTR* psczUser, - __deref_opt_out_z LPWSTR* psczPassword, - __deref_opt_out_z LPWSTR* psczPath, - __deref_opt_out_z LPWSTR* psczQueryString - ); - -HRESULT DAPI UriCrackEx( - __in_z LPCWSTR wzUri, - __in URI_INFO* pUriInfo - ); - -void DAPI UriInfoUninitialize( - __in URI_INFO* pUriInfo - ); - -HRESULT DAPI UriCreate( - __inout_z LPWSTR* psczUri, - __in INTERNET_SCHEME scheme, - __in_z_opt LPWSTR wzHostName, - __in INTERNET_PORT port, - __in_z_opt LPWSTR wzUser, - __in_z_opt LPWSTR wzPassword, - __in_z_opt LPWSTR wzPath, - __in_z_opt LPWSTR wzQueryString - ); - -HRESULT DAPI UriCanonicalize( - __inout_z LPWSTR* psczUri - ); - -HRESULT DAPI UriFile( - __deref_out_z LPWSTR* psczFile, - __in_z LPCWSTR wzUri - ); - -HRESULT DAPI UriProtocol( - __in_z LPCWSTR wzUri, - __out URI_PROTOCOL* pProtocol - ); - -HRESULT DAPI UriRoot( - __in_z LPCWSTR wzUri, - __out LPWSTR* ppwzRoot, - __out_opt URI_PROTOCOL* pProtocol - ); - -HRESULT DAPI UriResolve( - __in_z LPCWSTR wzUri, - __in_opt LPCWSTR wzBaseUri, - __out LPWSTR* ppwzResolvedUri, - __out_opt URI_PROTOCOL* pResolvedProtocol - ); - -#ifdef __cplusplus -} -#endif - diff --git a/src/dutil/inc/userutil.h b/src/dutil/inc/userutil.h deleted file mode 100644 index 2c86d229..00000000 --- a/src/dutil/inc/userutil.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI UserBuildDomainUserName( - __out_ecount_z(cchDest) LPWSTR wzDest, - __in int cchDest, - __in_z LPCWSTR pwzName, - __in_z LPCWSTR pwzDomain - ); - -HRESULT DAPI UserCheckIsMember( - __in_z LPCWSTR pwzName, - __in_z LPCWSTR pwzDomain, - __in_z LPCWSTR pwzGroupName, - __in_z LPCWSTR pwzGroupDomain, - __out LPBOOL lpfMember - ); - -HRESULT DAPI UserCreateADsPath( - __in_z LPCWSTR wzObjectDomain, - __in_z LPCWSTR wzObjectName, - __out BSTR *pbstrAdsPath - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/verutil.h b/src/dutil/inc/verutil.h deleted file mode 100644 index 5247bb61..00000000 --- a/src/dutil/inc/verutil.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -#define ReleaseVerutilVersion(p) if (p) { VerFreeVersion(p); p = NULL; } - -typedef struct _VERUTIL_VERSION_RELEASE_LABEL -{ - BOOL fNumeric; - DWORD dwValue; - SIZE_T cchLabelOffset; - int cchLabel; -} VERUTIL_VERSION_RELEASE_LABEL; - -typedef struct _VERUTIL_VERSION -{ - LPWSTR sczVersion; - DWORD dwMajor; - DWORD dwMinor; - DWORD dwPatch; - DWORD dwRevision; - DWORD cReleaseLabels; - VERUTIL_VERSION_RELEASE_LABEL* rgReleaseLabels; - SIZE_T cchMetadataOffset; - BOOL fInvalid; -} VERUTIL_VERSION; - -/******************************************************************* - VerCompareParsedVersions - compares the Verutil versions. - -*******************************************************************/ -HRESULT DAPI VerCompareParsedVersions( - __in_opt VERUTIL_VERSION* pVersion1, - __in_opt VERUTIL_VERSION* pVersion2, - __out int* pnResult - ); - -/******************************************************************* - VerCompareStringVersions - parses the strings with VerParseVersion and then - compares the Verutil versions with VerCompareParsedVersions. - -*******************************************************************/ -HRESULT DAPI VerCompareStringVersions( - __in_z LPCWSTR wzVersion1, - __in_z LPCWSTR wzVersion2, - __in BOOL fStrict, - __out int* pnResult - ); - -/******************************************************************** - VerCopyVersion - copies the given Verutil version. - -*******************************************************************/ -HRESULT DAPI VerCopyVersion( - __in VERUTIL_VERSION* pSource, - __out VERUTIL_VERSION** ppVersion - ); - -/******************************************************************** - VerFreeVersion - frees any memory associated with a Verutil version. - -*******************************************************************/ -void DAPI VerFreeVersion( - __in VERUTIL_VERSION* pVersion - ); - -/******************************************************************* - VerParseVersion - parses the string into a Verutil version. - -*******************************************************************/ -HRESULT DAPI VerParseVersion( - __in_z LPCWSTR wzVersion, - __in SIZE_T cchVersion, - __in BOOL fStrict, - __out VERUTIL_VERSION** ppVersion - ); - -/******************************************************************* - VerParseVersion - parses the QWORD into a Verutil version. - -*******************************************************************/ -HRESULT DAPI VerVersionFromQword( - __in DWORD64 qwVersion, - __out VERUTIL_VERSION** ppVersion - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/wiutil.h b/src/dutil/inc/wiutil.h deleted file mode 100644 index 9c2de209..00000000 --- a/src/dutil/inc/wiutil.h +++ /dev/null @@ -1,402 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -// constants - -#define IDNOACTION 0 -#define WIU_MB_OKIGNORECANCELRETRY 0xE - -#define MAX_DARWIN_KEY 73 -#define MAX_DARWIN_COLUMN 255 - -#define WIU_LOG_DEFAULT INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | \ - INSTALLLOGMODE_USER | INSTALLLOGMODE_INFO | INSTALLLOGMODE_RESOLVESOURCE | \ - INSTALLLOGMODE_OUTOFDISKSPACE | INSTALLLOGMODE_ACTIONSTART | \ - INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA | INSTALLLOGMODE_PROPERTYDUMP - -#define ReleaseMsi(h) if (h) { ::MsiCloseHandle(h); } -#define ReleaseNullMsi(h) if (h) { ::MsiCloseHandle(h); h = NULL; } - - -typedef enum WIU_RESTART -{ - WIU_RESTART_NONE, - WIU_RESTART_REQUIRED, - WIU_RESTART_INITIATED, -} WIU_RESTART; - -typedef enum WIU_MSI_EXECUTE_MESSAGE_TYPE -{ - WIU_MSI_EXECUTE_MESSAGE_NONE, - WIU_MSI_EXECUTE_MESSAGE_PROGRESS, - WIU_MSI_EXECUTE_MESSAGE_ERROR, - WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE, - WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE, -} WIU_MSI_EXECUTE_MESSAGE_TYPE; - - -// structures - -typedef struct _WIU_MSI_EXECUTE_MESSAGE -{ - WIU_MSI_EXECUTE_MESSAGE_TYPE type; - DWORD dwAllowedResults; - - DWORD cData; - LPCWSTR* rgwzData; - - INT nResultRecommendation; // recommended return result for this message based on analysis of real world installs. - - union - { - struct - { - DWORD dwPercentage; - } progress; - struct - { - DWORD dwErrorCode; - LPCWSTR wzMessage; - } error; - struct - { - INSTALLMESSAGE mt; - LPCWSTR wzMessage; - } msiMessage; - struct - { - DWORD cFiles; - LPCWSTR* rgwzFiles; - } msiFilesInUse; - }; -} WIU_MSI_EXECUTE_MESSAGE; - -typedef struct _WIU_MSI_PROGRESS -{ - DWORD dwTotal; - DWORD dwCompleted; - DWORD dwStep; - BOOL fMoveForward; - BOOL fEnableActionData; - BOOL fScriptInProgress; -} WIU_MSI_PROGRESS; - - -typedef int (*PFN_MSIEXECUTEMESSAGEHANDLER)( - __in WIU_MSI_EXECUTE_MESSAGE* pMessage, - __in_opt LPVOID pvContext - ); - -typedef struct _WIU_MSI_EXECUTE_CONTEXT -{ - BOOL fRollback; - PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler; - LPVOID pvContext; - WIU_MSI_PROGRESS rgMsiProgress[64]; - DWORD dwCurrentProgressIndex; - - INSTALLUILEVEL previousInstallUILevel; - HWND hwndPreviousParentWindow; - INSTALLUI_HANDLERW pfnPreviousExternalUI; - INSTALLUI_HANDLER_RECORD pfnPreviousExternalUIRecord; - - BOOL fSetPreviousExternalUIRecord; - BOOL fSetPreviousExternalUI; -} WIU_MSI_EXECUTE_CONTEXT; - - -// typedefs -typedef UINT (WINAPI *PFN_MSIENABLELOGW)( - __in DWORD dwLogMode, - __in_z LPCWSTR szLogFile, - __in DWORD dwLogAttributes - ); -typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOW)( - __in LPCWSTR szProductCode, - __in LPCWSTR szProperty, - __out_ecount_opt(*pcchValue) LPWSTR szValue, - __inout LPDWORD pcchValue - ); -typedef INSTALLSTATE (WINAPI *PFN_MSIGETCOMPONENTPATHW)( - __in LPCWSTR szProduct, - __in LPCWSTR szComponent, - __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, - __inout_opt LPDWORD pcchBuf - ); -typedef INSTALLSTATE (WINAPI *PFN_MSILOCATECOMPONENTW)( - __in LPCWSTR szComponent, - __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, - __inout_opt LPDWORD pcchBuf - ); -typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOEXW)( - __in LPCWSTR szProductCode, - __in_opt LPCWSTR szUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in LPCWSTR szProperty, - __out_ecount_opt(*pcchValue) LPWSTR szValue, - __inout_opt LPDWORD pcchValue - ); -typedef INSTALLSTATE (WINAPI *PFN_MSIQUERYFEATURESTATEW)( - __in LPCWSTR szProduct, - __in LPCWSTR szFeature - ); -typedef UINT (WINAPI *PFN_MSIGETPATCHINFOEXW)( - __in_z LPCWSTR wzPatchCode, - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in_z LPCWSTR wzProperty, - __out_opt LPWSTR wzValue, - __inout DWORD* pcchValue - ); -typedef UINT (WINAPI *PFN_MSIDETERMINEPATCHSEQUENCEW)( - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT context, - __in DWORD cPatchInfo, - __in PMSIPATCHSEQUENCEINFOW pPatchInfo - ); -typedef UINT (WINAPI *PFN_MSIDETERMINEAPPLICABLEPATCHESW)( - __in_z LPCWSTR wzProductPackagePath, - __in DWORD cPatchInfo, - __in PMSIPATCHSEQUENCEINFOW pPatchInfo - ); -typedef UINT (WINAPI *PFN_MSIINSTALLPRODUCTW)( - __in LPCWSTR szPackagePath, - __in_opt LPCWSTR szCommandLine - ); -typedef UINT (WINAPI *PFN_MSICONFIGUREPRODUCTEXW)( - __in LPCWSTR szProduct, - __in int iInstallLevel, - __in INSTALLSTATE eInstallState, - __in_opt LPCWSTR szCommandLine - ); -typedef UINT (WINAPI *PFN_MSIREMOVEPATCHESW)( - __in_z LPCWSTR wzPatchList, - __in_z LPCWSTR wzProductCode, - __in INSTALLTYPE eUninstallType, - __in_z_opt LPCWSTR szPropertyList - ); -typedef INSTALLUILEVEL (WINAPI *PFN_MSISETINTERNALUI)( - __in INSTALLUILEVEL dwUILevel, - __inout_opt HWND *phWnd - ); -typedef UINT (WINAPI *PFN_MSISETEXTERNALUIRECORD)( - __in_opt INSTALLUI_HANDLER_RECORD puiHandler, - __in DWORD dwMessageFilter, - __in_opt LPVOID pvContext, - __out_opt PINSTALLUI_HANDLER_RECORD ppuiPrevHandler - ); -typedef INSTALLUI_HANDLERW (WINAPI *PFN_MSISETEXTERNALUIW)( - __in_opt INSTALLUI_HANDLERW puiHandler, - __in DWORD dwMessageFilter, - __in_opt LPVOID pvContext - ); -typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSW)( - __in DWORD iProductIndex, - __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf - ); -typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSEXW)( - __in_z_opt LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in DWORD dwContext, - __in DWORD dwIndex, - __out_opt WCHAR wzInstalledProductCode[39], - __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, - __out_opt LPWSTR wzSid, - __inout_opt LPDWORD pcchSid - ); - -typedef UINT (WINAPI *PFN_MSIENUMRELATEDPRODUCTSW)( - __in LPCWSTR lpUpgradeCode, - __reserved DWORD dwReserved, - __in DWORD iProductIndex, - __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf - ); -typedef UINT (WINAPI *PFN_MSISOURCELISTADDSOURCEEXW)( - __in LPCWSTR szProductCodeOrPatchCode, - __in_opt LPCWSTR szUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in DWORD dwOptions, - __in LPCWSTR szSource, - __in_opt DWORD dwIndex - ); -typedef UINT(WINAPI* PFN_MSIBEGINTRANSACTIONW)( - __in LPCWSTR szName, - __in DWORD dwTransactionAttributes, - __out MSIHANDLE* phTransactionHandle, - __out HANDLE* phChangeOfOwnerEvent - ); -typedef UINT(WINAPI* PFN_MSIENDTRANSACTION)( - __in DWORD dwTransactionState - ); - - -HRESULT DAPI WiuInitialize( - ); -void DAPI WiuUninitialize( - ); -void DAPI WiuFunctionOverride( - __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW, - __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW, - __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW, - __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW, - __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW, - __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW, - __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW, - __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW, - __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI, - __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW, - __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW, - __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord, - __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW - ); -HRESULT DAPI WiuGetComponentPath( - __in_z LPCWSTR wzProductCode, - __in_z LPCWSTR wzComponentId, - __out INSTALLSTATE* pInstallState, - __out_z LPWSTR* psczValue - ); -HRESULT DAPI WiuLocateComponent( - __in_z LPCWSTR wzComponentId, - __out INSTALLSTATE* pInstallState, - __out_z LPWSTR* psczValue - ); -HRESULT DAPI WiuQueryFeatureState( - __in_z LPCWSTR wzProduct, - __in_z LPCWSTR wzFeature, - __out INSTALLSTATE* pInstallState - ); -HRESULT DAPI WiuGetProductInfo( - __in_z LPCWSTR wzProductCode, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ); -HRESULT DAPI WiuGetProductInfoEx( - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ); -HRESULT DAPI WiuGetProductProperty( - __in MSIHANDLE hProduct, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ); -HRESULT DAPI WiuGetPatchInfoEx( - __in_z LPCWSTR wzPatchCode, - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ); -HRESULT DAPI WiuDeterminePatchSequence( - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT context, - __in PMSIPATCHSEQUENCEINFOW pPatchInfo, - __in DWORD cPatchInfo - ); -HRESULT DAPI WiuDetermineApplicablePatches( - __in_z LPCWSTR wzProductPackagePath, - __in PMSIPATCHSEQUENCEINFOW pPatchInfo, - __in DWORD cPatchInfo - ); -HRESULT DAPI WiuEnumProducts( - __in DWORD iProductIndex, - __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode - ); -HRESULT DAPI WiuEnumProductsEx( - __in_z_opt LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in DWORD dwContext, - __in DWORD dwIndex, - __out_opt WCHAR wzInstalledProductCode[39], - __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, - __out_opt LPWSTR wzSid, - __inout_opt LPDWORD pcchSid - ); -HRESULT DAPI WiuEnumRelatedProducts( - __in_z LPCWSTR wzUpgradeCode, - __in DWORD iProductIndex, - __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode - ); -HRESULT DAPI WiuEnumRelatedProductCodes( - __in_z LPCWSTR wzUpgradeCode, - __deref_out_ecount_opt(*pcRelatedProducts) LPWSTR** prgsczProductCodes, - __out DWORD* pcRelatedProducts, - __in BOOL fReturnHighestVersionOnly - ); -HRESULT DAPI WiuEnableLog( - __in DWORD dwLogMode, - __in_z LPCWSTR wzLogFile, - __in DWORD dwLogAttributes - ); -HRESULT DAPI WiuInitializeInternalUI( - __in INSTALLUILEVEL internalUILevel, - __in_opt HWND hwndParent, - __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext - ); -HRESULT DAPI WiuInitializeExternalUI( - __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, - __in INSTALLUILEVEL internalUILevel, - __in_opt HWND hwndParent, - __in LPVOID pvContext, - __in BOOL fRollback, - __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext - ); -void DAPI WiuUninitializeExternalUI( - __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext - ); -HRESULT DAPI WiuConfigureProductEx( - __in_z LPCWSTR wzProduct, - __in int iInstallLevel, - __in INSTALLSTATE eInstallState, - __in_z LPCWSTR wzCommandLine, - __out WIU_RESTART* pRestart - ); -HRESULT DAPI WiuInstallProduct( - __in_z LPCWSTR wzPackagPath, - __in_z LPCWSTR wzCommandLine, - __out WIU_RESTART* pRestart - ); -HRESULT DAPI WiuRemovePatches( - __in_z LPCWSTR wzPatchList, - __in_z LPCWSTR wzProductCode, - __in_z LPCWSTR wzPropertyList, - __out WIU_RESTART* pRestart - ); -HRESULT DAPI WiuSourceListAddSourceEx( - __in_z LPCWSTR wzProductCodeOrPatchCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in DWORD dwCode, - __in_z LPCWSTR wzSource, - __in_opt DWORD dwIndex - ); -HRESULT DAPI WiuBeginTransaction( - __in_z LPCWSTR szName, - __in DWORD dwTransactionAttributes, - __out MSIHANDLE* phTransactionHandle, - __out HANDLE* phChangeOfOwnerEvent, - __in DWORD dwLogMode, - __in_z LPCWSTR szLogPath - ); -HRESULT DAPI WiuEndTransaction( - __in DWORD dwTransactionState, - __in DWORD dwLogMode, - __in_z LPCWSTR szLogPath - ); -BOOL DAPI WiuIsMsiTransactionSupported( - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inc/wuautil.h b/src/dutil/inc/wuautil.h deleted file mode 100644 index b239c4e6..00000000 --- a/src/dutil/inc/wuautil.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -HRESULT DAPI WuaPauseAutomaticUpdates(); - -HRESULT DAPI WuaResumeAutomaticUpdates(); - -HRESULT DAPI WuaRestartRequired( - __out BOOL* pfRestartRequired - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/dutil/inc/xmlutil.h b/src/dutil/inc/xmlutil.h deleted file mode 100644 index ba92ada9..00000000 --- a/src/dutil/inc/xmlutil.h +++ /dev/null @@ -1,167 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument = {0x2933BF90, 0x7B36, 0x11d2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument20 = {0xF6D90F11, 0x9C73, 0x11D3, {0xB3, 0x2E, 0x00, 0xC0, 0x4F, 0x99, 0x0B, 0xB4}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument26 = {0xf5078f1b, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument30 = {0xf5078f32, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument40 = {0x88d969c0, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument50 = {0x88d969e5, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument60 = {0x88d96a05, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; -extern __declspec(selectany) const CLSID XmlUtil_CLSID_XMLSchemaCache = {0x88d969c2, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; - -extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument = {0x2933BF81, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; -extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument2 = {0x2933BF95, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; -extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMSchemaCollection = {0x373984C8, 0xB845, 0x449B, {0x91, 0xE7, 0x45, 0xAC, 0x83, 0x03, 0x6A, 0xDE}}; - -typedef enum XML_LOAD_ATTRIBUTE -{ - XML_LOAD_PRESERVE_WHITESPACE = 1, -} XML_LOAD_ATTRIBUTE; - - -#ifdef __cplusplus -extern "C" { -#endif - -HRESULT DAPI XmlInitialize(); -void DAPI XmlUninitialize(); - -HRESULT DAPI XmlCreateElement( - __in IXMLDOMDocument *pixdDocument, - __in_z LPCWSTR wzElementName, - __out IXMLDOMElement **ppixnElement - ); -HRESULT DAPI XmlCreateDocument( - __in_opt LPCWSTR pwzElementName, - __out IXMLDOMDocument** ppixdDocument, - __out_opt IXMLDOMElement** ppixeRootElement = NULL - ); -HRESULT DAPI XmlLoadDocument( - __in_z LPCWSTR wzDocument, - __out IXMLDOMDocument** ppixdDocument - ); -HRESULT DAPI XmlLoadDocumentEx( - __in_z LPCWSTR wzDocument, - __in DWORD dwAttributes, - __out IXMLDOMDocument** ppixdDocument - ); -HRESULT DAPI XmlLoadDocumentFromFile( - __in_z LPCWSTR wzPath, - __out IXMLDOMDocument** ppixdDocument - ); -HRESULT DAPI XmlLoadDocumentFromBuffer( - __in_bcount(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __out IXMLDOMDocument** ppixdDocument - ); -HRESULT DAPI XmlLoadDocumentFromFileEx( - __in_z LPCWSTR wzPath, - __in DWORD dwAttributes, - __out IXMLDOMDocument** ppixdDocument - ); -HRESULT DAPI XmlSelectSingleNode( - __in IXMLDOMNode* pixnParent, - __in_z LPCWSTR wzXPath, - __out IXMLDOMNode **ppixnChild - ); -HRESULT DAPI XmlSetAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __in_z LPCWSTR pwzAttributeValue - ); -HRESULT DAPI XmlCreateTextNode( - __in IXMLDOMDocument *pixdDocument, - __in_z LPCWSTR wzText, - __out IXMLDOMText **ppixnTextNode - ); -HRESULT DAPI XmlGetText( - __in IXMLDOMNode* pixnNode, - __deref_out_z BSTR* pbstrText - ); -HRESULT DAPI XmlGetAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __deref_out_z BSTR* pbstrAttributeValue - ); -HRESULT DAPI XmlGetAttributeEx( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR wzAttribute, - __deref_out_z LPWSTR* psczAttributeValue - ); -HRESULT DAPI XmlGetYesNoAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR wzAttribute, - __out BOOL* pfYes - ); -HRESULT DAPI XmlGetAttributeNumber( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __out DWORD* pdwValue - ); -HRESULT DAPI XmlGetAttributeNumberBase( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __in int nBase, - __out DWORD* pdwValue - ); -HRESULT DAPI XmlGetAttributeLargeNumber( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __out DWORD64* pdw64Value - ); -HRESULT DAPI XmlGetNamedItem( - __in IXMLDOMNamedNodeMap *pixnmAttributes, - __in_opt LPCWSTR wzName, - __out IXMLDOMNode **ppixnNamedItem - ); -HRESULT DAPI XmlSetText( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzText - ); -HRESULT DAPI XmlSetTextNumber( - __in IXMLDOMNode *pixnNode, - __in DWORD dwValue - ); -HRESULT DAPI XmlCreateChild( - __in IXMLDOMNode* pixnParent, - __in_z LPCWSTR pwzElementType, - __out IXMLDOMNode** ppixnChild - ); -HRESULT DAPI XmlRemoveAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute - ); -HRESULT DAPI XmlSelectNodes( - __in IXMLDOMNode* pixnParent, - __in_z LPCWSTR wzXPath, - __out IXMLDOMNodeList **ppixnChild - ); -HRESULT DAPI XmlNextAttribute( - __in IXMLDOMNamedNodeMap* pixnnm, - __out IXMLDOMNode** pixnAttribute, - __deref_opt_out_z_opt BSTR* pbstrAttribute - ); -HRESULT DAPI XmlNextElement( - __in IXMLDOMNodeList* pixnl, - __out IXMLDOMNode** pixnElement, - __deref_opt_out_z_opt BSTR* pbstrElement - ); -HRESULT DAPI XmlRemoveChildren( - __in IXMLDOMNode* pixnSource, - __in_z LPCWSTR pwzXPath - ); -HRESULT DAPI XmlSaveDocument( - __in IXMLDOMDocument* pixdDocument, - __inout LPCWSTR wzPath - ); -HRESULT DAPI XmlSaveDocumentToBuffer( - __in IXMLDOMDocument* pixdDocument, - __deref_out_bcount(*pcbDest) BYTE** ppbDest, - __out DWORD* pcbDest - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/dutil/inetutil.cpp b/src/dutil/inetutil.cpp deleted file mode 100644 index 8dace55f..00000000 --- a/src/dutil/inetutil.cpp +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define InetExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) -#define InetExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) -#define InetExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) -#define InetExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) -#define InetExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) -#define InetExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) -#define InetExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INETUTIL, p, x, e, s, __VA_ARGS__) -#define InetExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INETUTIL, p, x, s, __VA_ARGS__) -#define InetExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INETUTIL, p, x, e, s, __VA_ARGS__) -#define InetExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INETUTIL, p, x, s, __VA_ARGS__) -#define InetExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INETUTIL, e, x, s, __VA_ARGS__) -#define InetExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INETUTIL, g, x, s, __VA_ARGS__) - - -/******************************************************************* - InternetGetSizeByHandle - returns size of file by url handle - -*******************************************************************/ -extern "C" HRESULT DAPI InternetGetSizeByHandle( - __in HINTERNET hiFile, - __out LONGLONG* pllSize - ) -{ - Assert(pllSize); - - HRESULT hr = S_OK; - DWORD dwSize = 0; - DWORD cb = 0; - - cb = sizeof(dwSize); - if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, reinterpret_cast(&dwSize), &cb, NULL)) - { - InetExitOnLastError(hr, "Failed to get size for internet file handle"); - } - - *pllSize = dwSize; -LExit: - return hr; -} - - -/******************************************************************* - InetGetCreateTimeByHandle - returns url creation time - -******************************************************************/ -extern "C" HRESULT DAPI InternetGetCreateTimeByHandle( - __in HINTERNET hiFile, - __out LPFILETIME pft - ) -{ - Assert(pft); - - HRESULT hr = S_OK; - SYSTEMTIME st = {0 }; - DWORD cb = sizeof(SYSTEMTIME); - - if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, reinterpret_cast(&st), &cb, NULL)) - { - InetExitWithLastError(hr, "failed to get create time for internet file handle"); - } - - if (!::SystemTimeToFileTime(&st, pft)) - { - InetExitWithLastError(hr, "failed to convert system time to file time"); - } - -LExit: - return hr; -} - - -/******************************************************************* - InternetQueryInfoString - query info string - -*******************************************************************/ -extern "C" HRESULT DAPI InternetQueryInfoString( - __in HINTERNET hRequest, - __in DWORD dwInfo, - __deref_out_z LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - SIZE_T cbOriginal = 0; - DWORD cbValue = 0; - DWORD dwIndex = 0; - - // If nothing was provided start off with some arbitrary size. - if (!*psczValue) - { - hr = StrAlloc(psczValue, 64); - InetExitOnFailure(hr, "Failed to allocate memory for value."); - } - - hr = StrSize(*psczValue, &cbOriginal); - InetExitOnFailure(hr, "Failed to get size of value."); - - cbValue = (DWORD)min(DWORD_MAX, cbOriginal); - - if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), &cbValue, &dwIndex)) - { - DWORD er = ::GetLastError(); - if (ERROR_INSUFFICIENT_BUFFER == er) - { - cbValue += sizeof(WCHAR); // add one character for the null terminator. - - hr = StrAlloc(psczValue, cbValue / sizeof(WCHAR)); - InetExitOnFailure(hr, "Failed to allocate value."); - - if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), &cbValue, &dwIndex)) - { - er = ::GetLastError(); - } - else - { - er = ERROR_SUCCESS; - } - } - - hr = HRESULT_FROM_WIN32(er); - InetExitOnRootFailure(hr, "Failed to get query information."); - } - -LExit: - return hr; -} - - -/******************************************************************* - InternetQueryInfoNumber - query info number - -*******************************************************************/ -extern "C" HRESULT DAPI InternetQueryInfoNumber( - __in HINTERNET hRequest, - __in DWORD dwInfo, - __inout LONG* plInfo - ) -{ - HRESULT hr = S_OK; - DWORD cbCode = sizeof(LONG); - DWORD dwIndex = 0; - - if (!::HttpQueryInfoW(hRequest, dwInfo | HTTP_QUERY_FLAG_NUMBER, static_cast(plInfo), &cbCode, &dwIndex)) - { - InetExitWithLastError(hr, "Failed to get query information."); - } - -LExit: - return hr; -} diff --git a/src/dutil/iniutil.cpp b/src/dutil/iniutil.cpp deleted file mode 100644 index 70b62995..00000000 --- a/src/dutil/iniutil.cpp +++ /dev/null @@ -1,768 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define IniExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) -#define IniExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) -#define IniExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) -#define IniExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) -#define IniExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) -#define IniExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) -#define IniExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__) -#define IniExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__) -#define IniExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__) -#define IniExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__) -#define IniExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INIUTIL, e, x, s, __VA_ARGS__) -#define IniExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INIUTIL, g, x, s, __VA_ARGS__) - -const LPCWSTR wzSectionSeparator = L"\\"; - -struct INI_STRUCT -{ - LPWSTR sczPath; // the path to the INI file to be parsed - - LPWSTR sczOpenTagPrefix; // For regular ini, this would be '[' - LPWSTR sczOpenTagPostfix; // For regular ini, this would be ']' - - LPWSTR sczValuePrefix; // for regular ini, this would be NULL - LPWSTR sczValueSeparator; // for regular ini, this would be '=' - - LPWSTR *rgsczValueSeparatorExceptions; - DWORD cValueSeparatorExceptions; - - LPWSTR sczCommentLinePrefix; // for regular ini, this would be ';' - - INI_VALUE *rgivValues; - DWORD cValues; - - LPWSTR *rgsczLines; - DWORD cLines; - - FILE_ENCODING feEncoding; - BOOL fModified; -}; - -const int INI_HANDLE_BYTES = sizeof(INI_STRUCT); - -static HRESULT GetSectionPrefixFromName( - __in_z LPCWSTR wzName, - __deref_inout_z LPWSTR* psczOutput - ); -static void UninitializeIniValue( - INI_VALUE *pivValue - ); - -extern "C" HRESULT DAPI IniInitialize( - __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle - ) -{ - HRESULT hr = S_OK; - - // Allocate the handle - *piHandle = static_cast(MemAlloc(sizeof(INI_STRUCT), TRUE)); - IniExitOnNull(*piHandle, hr, E_OUTOFMEMORY, "Failed to allocate ini object"); - -LExit: - return hr; -} - -extern "C" void DAPI IniUninitialize( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle - ) -{ - INI_STRUCT *pi = static_cast(piHandle); - - ReleaseStr(pi->sczPath); - ReleaseStr(pi->sczOpenTagPrefix); - ReleaseStr(pi->sczOpenTagPostfix); - ReleaseStr(pi->sczValuePrefix); - ReleaseStr(pi->sczValueSeparator); - - for (DWORD i = 0; i < pi->cValueSeparatorExceptions; ++i) - { - ReleaseStr(pi->rgsczValueSeparatorExceptions + i); - } - - ReleaseStr(pi->sczCommentLinePrefix); - - for (DWORD i = 0; i < pi->cValues; ++i) - { - UninitializeIniValue(pi->rgivValues + i); - } - ReleaseMem(pi->rgivValues); - - ReleaseStrArray(pi->rgsczLines, pi->cLines); - - ReleaseMem(pi); -} - -extern "C" HRESULT DAPI IniSetOpenTag( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzOpenTagPrefix, - __in_z_opt LPCWSTR wzOpenTagPostfix - ) -{ - HRESULT hr = S_OK; - - INI_STRUCT *pi = static_cast(piHandle); - - if (wzOpenTagPrefix) - { - hr = StrAllocString(&pi->sczOpenTagPrefix, wzOpenTagPrefix, 0); - IniExitOnFailure(hr, "Failed to copy open tag prefix to ini struct: %ls", wzOpenTagPrefix); - } - else - { - ReleaseNullStr(pi->sczOpenTagPrefix); - } - - if (wzOpenTagPostfix) - { - hr = StrAllocString(&pi->sczOpenTagPostfix, wzOpenTagPostfix, 0); - IniExitOnFailure(hr, "Failed to copy open tag postfix to ini struct: %ls", wzOpenTagPostfix); - } - else - { - ReleaseNullStr(pi->sczOpenTagPrefix); - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI IniSetValueStyle( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzValuePrefix, - __in_z_opt LPCWSTR wzValueSeparator - ) -{ - HRESULT hr = S_OK; - - INI_STRUCT *pi = static_cast(piHandle); - - if (wzValuePrefix) - { - hr = StrAllocString(&pi->sczValuePrefix, wzValuePrefix, 0); - IniExitOnFailure(hr, "Failed to copy value prefix to ini struct: %ls", wzValuePrefix); - } - else - { - ReleaseNullStr(pi->sczValuePrefix); - } - - if (wzValueSeparator) - { - hr = StrAllocString(&pi->sczValueSeparator, wzValueSeparator, 0); - IniExitOnFailure(hr, "Failed to copy value separator to ini struct: %ls", wzValueSeparator); - } - else - { - ReleaseNullStr(pi->sczValueSeparator); - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI IniSetValueSeparatorException( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z LPCWSTR wzValueNamePrefix - ) -{ - HRESULT hr = S_OK; - DWORD dwInsertedIndex = 0; - - INI_STRUCT *pi = static_cast(piHandle); - - hr = MemEnsureArraySize(reinterpret_cast(&pi->rgsczValueSeparatorExceptions), pi->cValueSeparatorExceptions + 1, sizeof(LPWSTR), 5); - IniExitOnFailure(hr, "Failed to increase array size for value separator exceptions"); - dwInsertedIndex = pi->cValueSeparatorExceptions; - ++pi->cValueSeparatorExceptions; - - hr = StrAllocString(&pi->rgsczValueSeparatorExceptions[dwInsertedIndex], wzValueNamePrefix, 0); - IniExitOnFailure(hr, "Failed to copy value separator exception"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI IniSetCommentStyle( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzLinePrefix - ) -{ - HRESULT hr = S_OK; - - INI_STRUCT *pi = static_cast(piHandle); - - if (wzLinePrefix) - { - hr = StrAllocString(&pi->sczCommentLinePrefix, wzLinePrefix, 0); - IniExitOnFailure(hr, "Failed to copy comment line prefix to ini struct: %ls", wzLinePrefix); - } - else - { - ReleaseNullStr(pi->sczCommentLinePrefix); - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI IniParse( - __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in LPCWSTR wzPath, - __out_opt FILE_ENCODING *pfeEncodingFound - ) -{ - HRESULT hr = S_OK; - DWORD dwValuePrefixLength = 0; - DWORD dwValueSeparatorExceptionLength = 0; - LPWSTR sczContents = NULL; - LPWSTR sczCurrentSection = NULL; - LPWSTR sczName = NULL; - LPWSTR sczNameTrimmed = NULL; - LPWSTR sczValue = NULL; - LPWSTR sczValueTrimmed = NULL; - LPWSTR wzOpenTagPrefix = NULL; - LPWSTR wzOpenTagPostfix = NULL; - LPWSTR wzValuePrefix = NULL; - LPWSTR wzValueNameStart = NULL; - LPWSTR wzValueSeparator = NULL; - LPWSTR wzCommentLinePrefix = NULL; - LPWSTR wzValueBegin = NULL; - LPCWSTR wzTemp = NULL; - - INI_STRUCT *pi = static_cast(piHandle); - - BOOL fSections = (NULL != pi->sczOpenTagPrefix) && (NULL != pi->sczOpenTagPostfix); - BOOL fValuePrefix = (NULL != pi->sczValuePrefix); - - hr = StrAllocString(&pi->sczPath, wzPath, 0); - IniExitOnFailure(hr, "Failed to copy path to ini struct: %ls", wzPath); - - hr = FileToString(pi->sczPath, &sczContents, &pi->feEncoding); - IniExitOnFailure(hr, "Failed to convert file to string: %ls", pi->sczPath); - - if (pfeEncodingFound) - { - *pfeEncodingFound = pi->feEncoding; - } - - if (!sczContents || !*sczContents) - { - // Empty string, nothing to parse - ExitFunction1(hr = S_OK); - } - - dwValuePrefixLength = lstrlenW(pi->sczValuePrefix); - hr = StrSplitAllocArray(&pi->rgsczLines, reinterpret_cast(&pi->cLines), sczContents, L"\n"); - IniExitOnFailure(hr, "Failed to split INI file into lines"); - - for (DWORD i = 0; i < pi->cLines; ++i) - { - if (!*pi->rgsczLines[i] || '\r' == *pi->rgsczLines[i]) - { - continue; - } - - if (pi->sczCommentLinePrefix) - { - wzCommentLinePrefix = wcsstr(pi->rgsczLines[i], pi->sczCommentLinePrefix); - - if (wzCommentLinePrefix && wzCommentLinePrefix <= pi->rgsczLines[i] + 1) - { - continue; - } - } - - if (pi->sczOpenTagPrefix) - { - wzOpenTagPrefix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPrefix); - if (wzOpenTagPrefix) - { - // If there is an open tag prefix but there is anything but whitespace before it, then it's NOT an open tag prefix - // This is important, for example, to support values with names like "Array[0]=blah" in INI format - for (wzTemp = pi->rgsczLines[i]; wzTemp < wzOpenTagPrefix; ++wzTemp) - { - if (*wzTemp != L' ' && *wzTemp != L'\t') - { - wzOpenTagPrefix = NULL; - break; - } - } - } - } - - if (pi->sczOpenTagPostfix) - { - wzOpenTagPostfix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPostfix); - } - - if (pi->sczValuePrefix) - { - wzValuePrefix = wcsstr(pi->rgsczLines[i], pi->sczValuePrefix); - if (wzValuePrefix != NULL) - { - wzValueNameStart = wzValuePrefix + dwValuePrefixLength; - } - } - else - { - wzValueNameStart = pi->rgsczLines[i]; - } - - if (pi->sczValueSeparator && NULL != wzValueNameStart && *wzValueNameStart != L'\0') - { - dwValueSeparatorExceptionLength = 0; - for (DWORD j = 0; j < pi->cValueSeparatorExceptions; ++j) - { - if (pi->rgsczLines[i] == wcsstr(pi->rgsczLines[i], pi->rgsczValueSeparatorExceptions[j])) - { - dwValueSeparatorExceptionLength = lstrlenW(pi->rgsczValueSeparatorExceptions[j]); - break; - } - } - - wzValueSeparator = wcsstr(wzValueNameStart + dwValueSeparatorExceptionLength, pi->sczValueSeparator); - } - - // Don't keep the endline - if (pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] == L'\r') - { - pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] = L'\0'; - } - - if (fSections && wzOpenTagPrefix && wzOpenTagPostfix && wzOpenTagPrefix < wzOpenTagPostfix && (NULL == wzCommentLinePrefix || wzOpenTagPrefix < wzCommentLinePrefix)) - { - // There is an section starting here, let's keep track of it and move on - hr = StrAllocString(&sczCurrentSection, wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix), wzOpenTagPostfix - (wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix))); - IniExitOnFailure(hr, "Failed to record section name for line: %ls of INI file: %ls", pi->rgsczLines[i], pi->sczPath); - - // Sections will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output - ReleaseNullStr(pi->rgsczLines[i]); - } - else if (wzValueSeparator && (NULL == wzCommentLinePrefix || wzValueSeparator < wzCommentLinePrefix) - && (!fValuePrefix || wzValuePrefix)) - { - if (fValuePrefix) - { - wzValueBegin = wzValuePrefix + lstrlenW(pi->sczValuePrefix); - } - else - { - wzValueBegin = pi->rgsczLines[i]; - } - - hr = MemEnsureArraySize(reinterpret_cast(&pi->rgivValues), pi->cValues + 1, sizeof(INI_VALUE), 100); - IniExitOnFailure(hr, "Failed to increase array size for value array"); - - if (sczCurrentSection) - { - hr = StrAllocString(&sczName, sczCurrentSection, 0); - IniExitOnFailure(hr, "Failed to copy current section name"); - - hr = StrAllocConcat(&sczName, wzSectionSeparator, 0); - IniExitOnFailure(hr, "Failed to copy current section name"); - } - - hr = StrAllocConcat(&sczName, wzValueBegin, wzValueSeparator - wzValueBegin); - IniExitOnFailure(hr, "Failed to copy name"); - - hr = StrAllocString(&sczValue, wzValueSeparator + lstrlenW(pi->sczValueSeparator), 0); - IniExitOnFailure(hr, "Failed to copy value"); - - hr = StrTrimWhitespace(&sczNameTrimmed, sczName); - IniExitOnFailure(hr, "Failed to trim whitespace from name"); - - hr = StrTrimWhitespace(&sczValueTrimmed, sczValue); - IniExitOnFailure(hr, "Failed to trim whitespace from value"); - - pi->rgivValues[pi->cValues].wzName = const_cast(sczNameTrimmed); - sczNameTrimmed = NULL; - pi->rgivValues[pi->cValues].wzValue = const_cast(sczValueTrimmed); - sczValueTrimmed = NULL; - pi->rgivValues[pi->cValues].dwLineNumber = i + 1; - - ++pi->cValues; - - // Values will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output - ReleaseNullStr(pi->rgsczLines[i]); - } - else - { - // Must be a comment, so ignore it and keep it in the list to output - } - - ReleaseNullStr(sczName); - } - -LExit: - ReleaseStr(sczCurrentSection); - ReleaseStr(sczContents); - ReleaseStr(sczName); - ReleaseStr(sczNameTrimmed); - ReleaseStr(sczValue); - ReleaseStr(sczValueTrimmed); - - return hr; -} - -extern "C" HRESULT DAPI IniGetValueList( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues, - __out DWORD *pcValues - ) -{ - HRESULT hr = S_OK; - - INI_STRUCT *pi = static_cast(piHandle); - - *prgivValues = pi->rgivValues; - *pcValues = pi->cValues; - - return hr; -} - -extern "C" HRESULT DAPI IniGetValue( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in LPCWSTR wzValueName, - __deref_out_z LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - - INI_STRUCT *pi = static_cast(piHandle); - INI_VALUE *pValue = NULL; - - for (DWORD i = 0; i < pi->cValues; ++i) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1)) - { - pValue = pi->rgivValues + i; - break; - } - } - - if (NULL == pValue) - { - hr = E_NOTFOUND; - IniExitOnFailure(hr, "Failed to check for INI value: %ls", wzValueName); - } - - if (NULL == pValue->wzValue) - { - ExitFunction1(hr = E_NOTFOUND); - } - - hr = StrAllocString(psczValue, pValue->wzValue, 0); - IniExitOnFailure(hr, "Failed to make copy of value while looking up INI value named: %ls", wzValueName); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI IniSetValue( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in LPCWSTR wzValueName, - __in_z_opt LPCWSTR wzValue - ) -{ - HRESULT hr = S_OK; - LPWSTR sczSectionPrefix = NULL; // includes section name and backslash - LPWSTR sczName = NULL; - LPWSTR sczValue = NULL; - DWORD dwInsertIndex = DWORD_MAX; - - INI_STRUCT *pi = static_cast(piHandle); - INI_VALUE *pValue = NULL; - - for (DWORD i = 0; i < pi->cValues; ++i) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1)) - { - pValue = pi->rgivValues + i; - break; - } - } - - // We're killing the value - if (NULL == wzValue) - { - if (pValue && pValue->wzValue) - { - pi->fModified = TRUE; - sczValue = const_cast(pValue->wzValue); - pValue->wzValue = NULL; - ReleaseNullStr(sczValue); - } - - ExitFunction(); - } - else - { - if (pValue) - { - if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, pValue->wzValue, -1, wzValue, -1)) - { - pi->fModified = TRUE; - hr = StrAllocString(const_cast(&pValue->wzValue), wzValue, 0); - IniExitOnFailure(hr, "Failed to update value INI value named: %ls", wzValueName); - } - - ExitFunction1(hr = S_OK); - } - else - { - if (wzValueName) - { - hr = GetSectionPrefixFromName(wzValueName, &sczSectionPrefix); - IniExitOnFailure(hr, "Failed to get section prefix from value name: %ls", wzValueName); - } - - // If we have a section prefix, figure out the index to insert it (at the end of the section it belongs in) - if (sczSectionPrefix) - { - for (DWORD i = 0; i < pi->cValues; ++i) - { - if (0 == wcsncmp(pi->rgivValues[i].wzName, sczSectionPrefix, lstrlenW(sczSectionPrefix))) - { - dwInsertIndex = i; - } - else if (DWORD_MAX != dwInsertIndex) - { - break; - } - } - } - else - { - for (DWORD i = 0; i < pi->cValues; ++i) - { - if (NULL == wcsstr(pi->rgivValues[i].wzName, wzSectionSeparator)) - { - dwInsertIndex = i; - } - else if (DWORD_MAX != dwInsertIndex) - { - break; - } - } - } - - // Otherwise, just add it to the end - if (DWORD_MAX == dwInsertIndex) - { - dwInsertIndex = pi->cValues; - } - - pi->fModified = TRUE; - hr = MemInsertIntoArray(reinterpret_cast(&pi->rgivValues), dwInsertIndex, 1, pi->cValues + 1, sizeof(INI_VALUE), 100); - IniExitOnFailure(hr, "Failed to insert value into array"); - - hr = StrAllocString(&sczName, wzValueName, 0); - IniExitOnFailure(hr, "Failed to copy name"); - - hr = StrAllocString(&sczValue, wzValue, 0); - IniExitOnFailure(hr, "Failed to copy value"); - - pi->rgivValues[dwInsertIndex].wzName = const_cast(sczName); - sczName = NULL; - pi->rgivValues[dwInsertIndex].wzValue = const_cast(sczValue); - sczValue = NULL; - - ++pi->cValues; - } - } - -LExit: - ReleaseStr(sczName); - ReleaseStr(sczValue); - - return hr; -} - -extern "C" HRESULT DAPI IniWriteFile( - __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, - __in_z_opt LPCWSTR wzPath, - __in FILE_ENCODING feOverrideEncoding - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCurrentSectionPrefix = NULL; - LPWSTR sczNewSectionPrefix = NULL; - LPWSTR sczContents = NULL; - LPCWSTR wzName = NULL; - DWORD dwLineArrayIndex = 1; - FILE_ENCODING feEncoding; - - INI_STRUCT *pi = static_cast(piHandle); - - if (FILE_ENCODING_UNSPECIFIED == feOverrideEncoding) - { - feEncoding = pi->feEncoding; - } - else - { - feEncoding = feOverrideEncoding; - } - - if (FILE_ENCODING_UNSPECIFIED == feEncoding) - { - feEncoding = FILE_ENCODING_UTF16_WITH_BOM; - } - - if (!pi->fModified) - { - ExitFunction1(hr = S_OK); - } - if (NULL == wzPath && NULL == pi->sczPath) - { - ExitFunction1(hr = E_NOTFOUND); - } - - BOOL fSections = (pi->sczOpenTagPrefix) && (pi->sczOpenTagPostfix); - - hr = StrAllocString(&sczContents, L"", 0); - IniExitOnFailure(hr, "Failed to begin contents string as empty string"); - - // Insert any beginning lines we didn't understand like comments - if (0 < pi->cLines) - { - while (pi->rgsczLines[dwLineArrayIndex]) - { - hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex], 0); - IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); - - hr = StrAllocConcat(&sczContents, L"\r\n", 2); - IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); - - ++dwLineArrayIndex; - } - } - - for (DWORD i = 0; i < pi->cValues; ++i) - { - // Skip if this value was killed off - if (NULL == pi->rgivValues[i].wzValue) - { - continue; - } - - // Now generate any lines for the current value like value line and maybe also a new section line before it - - // First see if we need to write a section line - hr = GetSectionPrefixFromName(pi->rgivValues[i].wzName, &sczNewSectionPrefix); - IniExitOnFailure(hr, "Failed to get section prefix from name: %ls", pi->rgivValues[i].wzName); - - // If the new section prefix is different, write a section out for it - if (fSections && sczNewSectionPrefix && (NULL == sczCurrentSectionPrefix || CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczNewSectionPrefix, -1, sczCurrentSectionPrefix, -1))) - { - hr = StrAllocConcat(&sczContents, pi->sczOpenTagPrefix, 0); - IniExitOnFailure(hr, "Failed to concat open tag prefix to string"); - - // Exclude section separator (i.e. backslash) from new section prefix - hr = StrAllocConcat(&sczContents, sczNewSectionPrefix, lstrlenW(sczNewSectionPrefix)-lstrlenW(wzSectionSeparator)); - IniExitOnFailure(hr, "Failed to concat section name to string"); - - hr = StrAllocConcat(&sczContents, pi->sczOpenTagPostfix, 0); - IniExitOnFailure(hr, "Failed to concat open tag postfix to string"); - - hr = StrAllocConcat(&sczContents, L"\r\n", 2); - IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); - - ReleaseNullStr(sczCurrentSectionPrefix); - sczCurrentSectionPrefix = sczNewSectionPrefix; - sczNewSectionPrefix = NULL; - } - - // Inserting lines we read before the current value if appropriate - while (pi->rgivValues[i].dwLineNumber > dwLineArrayIndex) - { - // Skip any lines were purposely forgot - if (NULL == pi->rgsczLines[dwLineArrayIndex]) - { - ++dwLineArrayIndex; - continue; - } - - hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex++], 0); - IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); - - hr = StrAllocConcat(&sczContents, L"\r\n", 2); - IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); - } - - wzName = pi->rgivValues[i].wzName; - if (fSections) - { - wzName += lstrlenW(sczCurrentSectionPrefix); - } - - // OK, now just write the name/value pair, if it isn't deleted - if (pi->sczValuePrefix) - { - hr = StrAllocConcat(&sczContents, pi->sczValuePrefix, 0); - IniExitOnFailure(hr, "Failed to concat value prefix to ini output buffer"); - } - - hr = StrAllocConcat(&sczContents, wzName, 0); - IniExitOnFailure(hr, "Failed to concat value name to ini output buffer"); - - hr = StrAllocConcat(&sczContents, pi->sczValueSeparator, 0); - IniExitOnFailure(hr, "Failed to concat value separator to ini output buffer"); - - hr = StrAllocConcat(&sczContents, pi->rgivValues[i].wzValue, 0); - IniExitOnFailure(hr, "Failed to concat value to ini output buffer"); - - hr = StrAllocConcat(&sczContents, L"\r\n", 2); - IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); - } - - // If no path was specified, use the path to the file we parsed - if (NULL == wzPath) - { - wzPath = pi->sczPath; - } - - hr = FileFromString(wzPath, 0, sczContents, feEncoding); - IniExitOnFailure(hr, "Failed to write INI contents out to file: %ls", wzPath); - -LExit: - ReleaseStr(sczContents); - ReleaseStr(sczCurrentSectionPrefix); - ReleaseStr(sczNewSectionPrefix); - - return hr; -} - -static void UninitializeIniValue( - INI_VALUE *pivValue - ) -{ - ReleaseStr(const_cast(pivValue->wzName)); - ReleaseStr(const_cast(pivValue->wzValue)); -} - -static HRESULT GetSectionPrefixFromName( - __in_z LPCWSTR wzName, - __deref_inout_z LPWSTR* psczOutput - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzSectionDelimiter = NULL; - - ReleaseNullStr(*psczOutput); - - wzSectionDelimiter = wcsstr(wzName, wzSectionSeparator); - if (wzSectionDelimiter && wzSectionDelimiter != wzName) - { - hr = StrAllocString(psczOutput, wzName, wzSectionDelimiter - wzName + 1); - IniExitOnFailure(hr, "Failed to copy section prefix"); - } - -LExit: - return hr; -} diff --git a/src/dutil/jsonutil.cpp b/src/dutil/jsonutil.cpp deleted file mode 100644 index 3450ba59..00000000 --- a/src/dutil/jsonutil.cpp +++ /dev/null @@ -1,687 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define JsonExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) -#define JsonExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) -#define JsonExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) -#define JsonExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) -#define JsonExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) -#define JsonExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) -#define JsonExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__) -#define JsonExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__) -#define JsonExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__) -#define JsonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__) -#define JsonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_JSONUTIL, e, x, s, __VA_ARGS__) -#define JsonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_JSONUTIL, g, x, s, __VA_ARGS__) - -const DWORD JSON_STACK_INCREMENT = 5; - -// Prototypes -static HRESULT DoStart( - __in JSON_WRITER* pWriter, - __in JSON_TOKEN tokenStart, - __in_z LPCWSTR wzStartString - ); -static HRESULT DoEnd( - __in JSON_WRITER* pWriter, - __in JSON_TOKEN tokenEnd, - __in_z LPCWSTR wzEndString - ); -static HRESULT DoKey( - __in JSON_WRITER* pWriter, - __in_z LPCWSTR wzKey - ); -static HRESULT DoValue( - __in JSON_WRITER* pWriter, - __in_z_opt LPCWSTR wzValue - ); -static HRESULT EnsureTokenStack( - __in JSON_WRITER* pWriter - ); -static HRESULT SerializeJsonString( - __out_z LPWSTR* psczJsonString, - __in_z LPCWSTR wzString - ); - - - -DAPI_(HRESULT) JsonInitializeReader( - __in_z LPCWSTR wzJson, - __in JSON_READER* pReader - ) -{ - HRESULT hr = S_OK; - - memset(pReader, 0, sizeof(JSON_READER)); - ::InitializeCriticalSection(&pReader->cs); - - hr = StrAllocString(&pReader->sczJson, wzJson, 0); - JsonExitOnFailure(hr, "Failed to allocate json string."); - - pReader->pwz = pReader->sczJson; - -LExit: - return hr; -} - - -DAPI_(void) JsonUninitializeReader( - __in JSON_READER* pReader - ) -{ - ReleaseStr(pReader->sczJson); - - ::DeleteCriticalSection(&pReader->cs); - memset(pReader, 0, sizeof(JSON_READER)); -} - - -static HRESULT NextToken( - __in JSON_READER* pReader, - __out JSON_TOKEN* pToken - ) -{ - HRESULT hr = S_OK; - - // Skip whitespace. - while (L' ' == *pReader->pwz || - L'\t' == *pReader->pwz || - L'\r' == *pReader->pwz || - L'\n' == *pReader->pwz || - L',' == *pReader->pwz) - { - ++pReader->pwz; - } - - switch (*pReader->pwz) - { - case L'{': - *pToken = JSON_TOKEN_OBJECT_START; - break; - - case L':': // begin object value - *pToken = JSON_TOKEN_OBJECT_VALUE; - break; - - case L'}': // end object - *pToken = JSON_TOKEN_OBJECT_END; - break; - - case L'[': // begin array - *pToken = JSON_TOKEN_ARRAY_START; - break; - - case L']': // end array - *pToken = JSON_TOKEN_ARRAY_END; - break; - - case L'"': // string - *pToken = JSON_TOKEN_OBJECT_START == *pToken ? JSON_TOKEN_VALUE : JSON_TOKEN_OBJECT_KEY; - break; - - case L't': // true - case L'f': // false - case L'n': // null - case L'-': // number - case L'0': - case L'1': - case L'2': - case L'3': - case L'4': - case L'5': - case L'6': - case L'7': - case L'8': - case L'9': - *pToken = JSON_TOKEN_VALUE; - break; - - case L'\0': - hr = E_NOMOREITEMS; - break; - - default: - hr = E_INVALIDSTATE; - } - - return hr; -} - - -DAPI_(HRESULT) JsonReadNext( - __in JSON_READER* pReader, - __out JSON_TOKEN* pToken, - __out JSON_VALUE* pValue - ) -{ - HRESULT hr = S_OK; - //WCHAR wz; - //JSON_TOKEN token = JSON_TOKEN_NONE; - - ::EnterCriticalSection(&pReader->cs); - - hr = NextToken(pReader, pToken); - if (E_NOMOREITEMS == hr) - { - ExitFunction(); - } - JsonExitOnFailure(hr, "Failed to get next token."); - - if (JSON_TOKEN_VALUE == *pToken) - { - hr = JsonReadValue(pReader, pValue); - } - else - { - ++pReader->pwz; - } - -LExit: - ::LeaveCriticalSection(&pReader->cs); - return hr; -} - - -DAPI_(HRESULT) JsonReadValue( - __in JSON_READER* /*pReader*/, - __in JSON_VALUE* /*pValue*/ - ) -{ - HRESULT hr = S_OK; - -//LExit: - return hr; -} - - -DAPI_(HRESULT) JsonInitializeWriter( - __in JSON_WRITER* pWriter - ) -{ - memset(pWriter, 0, sizeof(JSON_WRITER)); - ::InitializeCriticalSection(&pWriter->cs); - - return S_OK; -} - - -DAPI_(void) JsonUninitializeWriter( - __in JSON_WRITER* pWriter - ) -{ - ReleaseMem(pWriter->rgTokenStack); - ReleaseStr(pWriter->sczJson); - - ::DeleteCriticalSection(&pWriter->cs); - memset(pWriter, 0, sizeof(JSON_WRITER)); -} - - -DAPI_(HRESULT) JsonWriteBool( - __in JSON_WRITER* pWriter, - __in BOOL fValue - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - hr = StrAllocString(&sczValue, fValue ? L"true" : L"false", 0); - JsonExitOnFailure(hr, "Failed to convert boolean to string."); - - hr = DoValue(pWriter, sczValue); - JsonExitOnFailure(hr, "Failed to add boolean to JSON."); - -LExit: - ReleaseStr(sczValue); - return hr; -} - - -DAPI_(HRESULT) JsonWriteNumber( - __in JSON_WRITER* pWriter, - __in DWORD dwValue - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - hr = StrAllocFormatted(&sczValue, L"%u", dwValue); - JsonExitOnFailure(hr, "Failed to convert number to string."); - - hr = DoValue(pWriter, sczValue); - JsonExitOnFailure(hr, "Failed to add number to JSON."); - -LExit: - ReleaseStr(sczValue); - return hr; -} - - -DAPI_(HRESULT) JsonWriteString( - __in JSON_WRITER* pWriter, - __in_z LPCWSTR wzValue - ) -{ - HRESULT hr = S_OK; - LPWSTR sczJsonString = NULL; - - hr = SerializeJsonString(&sczJsonString, wzValue); - JsonExitOnFailure(hr, "Failed to allocate string JSON."); - - hr = DoValue(pWriter, sczJsonString); - JsonExitOnFailure(hr, "Failed to add string to JSON."); - -LExit: - ReleaseStr(sczJsonString); - return hr; -} - - -DAPI_(HRESULT) JsonWriteArrayStart( - __in JSON_WRITER* pWriter - ) -{ - HRESULT hr = S_OK; - - hr = DoStart(pWriter, JSON_TOKEN_ARRAY_START, L"["); - JsonExitOnFailure(hr, "Failed to start JSON array."); - -LExit: - return hr; -} - - -DAPI_(HRESULT) JsonWriteArrayEnd( - __in JSON_WRITER* pWriter - ) -{ - HRESULT hr = S_OK; - - hr = DoEnd(pWriter, JSON_TOKEN_ARRAY_END, L"]"); - JsonExitOnFailure(hr, "Failed to end JSON array."); - -LExit: - return hr; -} - - -DAPI_(HRESULT) JsonWriteObjectStart( - __in JSON_WRITER* pWriter - ) -{ - HRESULT hr = S_OK; - - hr = DoStart(pWriter, JSON_TOKEN_OBJECT_START, L"{"); - JsonExitOnFailure(hr, "Failed to start JSON object."); - -LExit: - return hr; -} - - -DAPI_(HRESULT) JsonWriteObjectKey( - __in JSON_WRITER* pWriter, - __in_z LPCWSTR wzKey - ) -{ - HRESULT hr = S_OK; - LPWSTR sczObjectKey = NULL; - - hr = StrAllocFormatted(&sczObjectKey, L"\"%ls\":", wzKey); - JsonExitOnFailure(hr, "Failed to allocate JSON object key."); - - hr = DoKey(pWriter, sczObjectKey); - JsonExitOnFailure(hr, "Failed to add object key to JSON."); - -LExit: - ReleaseStr(sczObjectKey); - return hr; -} - - -DAPI_(HRESULT) JsonWriteObjectEnd( - __in JSON_WRITER* pWriter - ) -{ - HRESULT hr = S_OK; - - hr = DoEnd(pWriter, JSON_TOKEN_OBJECT_END, L"}"); - JsonExitOnFailure(hr, "Failed to end JSON object."); - -LExit: - return hr; -} - - -static HRESULT DoStart( - __in JSON_WRITER* pWriter, - __in JSON_TOKEN tokenStart, - __in_z LPCWSTR wzStartString - ) -{ - Assert(JSON_TOKEN_ARRAY_START == tokenStart || JSON_TOKEN_OBJECT_START == tokenStart); - - HRESULT hr = S_OK; - JSON_TOKEN token = JSON_TOKEN_NONE; - BOOL fNeedComma = FALSE; - BOOL fPushToken = TRUE; - - ::EnterCriticalSection(&pWriter->cs); - - hr = EnsureTokenStack(pWriter); - JsonExitOnFailure(hr, "Failed to ensure token stack for start."); - - token = pWriter->rgTokenStack[pWriter->cTokens - 1]; - switch (token) - { - case JSON_TOKEN_NONE: - token = tokenStart; - fPushToken = FALSE; - break; - - case JSON_TOKEN_ARRAY_START: // array start changes to array value. - token = JSON_TOKEN_ARRAY_VALUE; - break; - - case JSON_TOKEN_ARRAY_VALUE: - case JSON_TOKEN_ARRAY_END: - case JSON_TOKEN_OBJECT_END: - fNeedComma = TRUE; - break; - - default: // everything else is not allowed. - hr = E_UNEXPECTED; - break; - } - JsonExitOnRootFailure(hr, "Cannot start array or object to JSON serializer now."); - - if (fNeedComma) - { - hr = StrAllocConcat(&pWriter->sczJson, L",", 0); - JsonExitOnFailure(hr, "Failed to add comma for start array or object to JSON."); - } - - hr = StrAllocConcat(&pWriter->sczJson, wzStartString, 0); - JsonExitOnFailure(hr, "Failed to start JSON array or object."); - - pWriter->rgTokenStack[pWriter->cTokens - 1] = token; - if (fPushToken) - { - pWriter->rgTokenStack[pWriter->cTokens] = tokenStart; - ++pWriter->cTokens; - } - -LExit: - ::LeaveCriticalSection(&pWriter->cs); - return hr; -} - - -static HRESULT DoEnd( - __in JSON_WRITER* pWriter, - __in JSON_TOKEN tokenEnd, - __in_z LPCWSTR wzEndString - ) -{ - HRESULT hr = S_OK; - - ::EnterCriticalSection(&pWriter->cs); - - if (!pWriter->rgTokenStack || 0 == pWriter->cTokens) - { - hr = E_UNEXPECTED; - JsonExitOnRootFailure(hr, "Failure to pop token because the stack is empty."); - } - else - { - JSON_TOKEN token = pWriter->rgTokenStack[pWriter->cTokens - 1]; - if ((JSON_TOKEN_ARRAY_END == tokenEnd && JSON_TOKEN_ARRAY_START != token && JSON_TOKEN_ARRAY_VALUE != token) || - (JSON_TOKEN_OBJECT_END == tokenEnd && JSON_TOKEN_OBJECT_START != token && JSON_TOKEN_OBJECT_VALUE != token)) - { - hr = E_UNEXPECTED; - JsonExitOnRootFailure(hr, "Failure to pop token because the stack did not match the expected token: %d", tokenEnd); - } - } - - hr = StrAllocConcat(&pWriter->sczJson, wzEndString, 0); - JsonExitOnFailure(hr, "Failed to end JSON array or object."); - - --pWriter->cTokens; - -LExit: - ::LeaveCriticalSection(&pWriter->cs); - return hr; -} - - -static HRESULT DoKey( - __in JSON_WRITER* pWriter, - __in_z LPCWSTR wzKey - ) -{ - HRESULT hr = S_OK; - JSON_TOKEN token = JSON_TOKEN_NONE; - BOOL fNeedComma = FALSE; - - ::EnterCriticalSection(&pWriter->cs); - - hr = EnsureTokenStack(pWriter); - JsonExitOnFailure(hr, "Failed to ensure token stack for key."); - - token = pWriter->rgTokenStack[pWriter->cTokens - 1]; - switch (token) - { - case JSON_TOKEN_OBJECT_START: - token = JSON_TOKEN_OBJECT_KEY; - break; - - case JSON_TOKEN_OBJECT_VALUE: - token = JSON_TOKEN_OBJECT_KEY; - fNeedComma = TRUE; - break; - - default: // everything else is not allowed. - hr = E_UNEXPECTED; - break; - } - JsonExitOnRootFailure(hr, "Cannot add key to JSON serializer now."); - - if (fNeedComma) - { - hr = StrAllocConcat(&pWriter->sczJson, L",", 0); - JsonExitOnFailure(hr, "Failed to add comma for key to JSON."); - } - - hr = StrAllocConcat(&pWriter->sczJson, wzKey, 0); - JsonExitOnFailure(hr, "Failed to add key to JSON."); - - pWriter->rgTokenStack[pWriter->cTokens - 1] = token; - -LExit: - ::LeaveCriticalSection(&pWriter->cs); - return hr; -} - - -static HRESULT DoValue( - __in JSON_WRITER* pWriter, - __in_z_opt LPCWSTR wzValue - ) -{ - HRESULT hr = S_OK; - JSON_TOKEN token = JSON_TOKEN_NONE; - BOOL fNeedComma = FALSE; - - ::EnterCriticalSection(&pWriter->cs); - - hr = EnsureTokenStack(pWriter); - JsonExitOnFailure(hr, "Failed to ensure token stack for value."); - - token = pWriter->rgTokenStack[pWriter->cTokens - 1]; - switch (token) - { - case JSON_TOKEN_ARRAY_START: - token = JSON_TOKEN_ARRAY_VALUE; - break; - - case JSON_TOKEN_ARRAY_VALUE: - fNeedComma = TRUE; - break; - - case JSON_TOKEN_OBJECT_KEY: - token = JSON_TOKEN_OBJECT_VALUE; - break; - - case JSON_TOKEN_NONE: - token = JSON_TOKEN_VALUE; - break; - - default: // everything else is not allowed. - hr = E_UNEXPECTED; - break; - } - JsonExitOnRootFailure(hr, "Cannot add value to JSON serializer now."); - - if (fNeedComma) - { - hr = StrAllocConcat(&pWriter->sczJson, L",", 0); - JsonExitOnFailure(hr, "Failed to add comma for value to JSON."); - } - - if (wzValue) - { - hr = StrAllocConcat(&pWriter->sczJson, wzValue, 0); - JsonExitOnFailure(hr, "Failed to add value to JSON."); - } - else - { - hr = StrAllocConcat(&pWriter->sczJson, L"null", 0); - JsonExitOnFailure(hr, "Failed to add null value to JSON."); - } - - pWriter->rgTokenStack[pWriter->cTokens - 1] = token; - -LExit: - ::LeaveCriticalSection(&pWriter->cs); - return hr; -} - - -static HRESULT EnsureTokenStack( - __in JSON_WRITER* pWriter - ) -{ - HRESULT hr = S_OK; - DWORD cNumAlloc = pWriter->cTokens != 0 ? pWriter->cTokens : 0; - - hr = MemEnsureArraySize(reinterpret_cast(&pWriter->rgTokenStack), cNumAlloc, sizeof(JSON_TOKEN), JSON_STACK_INCREMENT); - JsonExitOnFailure(hr, "Failed to allocate JSON token stack."); - - if (0 == pWriter->cTokens) - { - pWriter->rgTokenStack[0] = JSON_TOKEN_NONE; - ++pWriter->cTokens; - } - -LExit: - return hr; -} - - -static HRESULT SerializeJsonString( - __out_z LPWSTR* psczJsonString, - __in_z LPCWSTR wzString - ) -{ - HRESULT hr = S_OK; - DWORD cchRequired = 3; // start with enough space for null terminated empty quoted string (aka: ""\0) - - for (LPCWSTR pch = wzString; *pch; ++pch) - { - // If it is a special JSON character, add space for the escape backslash. - if (L'"' == *pch || L'\\' == *pch || L'/' == *pch || L'\b' == *pch || L'\f' == *pch || L'\n' == *pch || L'\r' == *pch || L'\t' == *pch) - { - ++cchRequired; - } - - ++cchRequired; - } - - hr = StrAlloc(psczJsonString, cchRequired); - JsonExitOnFailure(hr, "Failed to allocate space for JSON string."); - - LPWSTR pchTarget = *psczJsonString; - - *pchTarget = L'\"'; - ++pchTarget; - - for (LPCWSTR pch = wzString; *pch; ++pch, ++pchTarget) - { - // If it is a special JSON character, handle it or just add the character as is. - switch (*pch) - { - case L'"': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'"'; - break; - - case L'\\': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'\\'; - break; - - case L'/': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'/'; - break; - - case L'\b': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'b'; - break; - - case L'\f': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'f'; - break; - - case L'\n': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'n'; - break; - - case L'\r': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L'r'; - break; - - case L'\t': - *pchTarget = L'\\'; - ++pchTarget; - *pchTarget = L't'; - break; - - default: - *pchTarget = *pch; - break; - } - - } - - *pchTarget = L'\"'; - ++pchTarget; - *pchTarget = L'\0'; - -LExit: - return hr; -} diff --git a/src/dutil/locutil.cpp b/src/dutil/locutil.cpp deleted file mode 100644 index c4567c03..00000000 --- a/src/dutil/locutil.cpp +++ /dev/null @@ -1,628 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define LocExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) -#define LocExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) -#define LocExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) -#define LocExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) -#define LocExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) -#define LocExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) -#define LocExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_LOCUTIL, p, x, e, s, __VA_ARGS__) -#define LocExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, p, x, s, __VA_ARGS__) -#define LocExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_LOCUTIL, p, x, e, s, __VA_ARGS__) -#define LocExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, p, x, s, __VA_ARGS__) -#define LocExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_LOCUTIL, e, x, s, __VA_ARGS__) -#define LocExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_LOCUTIL, g, x, s, __VA_ARGS__) - -// prototypes -static HRESULT ParseWxl( - __in IXMLDOMDocument* pixd, - __out WIX_LOCALIZATION** ppWixLoc - ); -static HRESULT ParseWxlStrings( - __in IXMLDOMElement* pElement, - __in WIX_LOCALIZATION* pWixLoc - ); -static HRESULT ParseWxlControls( - __in IXMLDOMElement* pElement, - __in WIX_LOCALIZATION* pWixLoc - ); -static HRESULT ParseWxlString( - __in IXMLDOMNode* pixn, - __in DWORD dwIdx, - __in WIX_LOCALIZATION* pWixLoc - ); -static HRESULT ParseWxlControl( - __in IXMLDOMNode* pixn, - __in DWORD dwIdx, - __in WIX_LOCALIZATION* pWixLoc - ); - -// from Winnls.h -#ifndef MUI_LANGUAGE_ID -#define MUI_LANGUAGE_ID 0x4 // Use traditional language ID convention -#endif -#ifndef MUI_MERGE_USER_FALLBACK -#define MUI_MERGE_USER_FALLBACK 0x20 // GetThreadPreferredUILanguages merges in user preferred languages -#endif -#ifndef MUI_MERGE_SYSTEM_FALLBACK -#define MUI_MERGE_SYSTEM_FALLBACK 0x10 // GetThreadPreferredUILanguages merges in parent and base languages -#endif -typedef WINBASEAPI BOOL (WINAPI *GET_THREAD_PREFERRED_UI_LANGUAGES) ( - __in DWORD dwFlags, - __out PULONG pulNumLanguages, - __out_ecount_opt(*pcchLanguagesBuffer) PZZWSTR pwszLanguagesBuffer, - __inout PULONG pcchLanguagesBuffer -); - -extern "C" HRESULT DAPI LocProbeForFile( - __in_z LPCWSTR wzBasePath, - __in_z LPCWSTR wzLocFileName, - __in_z_opt LPCWSTR wzLanguage, - __inout LPWSTR* psczPath - ) -{ - HRESULT hr = S_OK; - LPWSTR sczProbePath = NULL; - LANGID langid = 0; - LPWSTR sczLangIdFile = NULL; - LPWSTR sczLangsBuff = NULL; - GET_THREAD_PREFERRED_UI_LANGUAGES pvfnGetThreadPreferredUILanguages = - reinterpret_cast( - GetProcAddress(GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages")); - - // If a language was specified, look for a loc file in that as a directory. - if (wzLanguage && *wzLanguage) - { - hr = PathConcat(wzBasePath, wzLanguage, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat base path to language."); - - hr = PathConcat(sczProbePath, wzLocFileName, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat loc file name to probe path."); - - if (FileExistsEx(sczProbePath, NULL)) - { - ExitFunction(); - } - } - - if (pvfnGetThreadPreferredUILanguages) - { - ULONG nLangs; - ULONG cchLangs = 0; - DWORD dwFlags = MUI_LANGUAGE_ID | MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK; - if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs)) - { - LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size."); - } - - hr = StrAlloc(&sczLangsBuff, cchLangs); - LocExitOnFailure(hr, "Failed to allocate buffer for languages"); - - nLangs = 0; - if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs)) - { - LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list."); - } - - LPWSTR szLangs = sczLangsBuff; - for (ULONG i = 0; i < nLangs; ++i, szLangs += 5) - { - // StrHexDecode assumes low byte is first. We'll need to swap the bytes once we parse out the value. - hr = StrHexDecode(szLangs, reinterpret_cast(&langid), sizeof(langid)); - LocExitOnFailure(hr, "Failed to parse langId."); - - langid = MAKEWORD(HIBYTE(langid), LOBYTE(langid)); - hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - LocExitOnFailure(hr, "Failed to format user preferred langid."); - - hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat user preferred langid file name to base path."); - - if (FileExistsEx(sczProbePath, NULL)) - { - ExitFunction(); - } - } - } - - langid = ::GetUserDefaultUILanguage(); - - hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - LocExitOnFailure(hr, "Failed to format user langid."); - - hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat user langid file name to base path."); - - if (FileExistsEx(sczProbePath, NULL)) - { - ExitFunction(); - } - - if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) - { - langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); - - hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - LocExitOnFailure(hr, "Failed to format user langid (default sublang)."); - - hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); - - if (FileExistsEx(sczProbePath, NULL)) - { - ExitFunction(); - } - } - - langid = ::GetSystemDefaultUILanguage(); - - hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - LocExitOnFailure(hr, "Failed to format system langid."); - - hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat system langid file name to base path."); - - if (FileExistsEx(sczProbePath, NULL)) - { - ExitFunction(); - } - - if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) - { - langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); - - hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); - LocExitOnFailure(hr, "Failed to format user langid (default sublang)."); - - hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); - - if (FileExistsEx(sczProbePath, NULL)) - { - ExitFunction(); - } - } - - // Finally, look for the loc file in the base path. - hr = PathConcat(wzBasePath, wzLocFileName, &sczProbePath); - LocExitOnFailure(hr, "Failed to concat loc file name to base path."); - - if (!FileExistsEx(sczProbePath, NULL)) - { - hr = E_FILENOTFOUND; - } - -LExit: - if (SUCCEEDED(hr)) - { - hr = StrAllocString(psczPath, sczProbePath, 0); - } - - ReleaseStr(sczLangIdFile); - ReleaseStr(sczProbePath); - ReleaseStr(sczLangsBuff); - - return hr; -} - -extern "C" HRESULT DAPI LocLoadFromFile( - __in_z LPCWSTR wzWxlFile, - __out WIX_LOCALIZATION** ppWixLoc - ) -{ - HRESULT hr = S_OK; - IXMLDOMDocument* pixd = NULL; - - hr = XmlLoadDocumentFromFile(wzWxlFile, &pixd); - LocExitOnFailure(hr, "Failed to load WXL file as XML document."); - - hr = ParseWxl(pixd, ppWixLoc); - LocExitOnFailure(hr, "Failed to parse WXL."); - -LExit: - ReleaseObject(pixd); - - return hr; -} - -extern "C" HRESULT DAPI LocLoadFromResource( - __in HMODULE hModule, - __in_z LPCSTR szResource, - __out WIX_LOCALIZATION** ppWixLoc - ) -{ - HRESULT hr = S_OK; - LPVOID pvResource = NULL; - DWORD cbResource = 0; - LPWSTR sczXml = NULL; - IXMLDOMDocument* pixd = NULL; - - hr = ResReadData(hModule, szResource, &pvResource, &cbResource); - LocExitOnFailure(hr, "Failed to read theme from resource."); - - hr = StrAllocStringAnsi(&sczXml, reinterpret_cast(pvResource), cbResource, CP_UTF8); - LocExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); - - hr = XmlLoadDocument(sczXml, &pixd); - LocExitOnFailure(hr, "Failed to load theme resource as XML document."); - - hr = ParseWxl(pixd, ppWixLoc); - LocExitOnFailure(hr, "Failed to parse WXL."); - -LExit: - ReleaseObject(pixd); - ReleaseStr(sczXml); - - return hr; -} - -extern "C" void DAPI LocFree( - __in_opt WIX_LOCALIZATION* pWixLoc - ) -{ - if (pWixLoc) - { - for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx) - { - ReleaseStr(pWixLoc->rgLocStrings[idx].wzId); - ReleaseStr(pWixLoc->rgLocStrings[idx].wzText); - } - - for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx) - { - ReleaseStr(pWixLoc->rgLocControls[idx].wzControl); - ReleaseStr(pWixLoc->rgLocControls[idx].wzText); - } - - ReleaseMem(pWixLoc->rgLocStrings); - ReleaseMem(pWixLoc->rgLocControls); - ReleaseMem(pWixLoc); - } -} - -extern "C" HRESULT DAPI LocLocalizeString( - __in const WIX_LOCALIZATION* pWixLoc, - __inout LPWSTR* ppsczInput - ) -{ - Assert(ppsczInput && pWixLoc); - HRESULT hr = S_OK; - - for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i) - { - hr = StrReplaceStringAll(ppsczInput, pWixLoc->rgLocStrings[i].wzId, pWixLoc->rgLocStrings[i].wzText); - LocExitOnFailure(hr, "Localizing string failed."); - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI LocGetControl( - __in const WIX_LOCALIZATION* pWixLoc, - __in_z LPCWSTR wzId, - __out LOC_CONTROL** ppLocControl - ) -{ - HRESULT hr = S_OK; - LOC_CONTROL* pLocControl = NULL; - - for (DWORD i = 0; i < pWixLoc->cLocControls; ++i) - { - pLocControl = &pWixLoc->rgLocControls[i]; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocControl->wzControl, -1, wzId, -1)) - { - *ppLocControl = pLocControl; - ExitFunction1(hr = S_OK); - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - -extern "C" HRESULT DAPI LocGetString( - __in const WIX_LOCALIZATION* pWixLoc, - __in_z LPCWSTR wzId, - __out LOC_STRING** ppLocString - ) -{ - HRESULT hr = E_NOTFOUND; - LOC_STRING* pLocString = NULL; - - for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i) - { - pLocString = pWixLoc->rgLocStrings + i; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocString->wzId, -1, wzId, -1)) - { - *ppLocString = pLocString; - hr = S_OK; - break; - } - } - - return hr; -} - -extern "C" HRESULT DAPI LocAddString( - __in WIX_LOCALIZATION* pWixLoc, - __in_z LPCWSTR wzId, - __in_z LPCWSTR wzLocString, - __in BOOL bOverridable - ) -{ - HRESULT hr = S_OK; - - ++pWixLoc->cLocStrings; - pWixLoc->rgLocStrings = static_cast(MemReAlloc(pWixLoc->rgLocStrings, sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE)); - LocExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to reallocate memory for localization strings."); - - LOC_STRING* pLocString = pWixLoc->rgLocStrings + (pWixLoc->cLocStrings - 1); - - hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", wzId); - LocExitOnFailure(hr, "Failed to set localization string Id."); - - hr = StrAllocString(&pLocString->wzText, wzLocString, 0); - LocExitOnFailure(hr, "Failed to set localization string Text."); - - pLocString->bOverridable = bOverridable; - -LExit: - return hr; -} - -// helper functions - -static HRESULT ParseWxl( - __in IXMLDOMDocument* pixd, - __out WIX_LOCALIZATION** ppWixLoc - ) -{ - HRESULT hr = S_OK; - IXMLDOMElement *pWxlElement = NULL; - WIX_LOCALIZATION* pWixLoc = NULL; - - pWixLoc = static_cast(MemAlloc(sizeof(WIX_LOCALIZATION), TRUE)); - LocExitOnNull(pWixLoc, hr, E_OUTOFMEMORY, "Failed to allocate memory for Wxl file."); - - // read the WixLocalization tag - hr = pixd->get_documentElement(&pWxlElement); - LocExitOnFailure(hr, "Failed to get localization element."); - - // get the Language attribute if present - pWixLoc->dwLangId = WIX_LOCALIZATION_LANGUAGE_NOT_SET; - hr = XmlGetAttributeNumber(pWxlElement, L"Language", &pWixLoc->dwLangId); - if (S_FALSE == hr) - { - hr = S_OK; - } - LocExitOnFailure(hr, "Failed to get Language value."); - - // store the strings and controls in a node list - hr = ParseWxlStrings(pWxlElement, pWixLoc); - LocExitOnFailure(hr, "Parsing localization strings failed."); - - hr = ParseWxlControls(pWxlElement, pWixLoc); - LocExitOnFailure(hr, "Parsing localization controls failed."); - - *ppWixLoc = pWixLoc; - pWixLoc = NULL; - -LExit: - ReleaseObject(pWxlElement); - ReleaseMem(pWixLoc); - - return hr; -} - - -static HRESULT ParseWxlStrings( - __in IXMLDOMElement* pElement, - __in WIX_LOCALIZATION* pWixLoc - ) -{ - HRESULT hr = S_OK; - IXMLDOMNode* pixn = NULL; - IXMLDOMNodeList* pixnl = NULL; - DWORD dwIdx = 0; - - hr = XmlSelectNodes(pElement, L"String", &pixnl); - LocExitOnLastError(hr, "Failed to get String child nodes of Wxl File."); - - hr = pixnl->get_length(reinterpret_cast(&pWixLoc->cLocStrings)); - LocExitOnLastError(hr, "Failed to get number of String child nodes in Wxl File."); - - if (0 < pWixLoc->cLocStrings) - { - pWixLoc->rgLocStrings = static_cast(MemAlloc(sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE)); - LocExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to allocate memory for localization strings."); - - while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) - { - hr = ParseWxlString(pixn, dwIdx, pWixLoc); - LocExitOnFailure(hr, "Failed to parse localization string."); - - ++dwIdx; - ReleaseNullObject(pixn); - } - - hr = S_OK; - LocExitOnFailure(hr, "Failed to enumerate all localization strings."); - } - -LExit: - if (FAILED(hr) && pWixLoc->rgLocStrings) - { - for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx) - { - ReleaseStr(pWixLoc->rgLocStrings[idx].wzId); - ReleaseStr(pWixLoc->rgLocStrings[idx].wzText); - } - - ReleaseMem(pWixLoc->rgLocStrings); - } - - ReleaseObject(pixn); - ReleaseObject(pixnl); - - return hr; -} - -static HRESULT ParseWxlControls( - __in IXMLDOMElement* pElement, - __in WIX_LOCALIZATION* pWixLoc - ) -{ - HRESULT hr = S_OK; - IXMLDOMNode* pixn = NULL; - IXMLDOMNodeList* pixnl = NULL; - DWORD dwIdx = 0; - - hr = XmlSelectNodes(pElement, L"UI|Control", &pixnl); - LocExitOnLastError(hr, "Failed to get UI child nodes of Wxl File."); - - hr = pixnl->get_length(reinterpret_cast(&pWixLoc->cLocControls)); - LocExitOnLastError(hr, "Failed to get number of UI child nodes in Wxl File."); - - if (0 < pWixLoc->cLocControls) - { - pWixLoc->rgLocControls = static_cast(MemAlloc(sizeof(LOC_CONTROL) * pWixLoc->cLocControls, TRUE)); - LocExitOnNull(pWixLoc->rgLocControls, hr, E_OUTOFMEMORY, "Failed to allocate memory for localized controls."); - - while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) - { - hr = ParseWxlControl(pixn, dwIdx, pWixLoc); - LocExitOnFailure(hr, "Failed to parse localized control."); - - ++dwIdx; - ReleaseNullObject(pixn); - } - - hr = S_OK; - LocExitOnFailure(hr, "Failed to enumerate all localized controls."); - } - -LExit: - if (FAILED(hr) && pWixLoc->rgLocControls) - { - for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx) - { - ReleaseStr(pWixLoc->rgLocControls[idx].wzControl); - ReleaseStr(pWixLoc->rgLocControls[idx].wzText); - } - - ReleaseMem(pWixLoc->rgLocControls); - } - - ReleaseObject(pixn); - ReleaseObject(pixnl); - - return hr; -} - -static HRESULT ParseWxlString( - __in IXMLDOMNode* pixn, - __in DWORD dwIdx, - __in WIX_LOCALIZATION* pWixLoc - ) -{ - HRESULT hr = S_OK; - LOC_STRING* pLocString = NULL; - BSTR bstrText = NULL; - - pLocString = pWixLoc->rgLocStrings + dwIdx; - - // Id - hr = XmlGetAttribute(pixn, L"Id", &bstrText); - LocExitOnFailure(hr, "Failed to get Xml attribute Id in Wxl file."); - - hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", bstrText); - LocExitOnFailure(hr, "Failed to duplicate Xml attribute Id in Wxl file."); - - ReleaseNullBSTR(bstrText); - - // Overrideable - hr = XmlGetAttribute(pixn, L"Overridable", &bstrText); - LocExitOnFailure(hr, "Failed to get Xml attribute Overridable."); - - if (S_OK == hr) - { - pLocString->bOverridable = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrText, -1, L"yes", -1); - } - - ReleaseNullBSTR(bstrText); - - // Text - hr = XmlGetText(pixn, &bstrText); - LocExitOnFailure(hr, "Failed to get Xml text in Wxl file."); - - hr = StrAllocString(&pLocString->wzText, bstrText, 0); - LocExitOnFailure(hr, "Failed to duplicate Xml text in Wxl file."); - -LExit: - ReleaseBSTR(bstrText); - - return hr; -} - -static HRESULT ParseWxlControl( - __in IXMLDOMNode* pixn, - __in DWORD dwIdx, - __in WIX_LOCALIZATION* pWixLoc - ) -{ - HRESULT hr = S_OK; - LOC_CONTROL* pLocControl = NULL; - BSTR bstrText = NULL; - - pLocControl = pWixLoc->rgLocControls + dwIdx; - - // Id - hr = XmlGetAttribute(pixn, L"Control", &bstrText); - LocExitOnFailure(hr, "Failed to get Xml attribute Control in Wxl file."); - - hr = StrAllocString(&pLocControl->wzControl, bstrText, 0); - LocExitOnFailure(hr, "Failed to duplicate Xml attribute Control in Wxl file."); - - ReleaseNullBSTR(bstrText); - - // X - pLocControl->nX = LOC_CONTROL_NOT_SET; - hr = XmlGetAttributeNumber(pixn, L"X", reinterpret_cast(&pLocControl->nX)); - LocExitOnFailure(hr, "Failed to get control X attribute."); - - // Y - pLocControl->nY = LOC_CONTROL_NOT_SET; - hr = XmlGetAttributeNumber(pixn, L"Y", reinterpret_cast(&pLocControl->nY)); - LocExitOnFailure(hr, "Failed to get control Y attribute."); - - // Width - pLocControl->nWidth = LOC_CONTROL_NOT_SET; - hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast(&pLocControl->nWidth)); - LocExitOnFailure(hr, "Failed to get control width attribute."); - - // Height - pLocControl->nHeight = LOC_CONTROL_NOT_SET; - hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pLocControl->nHeight)); - LocExitOnFailure(hr, "Failed to get control height attribute."); - - // Text - hr = XmlGetText(pixn, &bstrText); - LocExitOnFailure(hr, "Failed to get control text in Wxl file."); - - hr = StrAllocString(&pLocControl->wzText, bstrText, 0); - LocExitOnFailure(hr, "Failed to duplicate control text in Wxl file."); - -LExit: - ReleaseBSTR(bstrText); - - return hr; -} diff --git a/src/dutil/logutil.cpp b/src/dutil/logutil.cpp deleted file mode 100644 index ac68036a..00000000 --- a/src/dutil/logutil.cpp +++ /dev/null @@ -1,961 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define LoguExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) -#define LoguExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) -#define LoguExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) -#define LoguExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) -#define LoguExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) -#define LoguExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) -#define LoguExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__) -#define LoguExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__) -#define LoguExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__) -#define LoguExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__) -#define LoguExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_LOGUTIL, e, x, s, __VA_ARGS__) -#define LoguExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_LOGUTIL, g, x, s, __VA_ARGS__) - -// globals -static HMODULE LogUtil_hModule = NULL; -static BOOL LogUtil_fDisabled = FALSE; -static HANDLE LogUtil_hLog = INVALID_HANDLE_VALUE; -static LPWSTR LogUtil_sczLogPath = NULL; -static LPSTR LogUtil_sczPreInitBuffer = NULL; -static REPORT_LEVEL LogUtil_rlCurrent = REPORT_STANDARD; -static CRITICAL_SECTION LogUtil_csLog = { }; -static BOOL LogUtil_fInitializedCriticalSection = FALSE; - -// Customization of certain parts of the string, within a line -static LPWSTR LogUtil_sczSpecialBeginLine = NULL; -static LPWSTR LogUtil_sczSpecialEndLine = NULL; -static LPWSTR LogUtil_sczSpecialAfterTimeStamp = NULL; - -static LPCSTR LOGUTIL_UNKNOWN = "unknown"; -static LPCSTR LOGUTIL_WARNING = "warning"; -static LPCSTR LOGUTIL_STANDARD = "standard"; -static LPCSTR LOGUTIL_VERBOSE = "verbose"; -static LPCSTR LOGUTIL_DEBUG = "debug"; -static LPCSTR LOGUTIL_NONE = "none"; - -// prototypes -static HRESULT LogIdWork( - __in REPORT_LEVEL rl, - __in_opt HMODULE hModule, - __in DWORD dwLogId, - __in va_list args, - __in BOOL fLOGUTIL_NEWLINE - ); -static HRESULT LogStringWorkArgs( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - __in va_list args, - __in BOOL fLOGUTIL_NEWLINE - ); -static HRESULT LogStringWork( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - __in_z LPCWSTR sczString, - __in BOOL fLOGUTIL_NEWLINE - ); - -// Hook to allow redirecting LogStringWorkRaw function calls -static PFN_LOGSTRINGWORKRAW s_vpfLogStringWorkRaw = NULL; -static LPVOID s_vpvLogStringWorkRawContext = NULL; - - -/******************************************************************** - IsLogInitialized - Checks if log is currently initialized. -********************************************************************/ -extern "C" BOOL DAPI IsLogInitialized() -{ - return LogUtil_fInitializedCriticalSection; -} - -/******************************************************************** - IsLogOpen - Checks if log is currently initialized and open. -********************************************************************/ -extern "C" BOOL DAPI IsLogOpen() -{ - return (INVALID_HANDLE_VALUE != LogUtil_hLog && NULL != LogUtil_sczLogPath); -} - - -/******************************************************************** - LogInitialize - initializes the logutil API - -********************************************************************/ -extern "C" void DAPI LogInitialize( - __in HMODULE hModule - ) -{ - AssertSz(INVALID_HANDLE_VALUE == LogUtil_hLog && !LogUtil_sczLogPath, "LogInitialize() or LogOpen() - already called."); - - LogUtil_hModule = hModule; - LogUtil_fDisabled = FALSE; - - ::InitializeCriticalSection(&LogUtil_csLog); - LogUtil_fInitializedCriticalSection = TRUE; -} - - -/******************************************************************** - LogOpen - creates an application log file - - NOTE: if wzExt is null then wzLog is path to desired log else wzLog and wzExt are used to generate log name -********************************************************************/ -extern "C" HRESULT DAPI LogOpen( - __in_z_opt LPCWSTR wzDirectory, - __in_z LPCWSTR wzLog, - __in_z_opt LPCWSTR wzPostfix, - __in_z_opt LPCWSTR wzExt, - __in BOOL fAppend, - __in BOOL fHeader, - __out_z_opt LPWSTR* psczLogPath - ) -{ - HRESULT hr = S_OK; - BOOL fEnteredCriticalSection = FALSE; - LPWSTR sczLogDirectory = NULL; - - ::EnterCriticalSection(&LogUtil_csLog); - fEnteredCriticalSection = TRUE; - - if (wzExt && *wzExt) - { - hr = PathCreateTimeBasedTempFile(wzDirectory, wzLog, wzPostfix, wzExt, &LogUtil_sczLogPath, &LogUtil_hLog); - LoguExitOnFailure(hr, "Failed to create log based on current system time."); - } - else - { - hr = PathConcat(wzDirectory, wzLog, &LogUtil_sczLogPath); - LoguExitOnFailure(hr, "Failed to combine the log path."); - - hr = PathGetDirectory(LogUtil_sczLogPath, &sczLogDirectory); - LoguExitOnFailure(hr, "Failed to get log directory."); - - hr = DirEnsureExists(sczLogDirectory, NULL); - LoguExitOnFailure(hr, "Failed to ensure log file directory exists: %ls", sczLogDirectory); - - LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, (fAppend) ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == LogUtil_hLog) - { - LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); - } - - if (fAppend) - { - ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END); - } - } - - LogUtil_fDisabled = FALSE; - - if (fHeader) - { - LogHeader(); - } - - if (NULL != LogUtil_sczPreInitBuffer) - { - // Log anything that was logged before LogOpen() was called. - LogStringWorkRaw(LogUtil_sczPreInitBuffer); - ReleaseNullStr(LogUtil_sczPreInitBuffer); - } - - if (psczLogPath) - { - hr = StrAllocString(psczLogPath, LogUtil_sczLogPath, 0); - LoguExitOnFailure(hr, "Failed to copy log path."); - } - -LExit: - if (fEnteredCriticalSection) - { - ::LeaveCriticalSection(&LogUtil_csLog); - } - - ReleaseStr(sczLogDirectory); - - return hr; -} - - -/******************************************************************** - LogDisable - closes any open files and disables in memory logging. - -********************************************************************/ -void DAPI LogDisable() -{ - ::EnterCriticalSection(&LogUtil_csLog); - - LogUtil_fDisabled = TRUE; - - ReleaseFileHandle(LogUtil_hLog); - ReleaseNullStr(LogUtil_sczLogPath); - ReleaseNullStr(LogUtil_sczPreInitBuffer); - - ::LeaveCriticalSection(&LogUtil_csLog); -} - - -/******************************************************************** - LogRedirect - Redirects all logging strings to the specified - function - or set NULL to disable the hook -********************************************************************/ -void DAPI LogRedirect( - __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw, - __in_opt LPVOID pvContext - ) -{ - s_vpfLogStringWorkRaw = vpfLogStringWorkRaw; - s_vpvLogStringWorkRawContext = pvContext; -} - - -/******************************************************************** - LogRename - Renames a logfile, moving its contents to a new path, - and re-opening the file for appending at the new - location -********************************************************************/ -HRESULT DAPI LogRename( - __in_z LPCWSTR wzNewPath - ) -{ - HRESULT hr = S_OK; - BOOL fEnteredCriticalSection = FALSE; - - ::EnterCriticalSection(&LogUtil_csLog); - fEnteredCriticalSection = TRUE; - - ReleaseFileHandle(LogUtil_hLog); - - hr = FileEnsureMove(LogUtil_sczLogPath, wzNewPath, TRUE, TRUE); - LoguExitOnFailure(hr, "Failed to move logfile to new location: %ls", wzNewPath); - - hr = StrAllocString(&LogUtil_sczLogPath, wzNewPath, 0); - LoguExitOnFailure(hr, "Failed to store new logfile path: %ls", wzNewPath); - - LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == LogUtil_hLog) - { - LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); - } - - // Enable "append" mode by moving file pointer to the end - ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END); - -LExit: - if (fEnteredCriticalSection) - { - ::LeaveCriticalSection(&LogUtil_csLog); - } - - return hr; -} - - -extern "C" void DAPI LogClose( - __in BOOL fFooter - ) -{ - if (INVALID_HANDLE_VALUE != LogUtil_hLog && fFooter) - { - LogFooter(); - } - - ReleaseFileHandle(LogUtil_hLog); - ReleaseNullStr(LogUtil_sczLogPath); - ReleaseNullStr(LogUtil_sczPreInitBuffer); -} - - -extern "C" void DAPI LogUninitialize( - __in BOOL fFooter - ) -{ - LogClose(fFooter); - - if (LogUtil_fInitializedCriticalSection) - { - ::DeleteCriticalSection(&LogUtil_csLog); - LogUtil_fInitializedCriticalSection = FALSE; - } - - LogUtil_hModule = NULL; - LogUtil_fDisabled = FALSE; - - ReleaseNullStr(LogUtil_sczSpecialBeginLine); - ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp); - ReleaseNullStr(LogUtil_sczSpecialEndLine); -} - - -/******************************************************************** - LogIsOpen - returns whether log file is open or note - -********************************************************************/ -extern "C" BOOL DAPI LogIsOpen() -{ - return INVALID_HANDLE_VALUE != LogUtil_hLog; -} - - -/******************************************************************** - LogSetSpecialParams - sets a special beginline string, endline - string, post-timestamp string, etc. -********************************************************************/ -HRESULT DAPI LogSetSpecialParams( - __in_z_opt LPCWSTR wzSpecialBeginLine, - __in_z_opt LPCWSTR wzSpecialAfterTimeStamp, - __in_z_opt LPCWSTR wzSpecialEndLine - ) -{ - HRESULT hr = S_OK; - - // Handle special string to be prepended before every full line - if (NULL == wzSpecialBeginLine) - { - ReleaseNullStr(LogUtil_sczSpecialBeginLine); - } - else - { - hr = StrAllocConcat(&LogUtil_sczSpecialBeginLine, wzSpecialBeginLine, 0); - LoguExitOnFailure(hr, "Failed to allocate copy of special beginline string"); - } - - // Handle special string to be appended to every time stamp - if (NULL == wzSpecialAfterTimeStamp) - { - ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp); - } - else - { - hr = StrAllocConcat(&LogUtil_sczSpecialAfterTimeStamp, wzSpecialAfterTimeStamp, 0); - LoguExitOnFailure(hr, "Failed to allocate copy of special post-timestamp string"); - } - - // Handle special string to be appended before every full line - if (NULL == wzSpecialEndLine) - { - ReleaseNullStr(LogUtil_sczSpecialEndLine); - } - else - { - hr = StrAllocConcat(&LogUtil_sczSpecialEndLine, wzSpecialEndLine, 0); - LoguExitOnFailure(hr, "Failed to allocate copy of special endline string"); - } - -LExit: - return hr; -} - -/******************************************************************** - LogSetLevel - sets the logging level - - NOTE: returns previous logging level -********************************************************************/ -extern "C" REPORT_LEVEL DAPI LogSetLevel( - __in REPORT_LEVEL rl, - __in BOOL fLogChange - ) -{ - AssertSz(REPORT_ERROR != rl, "REPORT_ERROR is not a valid logging level to set"); - - REPORT_LEVEL rlPrev = LogUtil_rlCurrent; - - if (LogUtil_rlCurrent != rl) - { - LogUtil_rlCurrent = rl; - - if (fLogChange) - { - LPCSTR szLevel = LOGUTIL_UNKNOWN; - switch (LogUtil_rlCurrent) - { - case REPORT_WARNING: - szLevel = LOGUTIL_WARNING; - break; - case REPORT_STANDARD: - szLevel = LOGUTIL_STANDARD; - break; - case REPORT_VERBOSE: - szLevel = LOGUTIL_VERBOSE; - break; - case REPORT_DEBUG: - szLevel = LOGUTIL_DEBUG; - break; - case REPORT_NONE: - szLevel = LOGUTIL_NONE; - break; - } - - LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel); - } - } - - return rlPrev; -} - - -/******************************************************************** - LogGetLevel - gets the current logging level - -********************************************************************/ -extern "C" REPORT_LEVEL DAPI LogGetLevel() -{ - return LogUtil_rlCurrent; -} - - -/******************************************************************** - LogGetPath - gets the current log path - -********************************************************************/ -extern "C" HRESULT DAPI LogGetPath( - __out_ecount_z(cchLogPath) LPWSTR pwzLogPath, - __in DWORD cchLogPath - ) -{ - Assert(pwzLogPath); - - HRESULT hr = S_OK; - - if (NULL == LogUtil_sczLogPath) // they can't have a path if there isn't one! - { - ExitFunction1(hr = E_UNEXPECTED); - } - - hr = ::StringCchCopyW(pwzLogPath, cchLogPath, LogUtil_sczLogPath); - -LExit: - return hr; -} - - -/******************************************************************** - LogGetHandle - gets the current log file handle - -********************************************************************/ -extern "C" HANDLE DAPI LogGetHandle() -{ - return LogUtil_hLog; -} - - -/******************************************************************** - LogString - write a string to the log - - NOTE: use printf formatting ("%ls", "%d", etc.) -********************************************************************/ -extern "C" HRESULT DAPIV LogString( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - va_list args; - - va_start(args, szFormat); - hr = LogStringArgs(rl, szFormat, args); - va_end(args); - - return hr; -} - -extern "C" HRESULT DAPI LogStringArgs( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ) -{ - AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); - HRESULT hr = S_OK; - - if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) - { - ExitFunction1(hr = S_FALSE); - } - - hr = LogStringWorkArgs(rl, szFormat, args, FALSE); - -LExit: - return hr; -} - -/******************************************************************** - LogStringLine - write a string plus LOGUTIL_NEWLINE to the log - - NOTE: use printf formatting ("%ls", "%d", etc.) -********************************************************************/ -extern "C" HRESULT DAPIV LogStringLine( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - va_list args; - - va_start(args, szFormat); - hr = LogStringLineArgs(rl, szFormat, args); - va_end(args); - - return hr; -} - -extern "C" HRESULT DAPI LogStringLineArgs( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ) -{ - AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); - HRESULT hr = S_OK; - - if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) - { - ExitFunction1(hr = S_FALSE); - } - - hr = LogStringWorkArgs(rl, szFormat, args, TRUE); - -LExit: - return hr; -} - -/******************************************************************** - LogIdModuleArgs - write a string embedded in a MESSAGETABLE to the log - - NOTE: uses format string from MESSAGETABLE resource -********************************************************************/ - -extern "C" HRESULT DAPI LogIdModuleArgs( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - __in_opt HMODULE hModule, - __in va_list args - ) -{ - AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); - HRESULT hr = S_OK; - - if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) - { - ExitFunction1(hr = S_FALSE); - } - - hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI LogIdModule( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - __in_opt HMODULE hModule, - ... - ) -{ - AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); - HRESULT hr = S_OK; - va_list args; - - if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) - { - ExitFunction1(hr = S_FALSE); - } - - va_start(args, hModule); - hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE); - va_end(args); - -LExit: - return hr; -} - - - - -/******************************************************************** - LogError - write an error to the log - - NOTE: use printf formatting ("%ls", "%d", etc.) -********************************************************************/ -extern "C" HRESULT DAPIV LogErrorString( - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - - va_list args; - va_start(args, szFormat); - hr = LogErrorStringArgs(hrError, szFormat, args); - va_end(args); - - return hr; -} - -extern "C" HRESULT DAPI LogErrorStringArgs( - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ) -{ - HRESULT hr = S_OK; - LPWSTR sczFormat = NULL; - LPWSTR sczMessage = NULL; - - hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP); - LoguExitOnFailure(hr, "Failed to convert format string to wide character string"); - - // format the string as a unicode string - this is necessary to be able to include - // international characters in our output string. This does have the counterintuitive effect - // that the caller's "%s" is interpreted differently - // (so callers should use %hs for LPSTR and %ls for LPWSTR) - hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args); - LoguExitOnFailure(hr, "Failed to format error message: \"%ls\"", sczFormat); - - hr = LogStringLine(REPORT_ERROR, "Error 0x%x: %ls", hrError, sczMessage); - -LExit: - ReleaseStr(sczFormat); - ReleaseStr(sczMessage); - - return hr; -} - - -/******************************************************************** - LogErrorIdModule - write an error string embedded in a MESSAGETABLE to the log - - NOTE: uses format string from MESSAGETABLE resource - can log no more than three strings in the error message -********************************************************************/ -extern "C" HRESULT DAPI LogErrorIdModule( - __in HRESULT hrError, - __in DWORD dwLogId, - __in_opt HMODULE hModule, - __in_z_opt LPCWSTR wzString1 = NULL, - __in_z_opt LPCWSTR wzString2 = NULL, - __in_z_opt LPCWSTR wzString3 = NULL - ) -{ - HRESULT hr = S_OK; - WCHAR wzError[11]; - WORD cStrings = 1; // guaranteed wzError is in the list - - hr = ::StringCchPrintfW(wzError, countof(wzError), L"0x%08x", hrError); - LoguExitOnFailure(hr, "failed to format error code: \"0%08x\"", hrError); - - cStrings += wzString1 ? 1 : 0; - cStrings += wzString2 ? 1 : 0; - cStrings += wzString3 ? 1 : 0; - - hr = LogIdModule(REPORT_ERROR, dwLogId, hModule, wzError, wzString1, wzString2, wzString3); - LoguExitOnFailure(hr, "Failed to log id module."); - -LExit: - return hr; -} - -/******************************************************************** - LogHeader - write a standard header to the log - -********************************************************************/ -extern "C" HRESULT DAPI LogHeader() -{ - HRESULT hr = S_OK; - WCHAR wzComputerName[MAX_PATH]; - DWORD cchComputerName = countof(wzComputerName); - WCHAR wzPath[MAX_PATH]; - DWORD dwMajorVersion = 0; - DWORD dwMinorVersion = 0; - LPCSTR szLevel = LOGUTIL_UNKNOWN; - LPWSTR sczCurrentDateTime = NULL; - - // - // get the interesting data - // - if (!::GetModuleFileNameW(NULL, wzPath, countof(wzPath))) - { - memset(wzPath, 0, sizeof(wzPath)); - } - - hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion); - if (FAILED(hr)) - { - dwMajorVersion = 0; - dwMinorVersion = 0; - } - - if (!::GetComputerNameW(wzComputerName, &cchComputerName)) - { - ::SecureZeroMemory(wzComputerName, sizeof(wzComputerName)); - } - - TimeCurrentDateTime(&sczCurrentDateTime, FALSE); - - // - // write data to the log - // - LogStringLine(REPORT_STANDARD, "=== Logging started: %ls ===", sczCurrentDateTime); - LogStringLine(REPORT_STANDARD, "Executable: %ls v%d.%d.%d.%d", wzPath, dwMajorVersion >> 16, dwMajorVersion & 0xFFFF, dwMinorVersion >> 16, dwMinorVersion & 0xFFFF); - LogStringLine(REPORT_STANDARD, "Computer : %ls", wzComputerName); - switch (LogUtil_rlCurrent) - { - case REPORT_WARNING: - szLevel = LOGUTIL_WARNING; - break; - case REPORT_STANDARD: - szLevel = LOGUTIL_STANDARD; - break; - case REPORT_VERBOSE: - szLevel = LOGUTIL_VERBOSE; - break; - case REPORT_DEBUG: - szLevel = LOGUTIL_DEBUG; - break; - case REPORT_NONE: - szLevel = LOGUTIL_NONE; - break; - } - LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel); - - hr = S_OK; - - ReleaseStr(sczCurrentDateTime); - - return hr; -} - - -/******************************************************************** - LogFooterWork - write a standard footer to the log - -********************************************************************/ - -static HRESULT LogFooterWork( - __in_z __format_string LPCSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - - va_list args; - va_start(args, szFormat); - hr = LogStringWorkArgs(REPORT_STANDARD, szFormat, args, TRUE); - va_end(args); - - return hr; -} - -extern "C" HRESULT DAPI LogFooter() -{ - HRESULT hr = S_OK; - LPWSTR sczCurrentDateTime = NULL; - TimeCurrentDateTime(&sczCurrentDateTime, FALSE); - hr = LogFooterWork("=== Logging stopped: %ls ===", sczCurrentDateTime); - ReleaseStr(sczCurrentDateTime); - return hr; -} - -/******************************************************************** - LogStringWorkRaw - Write a raw, unformatted string to the log - -********************************************************************/ -extern "C" HRESULT LogStringWorkRaw( - __in_z LPCSTR szLogData - ) -{ - Assert(szLogData && *szLogData); - - HRESULT hr = S_OK; - size_t cchLogData = 0; - DWORD cbLogData = 0; - DWORD cbTotal = 0; - DWORD cbWrote = 0; - - hr = ::StringCchLengthA(szLogData, STRSAFE_MAX_CCH, &cchLogData); - LoguExitOnRootFailure(hr, "Failed to get length of raw string"); - - cbLogData = (DWORD)cchLogData; - - // If the log hasn't been initialized yet, store it in a buffer - if (INVALID_HANDLE_VALUE == LogUtil_hLog) - { - hr = StrAnsiAllocConcat(&LogUtil_sczPreInitBuffer, szLogData, 0); - LoguExitOnFailure(hr, "Failed to concatenate string to pre-init buffer"); - - ExitFunction1(hr = S_OK); - } - - // write the string - while (cbTotal < cbLogData) - { - if (!::WriteFile(LogUtil_hLog, reinterpret_cast(szLogData) + cbTotal, cbLogData - cbTotal, &cbWrote, NULL)) - { - LoguExitOnLastError(hr, "Failed to write output to log: %ls - %hs", LogUtil_sczLogPath, szLogData); - } - - cbTotal += cbWrote; - } - -LExit: - return hr; -} - -// -// private worker functions -// -static HRESULT LogIdWork( - __in REPORT_LEVEL rl, - __in_opt HMODULE hModule, - __in DWORD dwLogId, - __in va_list args, - __in BOOL fLOGUTIL_NEWLINE - ) -{ - HRESULT hr = S_OK; - LPWSTR pwz = NULL; - DWORD cch = 0; - - // get the string for the id -#pragma prefast(push) -#pragma prefast(disable:25028) -#pragma prefast(disable:25068) - cch = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE, - static_cast(hModule), dwLogId, 0, reinterpret_cast(&pwz), 0, &args); -#pragma prefast(pop) - - if (0 == cch) - { - LoguExitOnLastError(hr, "failed to log id: %d", dwLogId); - } - - if (2 <= cch && L'\r' == pwz[cch-2] && L'\n' == pwz[cch-1]) - { - pwz[cch-2] = L'\0'; // remove newline from message table - } - - LogStringWork(rl, dwLogId, pwz, fLOGUTIL_NEWLINE); - -LExit: - if (pwz) - { - ::LocalFree(pwz); - } - - return hr; -} - - -static HRESULT LogStringWorkArgs( - __in REPORT_LEVEL rl, - __in_z __format_string LPCSTR szFormat, - __in va_list args, - __in BOOL fLOGUTIL_NEWLINE - ) -{ - Assert(szFormat && *szFormat); - - HRESULT hr = S_OK; - LPWSTR sczFormat = NULL; - LPWSTR sczMessage = NULL; - - hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP); - LoguExitOnFailure(hr, "Failed to convert format string to wide character string"); - - // format the string as a unicode string - hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args); - LoguExitOnFailure(hr, "Failed to format message: \"%ls\"", sczFormat); - - hr = LogStringWork(rl, 0, sczMessage, fLOGUTIL_NEWLINE); - LoguExitOnFailure(hr, "Failed to write formatted string to log:%ls", sczMessage); - -LExit: - ReleaseStr(sczFormat); - ReleaseStr(sczMessage); - - return hr; -} - - -static HRESULT LogStringWork( - __in REPORT_LEVEL rl, - __in DWORD dwLogId, - __in_z LPCWSTR sczString, - __in BOOL fLOGUTIL_NEWLINE - ) -{ - Assert(sczString && *sczString); - - HRESULT hr = S_OK; - BOOL fEnteredCriticalSection = FALSE; - LPWSTR scz = NULL; - LPCWSTR wzLogData = NULL; - LPSTR sczMultiByte = NULL; - - // If logging is disabled, just bail. - if (LogUtil_fDisabled) - { - ExitFunction(); - } - - ::EnterCriticalSection(&LogUtil_csLog); - fEnteredCriticalSection = TRUE; - - if (fLOGUTIL_NEWLINE) - { - // get the process and thread id. - DWORD dwProcessId = ::GetCurrentProcessId(); - DWORD dwThreadId = ::GetCurrentThreadId(); - - // get the time relative to GMT. - SYSTEMTIME st = { }; - ::GetLocalTime(&st); - - DWORD dwId = dwLogId & 0xFFFFFFF; - DWORD dwType = dwLogId & 0xF0000000; - LPSTR szType = (0xE0000000 == dwType || REPORT_ERROR == rl) ? "e" : (0xA0000000 == dwType || REPORT_WARNING == rl) ? "w" : "i"; - - // add line prefix and trailing newline - hr = StrAllocFormatted(&scz, L"%ls[%04X:%04X][%04hu-%02hu-%02huT%02hu:%02hu:%02hu]%hs%03d:%ls %ls%ls", LogUtil_sczSpecialBeginLine ? LogUtil_sczSpecialBeginLine : L"", - dwProcessId, dwThreadId, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, szType, dwId, - LogUtil_sczSpecialAfterTimeStamp ? LogUtil_sczSpecialAfterTimeStamp : L"", sczString, LogUtil_sczSpecialEndLine ? LogUtil_sczSpecialEndLine : L"\r\n"); - LoguExitOnFailure(hr, "Failed to format line prefix."); - } - - wzLogData = scz ? scz : sczString; - - // Convert to UTF-8 before writing out to the log file - hr = StrAnsiAllocString(&sczMultiByte, wzLogData, 0, CP_UTF8); - LoguExitOnFailure(hr, "Failed to convert log string to UTF-8"); - - if (s_vpfLogStringWorkRaw) - { - hr = s_vpfLogStringWorkRaw(sczMultiByte, s_vpvLogStringWorkRawContext); - LoguExitOnFailure(hr, "Failed to write string to log using redirected function: %ls", sczString); - } - else - { - hr = LogStringWorkRaw(sczMultiByte); - LoguExitOnFailure(hr, "Failed to write string to log using default function: %ls", sczString); - } - -LExit: - if (fEnteredCriticalSection) - { - ::LeaveCriticalSection(&LogUtil_csLog); - } - - ReleaseStr(scz); - ReleaseStr(sczMultiByte); - - return hr; -} diff --git a/src/dutil/memutil.cpp b/src/dutil/memutil.cpp deleted file mode 100644 index c805a9c0..00000000 --- a/src/dutil/memutil.cpp +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define MemExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) -#define MemExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) -#define MemExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) -#define MemExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) -#define MemExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) -#define MemExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) -#define MemExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__) -#define MemExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__) -#define MemExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__) -#define MemExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__) -#define MemExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MEMUTIL, e, x, s, __VA_ARGS__) -#define MemExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MEMUTIL, g, x, s, __VA_ARGS__) - - -#if DEBUG -static BOOL vfMemInitialized = FALSE; -#endif - -extern "C" HRESULT DAPI MemInitialize() -{ -#if DEBUG - vfMemInitialized = TRUE; -#endif - return S_OK; -} - -extern "C" void DAPI MemUninitialize() -{ -#if DEBUG - vfMemInitialized = FALSE; -#endif -} - -extern "C" LPVOID DAPI MemAlloc( - __in SIZE_T cbSize, - __in BOOL fZero - ) -{ -// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); - AssertSz(0 < cbSize, "MemAlloc() called with invalid size"); - return ::HeapAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, cbSize); -} - - -extern "C" LPVOID DAPI MemReAlloc( - __in LPVOID pv, - __in SIZE_T cbSize, - __in BOOL fZero - ) -{ -// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); - AssertSz(0 < cbSize, "MemReAlloc() called with invalid size"); - return ::HeapReAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, pv, cbSize); -} - - -extern "C" HRESULT DAPI MemReAllocSecure( - __in LPVOID pv, - __in SIZE_T cbSize, - __in BOOL fZero, - __deref_out LPVOID* ppvNew - ) -{ -// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); - AssertSz(ppvNew, "MemReAllocSecure() called with uninitialized pointer"); - AssertSz(0 < cbSize, "MemReAllocSecure() called with invalid size"); - - HRESULT hr = S_OK; - DWORD dwFlags = HEAP_REALLOC_IN_PLACE_ONLY; - LPVOID pvNew = NULL; - - dwFlags |= fZero ? HEAP_ZERO_MEMORY : 0; - pvNew = ::HeapReAlloc(::GetProcessHeap(), dwFlags, pv, cbSize); - if (!pvNew) - { - pvNew = MemAlloc(cbSize, fZero); - if (pvNew) - { - const SIZE_T cbCurrent = MemSize(pv); - if (-1 == cbCurrent) - { - MemExitOnRootFailure(hr = E_INVALIDARG, "Failed to get memory size"); - } - - // HeapReAlloc may allocate more memory than requested. - const SIZE_T cbNew = MemSize(pvNew); - if (-1 == cbNew) - { - MemExitOnRootFailure(hr = E_INVALIDARG, "Failed to get memory size"); - } - - cbSize = cbNew; - if (cbSize > cbCurrent) - { - cbSize = cbCurrent; - } - - memcpy_s(pvNew, cbNew, pv, cbSize); - - SecureZeroMemory(pv, cbCurrent); - MemFree(pv); - } - } - MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to reallocate memory"); - - *ppvNew = pvNew; - pvNew = NULL; - -LExit: - ReleaseMem(pvNew); - - return hr; -} - - -extern "C" HRESULT DAPI MemAllocArray( - __inout LPVOID* ppvArray, - __in SIZE_T cbArrayType, - __in DWORD dwItemCount - ) -{ - return MemReAllocArray(ppvArray, 0, cbArrayType, dwItemCount); -} - - -extern "C" HRESULT DAPI MemReAllocArray( - __inout LPVOID* ppvArray, - __in DWORD cArray, - __in SIZE_T cbArrayType, - __in DWORD dwNewItemCount - ) -{ - HRESULT hr = S_OK; - DWORD cNew = 0; - LPVOID pvNew = NULL; - SIZE_T cbNew = 0; - - hr = ::DWordAdd(cArray, dwNewItemCount, &cNew); - MemExitOnFailure(hr, "Integer overflow when calculating new element count."); - - hr = ::SIZETMult(cNew, cbArrayType, &cbNew); - MemExitOnFailure(hr, "Integer overflow when calculating new block size."); - - if (*ppvArray) - { - SIZE_T cbCurrent = MemSize(*ppvArray); - if (cbCurrent < cbNew) - { - pvNew = MemReAlloc(*ppvArray, cbNew, TRUE); - MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate larger array."); - - *ppvArray = pvNew; - } - } - else - { - pvNew = MemAlloc(cbNew, TRUE); - MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); - - *ppvArray = pvNew; - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI MemEnsureArraySize( - __deref_inout_bcount(cArray * cbArrayType) LPVOID* ppvArray, - __in DWORD cArray, - __in SIZE_T cbArrayType, - __in DWORD dwGrowthCount - ) -{ - HRESULT hr = S_OK; - DWORD cNew = 0; - LPVOID pvNew = NULL; - SIZE_T cbNew = 0; - - hr = ::DWordAdd(cArray, dwGrowthCount, &cNew); - MemExitOnFailure(hr, "Integer overflow when calculating new element count."); - - hr = ::SIZETMult(cNew, cbArrayType, &cbNew); - MemExitOnFailure(hr, "Integer overflow when calculating new block size."); - - if (*ppvArray) - { - SIZE_T cbUsed = cArray * cbArrayType; - SIZE_T cbCurrent = MemSize(*ppvArray); - if (cbCurrent < cbUsed) - { - pvNew = MemReAlloc(*ppvArray, cbNew, TRUE); - MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate array larger."); - - *ppvArray = pvNew; - } - } - else - { - pvNew = MemAlloc(cbNew, TRUE); - MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); - - *ppvArray = pvNew; - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI MemInsertIntoArray( - __deref_inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, - __in DWORD dwInsertIndex, - __in DWORD cInsertItems, - __in DWORD cExistingArray, - __in SIZE_T cbArrayType, - __in DWORD dwGrowthCount - ) -{ - HRESULT hr = S_OK; - DWORD i; - BYTE *pbArray = NULL; - - if (0 == cInsertItems) - { - ExitFunction1(hr = S_OK); - } - - hr = MemEnsureArraySize(ppvArray, cExistingArray + cInsertItems, cbArrayType, dwGrowthCount); - MemExitOnFailure(hr, "Failed to resize array while inserting items"); - - pbArray = reinterpret_cast(*ppvArray); - for (i = cExistingArray + cInsertItems - 1; i > dwInsertIndex; --i) - { - memcpy_s(pbArray + i * cbArrayType, cbArrayType, pbArray + (i - 1) * cbArrayType, cbArrayType); - } - - // Zero out the newly-inserted items - memset(pbArray + dwInsertIndex * cbArrayType, 0, cInsertItems * cbArrayType); - -LExit: - return hr; -} - -extern "C" void DAPI MemRemoveFromArray( - __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, - __in DWORD dwRemoveIndex, - __in DWORD cRemoveItems, - __in DWORD cExistingArray, - __in SIZE_T cbArrayType, - __in BOOL fPreserveOrder - ) -{ - BYTE *pbArray = static_cast(pvArray); - DWORD cItemsLeftAfterRemoveIndex = (cExistingArray - cRemoveItems - dwRemoveIndex); - - if (fPreserveOrder) - { - memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (dwRemoveIndex + cRemoveItems) * cbArrayType, cItemsLeftAfterRemoveIndex * cbArrayType); - } - else - { - DWORD cItemsToMove = (cRemoveItems > cItemsLeftAfterRemoveIndex ? cItemsLeftAfterRemoveIndex : cRemoveItems); - memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (cExistingArray - cItemsToMove) * cbArrayType, cItemsToMove * cbArrayType); - } - - ZeroMemory(pbArray + (cExistingArray - cRemoveItems) * cbArrayType, cRemoveItems * cbArrayType); -} - -extern "C" void DAPI MemArraySwapItems( - __inout_bcount(cbArrayType) LPVOID pvArray, - __in DWORD dwIndex1, - __in DWORD dwIndex2, - __in SIZE_T cbArrayType - ) -{ - BYTE *pbArrayItem1 = static_cast(pvArray) + dwIndex1 * cbArrayType; - BYTE *pbArrayItem2 = static_cast(pvArray) + dwIndex2 * cbArrayType; - DWORD dwByteIndex = 0; - - if (dwIndex1 == dwIndex2) - { - return; - } - - // Use XOR swapping to avoid the need for a temporary item - while (dwByteIndex < cbArrayType) - { - // Try to do many bytes at a time in most cases - if (cbArrayType - dwByteIndex > sizeof(DWORD64)) - { - // x: X xor Y - *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); - // y: X xor Y - *(reinterpret_cast(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); - // x: X xor Y - *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); - - dwByteIndex += sizeof(DWORD64); - } - else - { - // x: X xor Y - *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); - // y: X xor Y - *(reinterpret_cast(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); - // x: X xor Y - *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); - - dwByteIndex += sizeof(unsigned char); - } - } -} - -extern "C" HRESULT DAPI MemFree( - __in LPVOID pv - ) -{ -// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); - return ::HeapFree(::GetProcessHeap(), 0, pv) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()); -} - - -extern "C" SIZE_T DAPI MemSize( - __in LPCVOID pv - ) -{ -// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); - return ::HeapSize(::GetProcessHeap(), 0, pv); -} diff --git a/src/dutil/metautil.cpp b/src/dutil/metautil.cpp deleted file mode 100644 index f313fc1c..00000000 --- a/src/dutil/metautil.cpp +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// okay, this may look a little weird, but metautil.h cannot be in the -// pre-compiled header because we need to #define these things so the -// correct GUID's get pulled into this object file -#include -#include "metautil.h" - - -// Exit macros -#define MetaExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) -#define MetaExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) -#define MetaExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) -#define MetaExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) -#define MetaExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) -#define MetaExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) -#define MetaExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__) -#define MetaExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__) -#define MetaExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__) -#define MetaExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__) -#define MetaExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_METAUTIL, e, x, s, __VA_ARGS__) -#define MetaExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_METAUTIL, g, x, s, __VA_ARGS__) - - -// prototypes -static void Sort( - __in_ecount(cArray) DWORD dwArray[], - __in int cArray - ); - - -/******************************************************************** - MetaFindWebBase - finds a metabase base string that matches IP, Port and Header - -********************************************************************/ -extern "C" HRESULT DAPI MetaFindWebBase( - __in IMSAdminBaseW* piMetabase, - __in_z LPCWSTR wzIP, - __in int iPort, - __in_z LPCWSTR wzHeader, - __in BOOL fSecure, - __out_ecount(cchWebBase) LPWSTR wzWebBase, - __in DWORD cchWebBase - ) -{ - Assert(piMetabase && cchWebBase); - - HRESULT hr = S_OK; - - BOOL fFound = FALSE; - - WCHAR wzKey[METADATA_MAX_NAME_LEN]; - WCHAR wzSubkey[METADATA_MAX_NAME_LEN]; - DWORD dwIndex = 0; - - METADATA_RECORD mr; - METADATA_RECORD mrAddress; - - LPWSTR pwzExists = NULL; - LPWSTR pwzIPExists = NULL; - LPWSTR pwzPortExists = NULL; - int iPortExists = 0; - LPCWSTR pwzHeaderExists = NULL; - - memset(&mr, 0, sizeof(mr)); - mr.dwMDIdentifier = MD_KEY_TYPE; - mr.dwMDAttributes = METADATA_INHERIT; - mr.dwMDUserType = IIS_MD_UT_SERVER; - mr.dwMDDataType = ALL_METADATA; - - memset(&mrAddress, 0, sizeof(mrAddress)); - mrAddress.dwMDIdentifier = (fSecure) ? MD_SECURE_BINDINGS : MD_SERVER_BINDINGS; - mrAddress.dwMDAttributes = METADATA_INHERIT; - mrAddress.dwMDUserType = IIS_MD_UT_SERVER; - mrAddress.dwMDDataType = ALL_METADATA; - - // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb - for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex) - { - hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex); - if (FAILED(hr)) - break; - - ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey); - hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr); - if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) - { - hr = S_FALSE; // didn't find anything, try next one - continue; - } - MetaExitOnFailure(hr, "failed to get key from metabase while searching for web servers"); - - // if we have an IIsWebServer store the key - if (0 == lstrcmpW(L"IIsWebServer", (LPCWSTR)mr.pbMDData)) - { - hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mrAddress); - if (MD_ERROR_DATA_NOT_FOUND == hr) - hr = S_FALSE; - MetaExitOnFailure(hr, "failed to get address from metabase while searching for web servers"); - - // break down the first address into parts - pwzIPExists = reinterpret_cast(mrAddress.pbMDData); - pwzExists = wcsstr(pwzIPExists, L":"); - if (NULL == pwzExists) - continue; - - *pwzExists = L'\0'; - - pwzPortExists = pwzExists + 1; - pwzExists = wcsstr(pwzPortExists, L":"); - if (NULL == pwzExists) - continue; - - *pwzExists = L'\0'; - iPortExists = wcstol(pwzPortExists, NULL, 10); - - pwzHeaderExists = pwzExists + 1; - - // compare the passed in address with the address listed for this web - if (S_OK == hr && - (0 == lstrcmpW(wzIP, pwzIPExists) || 0 == lstrcmpW(wzIP, L"*")) && - iPort == iPortExists && - 0 == lstrcmpW(wzHeader, pwzHeaderExists)) - { - // if the passed in buffer wasn't big enough - hr = ::StringCchCopyW(wzWebBase, cchWebBase, wzKey); - MetaExitOnFailure(hr, "failed to copy in web base: %ls", wzKey); - - fFound = TRUE; - break; - } - } - } - - if (E_NOMOREITEMS == hr) - { - Assert(!fFound); - hr = S_FALSE; - } - -LExit: - MetaFreeValue(&mrAddress); - MetaFreeValue(&mr); - - if (!fFound && SUCCEEDED(hr)) - hr = S_FALSE; - - return hr; -} - - -/******************************************************************** - MetaFindFreeWebBase - finds the next metabase base string - -********************************************************************/ -extern "C" HRESULT DAPI MetaFindFreeWebBase( - __in IMSAdminBaseW* piMetabase, - __out_ecount(cchWebBase) LPWSTR wzWebBase, - __in DWORD cchWebBase - ) -{ - Assert(piMetabase); - - HRESULT hr = S_OK; - - WCHAR wzKey[METADATA_MAX_NAME_LEN]; - WCHAR wzSubkey[METADATA_MAX_NAME_LEN]; - DWORD dwSubKeys[100]; - int cSubKeys = 0; - DWORD dwIndex = 0; - - int i; - DWORD dwKey; - - METADATA_RECORD mr; - - memset(&mr, 0, sizeof(mr)); - mr.dwMDIdentifier = MD_KEY_TYPE; - mr.dwMDAttributes = 0; - mr.dwMDUserType = IIS_MD_UT_SERVER; - mr.dwMDDataType = STRING_METADATA; - - // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb - for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex) - { - hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex); - if (FAILED(hr)) - break; - - ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey); - - hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr); - if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) - { - hr = S_FALSE; // didn't find anything, try next one - continue; - } - MetaExitOnFailure(hr, "failed to get key from metabase while searching for free web root"); - - // if we have a IIsWebServer get the address information - if (0 == lstrcmpW(L"IIsWebServer", reinterpret_cast(mr.pbMDData))) - { - if (cSubKeys >= countof(dwSubKeys)) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - MetaExitOnFailure(hr, "Insufficient buffer to track all sub-WebSites"); - } - - dwSubKeys[cSubKeys] = wcstol(wzSubkey, NULL, 10); - ++cSubKeys; - Sort(dwSubKeys, cSubKeys); - } - } - - if (E_NOMOREITEMS == hr) - hr = S_OK; - MetaExitOnFailure(hr, "failed to find free web root"); - - // find the lowest free web root - dwKey = 1; - for (i = 0; i < cSubKeys; ++i) - { - if (dwKey < dwSubKeys[i]) - break; - - dwKey = dwSubKeys[i] + 1; - } - - hr = ::StringCchPrintfW(wzWebBase, cchWebBase, L"/LM/W3SVC/%u", dwKey); -LExit: - MetaFreeValue(&mr); - return hr; -} - - -/******************************************************************** - MetaOpenKey - open key - -********************************************************************/ -extern "C" HRESULT DAPI MetaOpenKey( - __in IMSAdminBaseW* piMetabase, - __in METADATA_HANDLE mhKey, - __in_z LPCWSTR wzKey, - __in DWORD dwAccess, - __in DWORD cRetries, - __out METADATA_HANDLE* pmh - ) -{ - Assert(piMetabase && pmh); - - HRESULT hr = S_OK; - - // loop while the key is busy - do - { - hr = piMetabase->OpenKey(mhKey, wzKey, dwAccess, 10, pmh); - if (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr) - ::SleepEx(1000, TRUE); - } while (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr && 0 < cRetries--); - - return hr; -} - - -/******************************************************************** - MetaGetValue - finds the next metabase base string - - NOTE: piMetabase is optional -********************************************************************/ -extern "C" HRESULT DAPI MetaGetValue( - __in IMSAdminBaseW* piMetabase, - __in METADATA_HANDLE mhKey, - __in_z LPCWSTR wzKey, - __inout METADATA_RECORD* pmr - ) -{ - Assert(pmr); - - HRESULT hr = S_OK; - BOOL fInitialized = FALSE; - DWORD cbRequired = 0; - - if (!piMetabase) - { - hr = ::CoInitialize(NULL); - MetaExitOnFailure(hr, "failed to initialize COM"); - fInitialized = TRUE; - - hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); - MetaExitOnFailure(hr, "failed to get IID_IMSAdminBaseW object"); - } - - if (!pmr->pbMDData) - { - pmr->dwMDDataLen = 256; - pmr->pbMDData = static_cast(MemAlloc(pmr->dwMDDataLen, TRUE)); - MetaExitOnNull(pmr->pbMDData, hr, E_OUTOFMEMORY, "failed to allocate memory for metabase value"); - } - else // set the size of the data to the actual size of the memory - { - SIZE_T cb = MemSize(pmr->pbMDData); - if (cb > DWORD_MAX) - { - MetaExitOnRootFailure(hr = E_INVALIDSTATE, "metabase data is too large: %Iu", cb); - } - pmr->dwMDDataLen = (DWORD)cb; - } - - hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired); - if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) - { - pmr->dwMDDataLen = cbRequired; - BYTE* pb = static_cast(MemReAlloc(pmr->pbMDData, pmr->dwMDDataLen, TRUE)); - MetaExitOnNull(pb, hr, E_OUTOFMEMORY, "failed to reallocate memory for metabase value"); - - pmr->pbMDData = pb; - hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired); - } - MetaExitOnFailure(hr, "failed to get metabase data"); - -LExit: - if (fInitialized) - { - ReleaseObject(piMetabase); - ::CoUninitialize(); - } - - return hr; -} - - -/******************************************************************** - MetaFreeValue - frees data in METADATA_RECORD remove MetaGetValue() - - NOTE: METADATA_RECORD must have been returned from MetaGetValue() above -********************************************************************/ -extern "C" void DAPI MetaFreeValue( - __in METADATA_RECORD* pmr - ) -{ - Assert(pmr); - - ReleaseNullMem(pmr->pbMDData); -} - - -// -// private -// - -/******************************************************************** - Sort - quick and dirty insertion sort - -********************************************************************/ -static void Sort( - __in_ecount(cArray) DWORD dwArray[], - __in int cArray - ) -{ - int i, j; - DWORD dwData; - - for (i = 1; i < cArray; ++i) - { - dwData = dwArray[i]; - - j = i - 1; - while (0 <= j && dwArray[j] > dwData) - { - dwArray[j + 1] = dwArray[j]; - j--; - } - - dwArray[j + 1] = dwData; - } -} diff --git a/src/dutil/monutil.cpp b/src/dutil/monutil.cpp deleted file mode 100644 index 6a7f0596..00000000 --- a/src/dutil/monutil.cpp +++ /dev/null @@ -1,2019 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define MonExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) -#define MonExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) -#define MonExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) -#define MonExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) -#define MonExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) -#define MonExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) -#define MonExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_MONUTIL, p, x, e, s, __VA_ARGS__) -#define MonExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__) -#define MonExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_MONUTIL, p, x, e, s, __VA_ARGS__) -#define MonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__) -#define MonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MONUTIL, e, x, s, __VA_ARGS__) -#define MonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MONUTIL, g, x, s, __VA_ARGS__) - -const int MON_THREAD_GROWTH = 5; -const int MON_ARRAY_GROWTH = 40; -const int MON_MAX_MONITORS_PER_THREAD = 63; -const int MON_THREAD_INIT_RETRIES = 1000; -const int MON_THREAD_INIT_RETRY_PERIOD_IN_MS = 10; -const int MON_THREAD_NETWORK_FAIL_RETRY_IN_MS = 1000*60; // if we know we failed to connect, retry every minute -const int MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS = 1000*60*20; // if we're just checking for remote servers dieing, check much less frequently -const int MON_THREAD_WAIT_REMOVE_DEVICE = 5000; -const LPCWSTR MONUTIL_WINDOW_CLASS = L"MonUtilClass"; - -enum MON_MESSAGE -{ - MON_MESSAGE_ADD = WM_APP + 1, - MON_MESSAGE_REMOVE, - MON_MESSAGE_REMOVED, // Sent by waiter thread back to coordinator thread to indicate a remove occurred - MON_MESSAGE_NETWORK_WAIT_FAILED, // Sent by waiter thread back to coordinator thread to indicate a network wait failed. Coordinator thread will periodically trigger retries (via MON_MESSAGE_NETWORK_STATUS_UPDATE messages). - MON_MESSAGE_NETWORK_WAIT_SUCCEEDED, // Sent by waiter thread back to coordinator thread to indicate a previously failing network wait is now succeeding. Coordinator thread will stop triggering retries if no other failing waits exist. - MON_MESSAGE_NETWORK_STATUS_UPDATE, // Some change to network connectivity occurred (a network connection was connected or disconnected for example) - MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any successful network waits. - // Annoyingly, this is necessary to catch the rare case that the remote server goes offline unexpectedly, such as by - // network cable unplugged or power loss - in this case there is no local network status change, and the wait will just never fire. - // So we very occasionally retry all successful network waits. When this occurs, we notify for changes, even though there may not have been any. - // This is because we have no way to detect if the old wait had failed (and changes were lost) due to the remote server going offline during that time or not. - // If we do this often, it can cause a lot of wasted work (which could be expensive for battery life), so the default is to do it very rarely (every 20 minutes). - MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any failed network waits - MON_MESSAGE_DRIVE_STATUS_UPDATE, // Some change to local drive has occurred (new drive created or plugged in, or removed) - MON_MESSAGE_DRIVE_QUERY_REMOVE, // User wants to unplug a drive, which MonUtil will always allow - MON_MESSAGE_STOP -}; - -enum MON_TYPE -{ - MON_NONE = 0, - MON_DIRECTORY = 1, - MON_REGKEY = 2 -}; - -struct MON_REQUEST -{ - MON_TYPE type; - DWORD dwMaxSilencePeriodInMs; - - // Handle to the main window for RegisterDeviceNotification() (same handle as owned by coordinator thread) - HWND hwnd; - // and handle to the notification (specific to this request) - HDEVNOTIFY hNotify; - - BOOL fRecursive; - void *pvContext; - - HRESULT hrStatus; - - LPWSTR sczOriginalPathRequest; - BOOL fNetwork; // This reflects either a UNC or mounted drive original request - DWORD dwPathHierarchyIndex; - LPWSTR *rgsczPathHierarchy; - DWORD cPathHierarchy; - - // If the notify fires, fPendingFire gets set to TRUE, and we wait to see if other writes are occurring, and only after the configured silence period do we notify of changes - // after notification, we set fPendingFire back to FALSE - BOOL fPendingFire; - BOOL fSkipDeltaAdd; - DWORD dwSilencePeriodInMs; - - union - { - struct - { - } directory; - struct - { - HKEY hkRoot; - HKEY hkSubKey; - REG_KEY_BITNESS kbKeyBitness; // Only used to pass on 32-bit, 64-bit, or default parameter - } regkey; - }; -}; - -struct MON_ADD_MESSAGE -{ - MON_REQUEST request; - HANDLE handle; -}; - -struct MON_REMOVE_MESSAGE -{ - MON_TYPE type; - BOOL fRecursive; - - union - { - struct - { - LPWSTR sczDirectory; - } directory; - struct - { - HKEY hkRoot; - LPWSTR sczSubKey; - REG_KEY_BITNESS kbKeyBitness; - } regkey; - }; -}; - -struct MON_WAITER_CONTEXT -{ - DWORD dwCoordinatorThreadId; - - HANDLE hWaiterThread; - DWORD dwWaiterThreadId; - BOOL fWaiterThreadMessageQueueInitialized; - - // Callbacks - PFN_MONGENERAL vpfMonGeneral; - PFN_MONDIRECTORY vpfMonDirectory; - PFN_MONREGKEY vpfMonRegKey; - - // Context for callbacks - LPVOID pvContext; - - // HANDLEs are in their own array for easy use with WaitForMultipleObjects() - // After initialization, the very first handle is just to wake the listener thread to have it re-wait on a new list - // Because this array is read by both coordinator thread and waiter thread, to avoid locking between both threads, it must start at the maximum size - HANDLE *rgHandles; - DWORD cHandles; - - // Requested things to monitor - MON_REQUEST *rgRequests; - DWORD cRequests; - - // Number of pending notifications - DWORD cRequestsPending; - - // Number of requests in a failed state (couldn't initiate wait) - DWORD cRequestsFailing; -}; - -// Info stored about each waiter by the coordinator -struct MON_WAITER_INFO -{ - DWORD cMonitorCount; - - MON_WAITER_CONTEXT *pWaiterContext; -}; - -// This struct is used when Thread A wants to send a task to another thread B (and get notified when the task finishes) -// You typically declare this struct in a manner that a pointer to it is valid as long as a thread that could respond is still running -// (even long after sender is no longer waiting, in case thread has huge message queue) -// and you must send 2 parameters in the message: -// 1) a pointer to this struct (which is always valid) -// 2) the original value of dwIteration -// The receiver of the message can compare the current value of dwSendIteration in the struct with what was sent in the message -// If values are different, we're too late and thread A is no longer waiting on this response -// otherwise, set dwResponseIteration to the same value, and call ::SetEvent() on hWait -// Thread A will then wakeup, and must verify that dwResponseIteration == dwSendIteration to ensure it isn't an earlier out-of-date reply -// replying to a newer wait -// pvContext is used to send a misc parameter related to processing data -struct MON_INTERNAL_TEMPORARY_WAIT -{ - // Should be incremented each time sender sends a pointer to this struct, so each request has a different iteration - DWORD dwSendIteration; - DWORD dwReceiveIteration; - HANDLE hWait; - void *pvContext; -}; - -struct MON_STRUCT -{ - HANDLE hCoordinatorThread; - DWORD dwCoordinatorThreadId; - BOOL fCoordinatorThreadMessageQueueInitialized; - - // Invisible window for receiving network status & drive added/removal messages - HWND hwnd; - // Used by window procedure for sending request and waiting for response from waiter threads - // such as in event of a request to remove a device - MON_INTERNAL_TEMPORARY_WAIT internalWait; - - // Callbacks - PFN_MONGENERAL vpfMonGeneral; - PFN_MONDRIVESTATUS vpfMonDriveStatus; - PFN_MONDIRECTORY vpfMonDirectory; - PFN_MONREGKEY vpfMonRegKey; - - // Context for callbacks - LPVOID pvContext; - - // Waiter thread array - MON_WAITER_INFO *rgWaiterThreads; - DWORD cWaiterThreads; -}; - -const int MON_HANDLE_BYTES = sizeof(MON_STRUCT); - -static DWORD WINAPI CoordinatorThread( - __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext - ); -// Initiates (or if *pHandle is non-null, continues) wait on the directory or subkey -// if the directory or subkey doesn't exist, instead calls it on the first existing parent directory or subkey -// writes to pRequest->dwPathHierarchyIndex with the array index that was waited on -static HRESULT InitiateWait( - __inout MON_REQUEST *pRequest, - __inout HANDLE *pHandle - ); -static DWORD WINAPI WaiterThread( - __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext - ); -static void Notify( - __in HRESULT hr, - __in MON_WAITER_CONTEXT *pWaiterContext, - __in MON_REQUEST *pRequest - ); -static void MonRequestDestroy( - __in MON_REQUEST *pRequest - ); -static void MonAddMessageDestroy( - __in_opt MON_ADD_MESSAGE *pMessage - ); -static void MonRemoveMessageDestroy( - __in_opt MON_REMOVE_MESSAGE *pMessage - ); -static BOOL GetRecursiveFlag( - __in MON_REQUEST *pRequest, - __in DWORD dwIndex - ); -static HRESULT FindRequestIndex( - __in MON_WAITER_CONTEXT *pWaiterContext, - __in MON_REMOVE_MESSAGE *pMessage, - __out DWORD *pdwIndex - ); -static HRESULT RemoveRequest( - __inout MON_WAITER_CONTEXT *pWaiterContext, - __in DWORD dwRequestIndex - ); -static REGSAM GetRegKeyBitness( - __in MON_REQUEST *pRequest - ); -static HRESULT DuplicateRemoveMessage( - __in MON_REMOVE_MESSAGE *pMessage, - __out MON_REMOVE_MESSAGE **ppMessage - ); -static LRESULT CALLBACK MonWndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ); -static HRESULT CreateMonWindow( - __in MON_STRUCT *pm, - __out HWND *pHwnd - ); -// if *phMonitor is non-NULL, closes the old wait before re-starting the new wait -static HRESULT WaitForNetworkChanges( - __inout HANDLE *phMonitor, - __in MON_STRUCT *pm - ); -static HRESULT UpdateWaitStatus( - __in HRESULT hrNewStatus, - __inout MON_WAITER_CONTEXT *pWaiterContext, - __in DWORD dwRequestIndex, - __out_opt DWORD *pdwNewRequestIndex - ); - -extern "C" HRESULT DAPI MonCreate( - __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle, - __in PFN_MONGENERAL vpfMonGeneral, - __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus, - __in_opt PFN_MONDIRECTORY vpfMonDirectory, - __in_opt PFN_MONREGKEY vpfMonRegKey, - __in_opt LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - DWORD dwRetries = MON_THREAD_INIT_RETRIES; - - MonExitOnNull(pHandle, hr, E_INVALIDARG, "Pointer to handle not specified while creating monitor"); - - // Allocate the struct - *pHandle = static_cast(MemAlloc(sizeof(MON_STRUCT), TRUE)); - MonExitOnNull(*pHandle, hr, E_OUTOFMEMORY, "Failed to allocate monitor object"); - - MON_STRUCT *pm = static_cast(*pHandle); - - pm->vpfMonGeneral = vpfMonGeneral; - pm->vpfMonDriveStatus = vpfMonDriveStatus; - pm->vpfMonDirectory = vpfMonDirectory; - pm->vpfMonRegKey = vpfMonRegKey; - pm->pvContext = pvContext; - - pm->hCoordinatorThread = ::CreateThread(NULL, 0, CoordinatorThread, pm, 0, &pm->dwCoordinatorThreadId); - if (!pm->hCoordinatorThread) - { - MonExitWithLastError(hr, "Failed to create waiter thread."); - } - - // Ensure the created thread initializes its message queue. It does this first thing, so if it doesn't within 10 seconds, there must be a huge problem. - while (!pm->fCoordinatorThreadMessageQueueInitialized && 0 < dwRetries) - { - ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS); - --dwRetries; - } - - if (0 == dwRetries) - { - hr = E_UNEXPECTED; - MonExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI MonAddDirectory( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in_z LPCWSTR wzDirectory, - __in BOOL fRecursive, - __in DWORD dwSilencePeriodInMs, - __in_opt LPVOID pvDirectoryContext - ) -{ - HRESULT hr = S_OK; - MON_STRUCT *pm = static_cast(handle); - LPWSTR sczDirectory = NULL; - LPWSTR sczOriginalPathRequest = NULL; - MON_ADD_MESSAGE *pMessage = NULL; - - hr = StrAllocString(&sczOriginalPathRequest, wzDirectory, 0); - MonExitOnFailure(hr, "Failed to convert directory string to UNC path"); - - hr = PathBackslashTerminate(&sczOriginalPathRequest); - MonExitOnFailure(hr, "Failed to ensure directory ends in backslash"); - - pMessage = reinterpret_cast(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE)); - MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); - - if (sczOriginalPathRequest[0] == L'\\' && sczOriginalPathRequest[1] == L'\\') - { - pMessage->request.fNetwork = TRUE; - } - else - { - hr = UncConvertFromMountedDrive(&sczDirectory, sczOriginalPathRequest); - if (SUCCEEDED(hr)) - { - pMessage->request.fNetwork = TRUE; - } - } - - if (NULL == sczDirectory) - { - // Likely not a mounted drive - just copy the request then - hr = S_OK; - - hr = StrAllocString(&sczDirectory, sczOriginalPathRequest, 0); - MonExitOnFailure(hr, "Failed to copy original path request: %ls", sczOriginalPathRequest); - } - - pMessage->handle = INVALID_HANDLE_VALUE; - pMessage->request.type = MON_DIRECTORY; - pMessage->request.fRecursive = fRecursive; - pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs; - pMessage->request.hwnd = pm->hwnd; - pMessage->request.pvContext = pvDirectoryContext; - pMessage->request.sczOriginalPathRequest = sczOriginalPathRequest; - sczOriginalPathRequest = NULL; - - hr = PathGetHierarchyArray(sczDirectory, &pMessage->request.rgsczPathHierarchy, reinterpret_cast(&pMessage->request.cPathHierarchy)); - MonExitOnFailure(hr, "Failed to get hierarchy array for path %ls", sczDirectory); - - if (0 < pMessage->request.cPathHierarchy) - { - pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle); - if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast(pMessage), 0)) - { - MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); - } - pMessage = NULL; - } - -LExit: - ReleaseStr(sczDirectory); - ReleaseStr(sczOriginalPathRequest); - MonAddMessageDestroy(pMessage); - - return hr; -} - -extern "C" HRESULT DAPI MonAddRegKey( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fRecursive, - __in DWORD dwSilencePeriodInMs, - __in_opt LPVOID pvRegKeyContext - ) -{ - HRESULT hr = S_OK; - MON_STRUCT *pm = static_cast(handle); - LPWSTR sczSubKey = NULL; - MON_ADD_MESSAGE *pMessage = NULL; - - hr = StrAllocString(&sczSubKey, wzSubKey, 0); - MonExitOnFailure(hr, "Failed to copy subkey string"); - - hr = PathBackslashTerminate(&sczSubKey); - MonExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); - - pMessage = reinterpret_cast(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE)); - MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); - - pMessage->handle = ::CreateEventW(NULL, TRUE, FALSE, NULL); - MonExitOnNullWithLastError(pMessage->handle, hr, "Failed to create anonymous event for regkey monitor"); - - pMessage->request.type = MON_REGKEY; - pMessage->request.regkey.hkRoot = hkRoot; - pMessage->request.regkey.kbKeyBitness = kbKeyBitness; - pMessage->request.fRecursive = fRecursive; - pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs, - pMessage->request.hwnd = pm->hwnd; - pMessage->request.pvContext = pvRegKeyContext; - - hr = PathGetHierarchyArray(sczSubKey, &pMessage->request.rgsczPathHierarchy, reinterpret_cast(&pMessage->request.cPathHierarchy)); - MonExitOnFailure(hr, "Failed to get hierarchy array for subkey %ls", sczSubKey); - - if (0 < pMessage->request.cPathHierarchy) - { - pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle); - MonExitOnFailure(hr, "Failed to initiate wait"); - - if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast(pMessage), 0)) - { - MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for regkey %ls", sczSubKey); - } - pMessage = NULL; - } - -LExit: - ReleaseStr(sczSubKey); - MonAddMessageDestroy(pMessage); - - return hr; -} - -extern "C" HRESULT DAPI MonRemoveDirectory( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in_z LPCWSTR wzDirectory, - __in BOOL fRecursive - ) -{ - HRESULT hr = S_OK; - MON_STRUCT *pm = static_cast(handle); - LPWSTR sczDirectory = NULL; - MON_REMOVE_MESSAGE *pMessage = NULL; - - hr = StrAllocString(&sczDirectory, wzDirectory, 0); - MonExitOnFailure(hr, "Failed to copy directory string"); - - hr = PathBackslashTerminate(&sczDirectory); - MonExitOnFailure(hr, "Failed to ensure directory ends in backslash"); - - pMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); - MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); - - pMessage->type = MON_DIRECTORY; - pMessage->fRecursive = fRecursive; - - hr = StrAllocString(&pMessage->directory.sczDirectory, sczDirectory, 0); - MonExitOnFailure(hr, "Failed to allocate copy of directory string"); - - if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pMessage), 0)) - { - MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); - } - pMessage = NULL; - -LExit: - MonRemoveMessageDestroy(pMessage); - - return hr; -} - -extern "C" HRESULT DAPI MonRemoveRegKey( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fRecursive - ) -{ - HRESULT hr = S_OK; - MON_STRUCT *pm = static_cast(handle); - LPWSTR sczSubKey = NULL; - MON_REMOVE_MESSAGE *pMessage = NULL; - - hr = StrAllocString(&sczSubKey, wzSubKey, 0); - MonExitOnFailure(hr, "Failed to copy subkey string"); - - hr = PathBackslashTerminate(&sczSubKey); - MonExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); - - pMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); - MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); - - pMessage->type = MON_REGKEY; - pMessage->regkey.hkRoot = hkRoot; - pMessage->regkey.kbKeyBitness = kbKeyBitness; - pMessage->fRecursive = fRecursive; - - hr = StrAllocString(&pMessage->regkey.sczSubKey, sczSubKey, 0); - MonExitOnFailure(hr, "Failed to allocate copy of directory string"); - - if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pMessage), 0)) - { - MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczSubKey); - } - pMessage = NULL; - -LExit: - ReleaseStr(sczSubKey); - MonRemoveMessageDestroy(pMessage); - - return hr; -} - -extern "C" void DAPI MonDestroy( - __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - MON_STRUCT *pm = static_cast(handle); - - if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0)) - { - er = ::GetLastError(); - if (ERROR_INVALID_THREAD_ID == er) - { - // It already halted, or doesn't exist for some other reason, so let's just ignore it and clean up - er = ERROR_SUCCESS; - } - MonExitOnWin32Error(er, hr, "Failed to send message to background thread to halt"); - } - - if (pm->hCoordinatorThread) - { - ::WaitForSingleObject(pm->hCoordinatorThread, INFINITE); - ::CloseHandle(pm->hCoordinatorThread); - } - -LExit: - return; -} - -static void MonRequestDestroy( - __in MON_REQUEST *pRequest - ) -{ - if (NULL != pRequest) - { - if (MON_REGKEY == pRequest->type) - { - ReleaseRegKey(pRequest->regkey.hkSubKey); - } - else if (MON_DIRECTORY == pRequest->type && pRequest->hNotify) - { - UnregisterDeviceNotification(pRequest->hNotify); - pRequest->hNotify = NULL; - } - ReleaseStr(pRequest->sczOriginalPathRequest); - ReleaseStrArray(pRequest->rgsczPathHierarchy, pRequest->cPathHierarchy); - } -} - -static void MonAddMessageDestroy( - __in_opt MON_ADD_MESSAGE *pMessage - ) -{ - if (pMessage) - { - MonRequestDestroy(&pMessage->request); - if (MON_DIRECTORY == pMessage->request.type && INVALID_HANDLE_VALUE != pMessage->handle) - { - ::FindCloseChangeNotification(pMessage->handle); - } - else if (MON_REGKEY == pMessage->request.type) - { - ReleaseHandle(pMessage->handle); - } - - ReleaseMem(pMessage); - } -} - -static void MonRemoveMessageDestroy( - __in_opt MON_REMOVE_MESSAGE *pMessage - ) -{ - if (pMessage) - { - switch (pMessage->type) - { - case MON_DIRECTORY: - ReleaseStr(pMessage->directory.sczDirectory); - break; - case MON_REGKEY: - ReleaseStr(pMessage->regkey.sczSubKey); - break; - default: - Assert(false); - } - - ReleaseMem(pMessage); - } -} - -static DWORD WINAPI CoordinatorThread( - __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - MSG msg = { }; - DWORD dwThreadIndex = DWORD_MAX; - DWORD dwRetries; - DWORD dwFailingNetworkWaits = 0; - MON_WAITER_CONTEXT *pWaiterContext = NULL; - MON_REMOVE_MESSAGE *pRemoveMessage = NULL; - MON_REMOVE_MESSAGE *pTempRemoveMessage = NULL; - MON_STRUCT *pm = reinterpret_cast(pvContext); - WSADATA wsaData = { }; - HANDLE hMonitor = NULL; - BOOL fRet = FALSE; - UINT_PTR uTimerSuccessfulNetworkRetry = 0; - UINT_PTR uTimerFailedNetworkRetry = 0; - - // Ensure the thread has a message queue - ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); - pm->fCoordinatorThreadMessageQueueInitialized = TRUE; - - hr = CreateMonWindow(pm, &pm->hwnd); - MonExitOnFailure(hr, "Failed to create window for status update thread"); - - ::WSAStartup(MAKEWORD(2, 2), &wsaData); - - hr = WaitForNetworkChanges(&hMonitor, pm); - MonExitOnFailure(hr, "Failed to wait for network changes"); - - uTimerSuccessfulNetworkRetry = ::SetTimer(NULL, 1, MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS, NULL); - if (0 == uTimerSuccessfulNetworkRetry) - { - MonExitWithLastError(hr, "Failed to set timer for network successful retry"); - } - - while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) - { - if (-1 == fRet) - { - hr = E_UNEXPECTED; - MonExitOnRootFailure(hr, "Unexpected return value from message pump."); - } - else - { - switch (msg.message) - { - case MON_MESSAGE_ADD: - dwThreadIndex = DWORD_MAX; - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - if (pm->rgWaiterThreads[i].cMonitorCount < MON_MAX_MONITORS_PER_THREAD) - { - dwThreadIndex = i; - break; - } - } - - if (dwThreadIndex < pm->cWaiterThreads) - { - pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext; - } - else - { - hr = MemEnsureArraySize(reinterpret_cast(&pm->rgWaiterThreads), pm->cWaiterThreads + 1, sizeof(MON_WAITER_INFO), MON_THREAD_GROWTH); - MonExitOnFailure(hr, "Failed to grow waiter thread array size"); - ++pm->cWaiterThreads; - - dwThreadIndex = pm->cWaiterThreads - 1; - pm->rgWaiterThreads[dwThreadIndex].pWaiterContext = reinterpret_cast(MemAlloc(sizeof(MON_WAITER_CONTEXT), TRUE)); - MonExitOnNull(pm->rgWaiterThreads[dwThreadIndex].pWaiterContext, hr, E_OUTOFMEMORY, "Failed to allocate waiter context struct"); - pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext; - pWaiterContext->dwCoordinatorThreadId = ::GetCurrentThreadId(); - pWaiterContext->vpfMonGeneral = pm->vpfMonGeneral; - pWaiterContext->vpfMonDirectory = pm->vpfMonDirectory; - pWaiterContext->vpfMonRegKey = pm->vpfMonRegKey; - pWaiterContext->pvContext = pm->pvContext; - - hr = MemEnsureArraySize(reinterpret_cast(&pWaiterContext->rgHandles), MON_MAX_MONITORS_PER_THREAD + 1, sizeof(HANDLE), 0); - MonExitOnFailure(hr, "Failed to allocate first handle"); - pWaiterContext->cHandles = 1; - - pWaiterContext->rgHandles[0] = ::CreateEventW(NULL, FALSE, FALSE, NULL); - MonExitOnNullWithLastError(pWaiterContext->rgHandles[0], hr, "Failed to create general event"); - - pWaiterContext->hWaiterThread = ::CreateThread(NULL, 0, WaiterThread, pWaiterContext, 0, &pWaiterContext->dwWaiterThreadId); - if (!pWaiterContext->hWaiterThread) - { - MonExitWithLastError(hr, "Failed to create waiter thread."); - } - - dwRetries = MON_THREAD_INIT_RETRIES; - while (!pWaiterContext->fWaiterThreadMessageQueueInitialized && 0 < dwRetries) - { - ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS); - --dwRetries; - } - - if (0 == dwRetries) - { - hr = E_UNEXPECTED; - MonExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); - } - } - - ++pm->rgWaiterThreads[dwThreadIndex].cMonitorCount; - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_ADD, msg.wParam, 0)) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); - } - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming message"); - } - break; - - case MON_MESSAGE_REMOVE: - // Send remove to all waiter threads. They'll ignore it if they don't have that monitor. - // If they do have that monitor, they'll remove it from their list, and tell coordinator they have another - // empty slot via MON_MESSAGE_REMOVED message - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - pRemoveMessage = reinterpret_cast(msg.wParam); - - hr = DuplicateRemoveMessage(pRemoveMessage, &pTempRemoveMessage); - MonExitOnFailure(hr, "Failed to duplicate remove message"); - - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pTempRemoveMessage), msg.lParam)) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); - } - pTempRemoveMessage = NULL; - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming remove message"); - } - } - MonRemoveMessageDestroy(pRemoveMessage); - pRemoveMessage = NULL; - break; - - case MON_MESSAGE_REMOVED: - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - if (pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId == static_cast(msg.wParam)) - { - Assert(pm->rgWaiterThreads[i].cMonitorCount > 0); - --pm->rgWaiterThreads[i].cMonitorCount; - if (0 == pm->rgWaiterThreads[i].cMonitorCount) - { - if (!::PostThreadMessageW(pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam)) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to stop"); - } - MemRemoveFromArray(reinterpret_cast(pm->rgWaiterThreads), i, 1, pm->cWaiterThreads, sizeof(MON_WAITER_INFO), TRUE); - --pm->cWaiterThreads; - --i; // reprocess this index in the for loop, which will now contain the item after the one we removed - } - } - } - break; - - case MON_MESSAGE_NETWORK_WAIT_FAILED: - if (0 == dwFailingNetworkWaits) - { - uTimerFailedNetworkRetry = ::SetTimer(NULL, uTimerSuccessfulNetworkRetry + 1, MON_THREAD_NETWORK_FAIL_RETRY_IN_MS, NULL); - if (0 == uTimerFailedNetworkRetry) - { - MonExitWithLastError(hr, "Failed to set timer for network fail retry"); - } - } - ++dwFailingNetworkWaits; - break; - - case MON_MESSAGE_NETWORK_WAIT_SUCCEEDED: - --dwFailingNetworkWaits; - if (0 == dwFailingNetworkWaits) - { - if (!::KillTimer(NULL, uTimerFailedNetworkRetry)) - { - MonExitWithLastError(hr, "Failed to kill timer for network fail retry"); - } - uTimerFailedNetworkRetry = 0; - } - break; - - case MON_MESSAGE_NETWORK_STATUS_UPDATE: - hr = WaitForNetworkChanges(&hMonitor, pm); - MonExitOnFailure(hr, "Failed to re-wait for network changes"); - - // Propagate any network status update messages to all waiter threads - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_NETWORK_STATUS_UPDATE, 0, 0)) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); - } - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); - } - } - break; - - case WM_TIMER: - // Timer means some network wait is failing, and we need to retry every so often in case a remote server goes back up - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, msg.wParam == uTimerFailedNetworkRetry ? MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS : MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, 0, 0)) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); - } - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); - } - } - break; - - case MON_MESSAGE_DRIVE_STATUS_UPDATE: - // If user requested to be notified of drive status updates, notify! - if (pm->vpfMonDriveStatus) - { - pm->vpfMonDriveStatus(static_cast(msg.wParam), static_cast(msg.lParam), pm->pvContext); - } - - // Propagate any drive status update messages to all waiter threads - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_STATUS_UPDATE, msg.wParam, msg.lParam)) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive status update"); - } - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive status update message"); - } - } - break; - - case MON_MESSAGE_STOP: - ExitFunction1(hr = static_cast(msg.wParam)); - - default: - // This thread owns a window, so this handles all the other random messages we get - ::TranslateMessage(&msg); - ::DispatchMessageW(&msg); - break; - } - } - } - -LExit: - if (uTimerFailedNetworkRetry) - { - fRet = ::KillTimer(NULL, uTimerFailedNetworkRetry); - } - if (uTimerSuccessfulNetworkRetry) - { - fRet = ::KillTimer(NULL, uTimerSuccessfulNetworkRetry); - } - - if (pm->hwnd) - { - ::CloseWindow(pm->hwnd); - } - - // Tell all waiter threads to shutdown - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - if (NULL != pWaiterContext->rgHandles[0]) - { - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam)) - { - TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to waiter thread to stop"); - } - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to set event to notify waiter thread of incoming message"); - } - } - } - - if (hMonitor != NULL) - { - ::WSALookupServiceEnd(hMonitor); - } - - // Now confirm they're actually shut down before returning - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - if (NULL != pWaiterContext->hWaiterThread) - { - ::WaitForSingleObject(pWaiterContext->hWaiterThread, INFINITE); - ::CloseHandle(pWaiterContext->hWaiterThread); - } - - // Waiter thread can't release these, because coordinator thread uses it to try communicating with waiter thread - ReleaseHandle(pWaiterContext->rgHandles[0]); - ReleaseMem(pWaiterContext->rgHandles); - - ReleaseMem(pWaiterContext); - } - - if (FAILED(hr)) - { - // If coordinator thread fails, notify general callback of an error - Assert(pm->vpfMonGeneral); - pm->vpfMonGeneral(hr, pm->pvContext); - } - MonRemoveMessageDestroy(pRemoveMessage); - MonRemoveMessageDestroy(pTempRemoveMessage); - - ::WSACleanup(); - - return hr; -} - -static HRESULT InitiateWait( - __inout MON_REQUEST *pRequest, - __inout HANDLE *pHandle - ) -{ - HRESULT hr = S_OK; - HRESULT hrTemp = S_OK; - DEV_BROADCAST_HANDLE dev = { }; - BOOL fRedo = FALSE; - BOOL fHandleFound; - DWORD er = ERROR_SUCCESS; - DWORD dwIndex = 0; - HKEY hk = NULL; - HANDLE hTemp = INVALID_HANDLE_VALUE; - - if (pRequest->hNotify) - { - UnregisterDeviceNotification(pRequest->hNotify); - pRequest->hNotify = NULL; - } - - do - { - fRedo = FALSE; - fHandleFound = FALSE; - - for (DWORD i = 0; i < pRequest->cPathHierarchy && !fHandleFound; ++i) - { - dwIndex = pRequest->cPathHierarchy - i - 1; - switch (pRequest->type) - { - case MON_DIRECTORY: - if (INVALID_HANDLE_VALUE != *pHandle) - { - ::FindCloseChangeNotification(*pHandle); - *pHandle = INVALID_HANDLE_VALUE; - } - - *pHandle = ::FindFirstChangeNotificationW(pRequest->rgsczPathHierarchy[dwIndex], GetRecursiveFlag(pRequest, dwIndex), FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SECURITY); - if (INVALID_HANDLE_VALUE == *pHandle) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || E_ACCESSDENIED == hr) - { - continue; - } - MonExitOnWin32Error(er, hr, "Failed to wait on path %ls", pRequest->rgsczPathHierarchy[dwIndex]); - } - else - { - fHandleFound = TRUE; - hr = S_OK; - } - break; - case MON_REGKEY: - ReleaseRegKey(pRequest->regkey.hkSubKey); - hr = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex], KEY_NOTIFY | GetRegKeyBitness(pRequest), &pRequest->regkey.hkSubKey); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - continue; - } - MonExitOnFailure(hr, "Failed to open regkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); - - er = ::RegNotifyChangeKeyValue(pRequest->regkey.hkSubKey, GetRecursiveFlag(pRequest, dwIndex), REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY, *pHandle, TRUE); - ReleaseRegKey(hk); - hr = HRESULT_FROM_WIN32(er); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || HRESULT_FROM_WIN32(ERROR_KEY_DELETED) == hr) - { - continue; - } - else - { - MonExitOnWin32Error(er, hr, "Failed to wait on subkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); - - fHandleFound = TRUE; - } - - break; - default: - return E_INVALIDARG; - } - } - - pRequest->dwPathHierarchyIndex = dwIndex; - - // If we're monitoring a parent instead of the real path because the real path didn't exist, double-check the child hasn't been created since. - // If it has, restart the whole loop - if (dwIndex < pRequest->cPathHierarchy - 1) - { - switch (pRequest->type) - { - case MON_DIRECTORY: - hTemp = ::FindFirstChangeNotificationW(pRequest->rgsczPathHierarchy[dwIndex + 1], GetRecursiveFlag(pRequest, dwIndex + 1), FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SECURITY); - if (INVALID_HANDLE_VALUE != hTemp) - { - ::FindCloseChangeNotification(hTemp); - fRedo = TRUE; - } - break; - case MON_REGKEY: - hrTemp = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex + 1], KEY_NOTIFY | GetRegKeyBitness(pRequest), &hk); - ReleaseRegKey(hk); - fRedo = SUCCEEDED(hrTemp); - break; - default: - Assert(false); - } - } - } while (fRedo); - - MonExitOnFailure(hr, "Didn't get a successful wait after looping through all available options %ls", pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1]); - - if (MON_DIRECTORY == pRequest->type) - { - dev.dbch_size = sizeof(dev); - dev.dbch_devicetype = DBT_DEVTYP_HANDLE; - dev.dbch_handle = *pHandle; - // Ignore failure on this - some drives by design don't support it (like network paths), and the worst that can happen is a - // removable device will be left in use so user cannot gracefully remove - pRequest->hNotify = RegisterDeviceNotification(pRequest->hwnd, &dev, DEVICE_NOTIFY_WINDOW_HANDLE); - } - -LExit: - ReleaseRegKey(hk); - - return hr; -} - -static DWORD WINAPI WaiterThread( - __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - HRESULT hrTemp = S_OK; - DWORD dwRet = 0; - BOOL fAgain = FALSE; - BOOL fContinue = TRUE; - BOOL fNotify = FALSE; - BOOL fRet = FALSE; - MSG msg = { }; - MON_ADD_MESSAGE *pAddMessage = NULL; - MON_REMOVE_MESSAGE *pRemoveMessage = NULL; - MON_WAITER_CONTEXT *pWaiterContext = reinterpret_cast(pvContext); - DWORD dwRequestIndex; - DWORD dwNewRequestIndex; - // If we have one or more requests pending notification, this is the period we intend to wait for multiple objects (shortest amount of time to next potential notify) - DWORD dwWait = 0; - DWORD uCurrentTime = 0; - DWORD uLastTimeInMs = ::GetTickCount(); - DWORD uDeltaInMs = 0; - DWORD cRequestsPendingBeforeLoop = 0; - LPWSTR sczDirectory = NULL; - bool rgfProcessedIndex[MON_MAX_MONITORS_PER_THREAD + 1] = { }; - MON_INTERNAL_TEMPORARY_WAIT * pInternalWait = NULL; - - // Ensure the thread has a message queue - ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); - pWaiterContext->fWaiterThreadMessageQueueInitialized = TRUE; - - do - { - dwRet = ::WaitForMultipleObjects(pWaiterContext->cHandles - pWaiterContext->cRequestsFailing, pWaiterContext->rgHandles, FALSE, pWaiterContext->cRequestsPending > 0 ? dwWait : INFINITE); - - uCurrentTime = ::GetTickCount(); - uDeltaInMs = uCurrentTime - uLastTimeInMs; - uLastTimeInMs = uCurrentTime; - - if (WAIT_OBJECT_0 == dwRet) - { - do - { - fRet = ::PeekMessage(&msg, reinterpret_cast(-1), 0, 0, PM_REMOVE); - fAgain = fRet; - if (fRet) - { - switch (msg.message) - { - case MON_MESSAGE_ADD: - pAddMessage = reinterpret_cast(msg.wParam); - - // Don't just blindly put it at the end of the array - it must be before any failing requests - // for WaitForMultipleObjects() to succeed - dwNewRequestIndex = pWaiterContext->cRequests - pWaiterContext->cRequestsFailing; - if (FAILED(pAddMessage->request.hrStatus)) - { - ++pWaiterContext->cRequestsFailing; - } - - hr = MemInsertIntoArray(reinterpret_cast(&pWaiterContext->rgHandles), dwNewRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), MON_ARRAY_GROWTH); - MonExitOnFailure(hr, "Failed to insert additional handle"); - ++pWaiterContext->cHandles; - - // Ugh - directory types start with INVALID_HANDLE_VALUE instead of NULL - if (MON_DIRECTORY == pAddMessage->request.type) - { - pWaiterContext->rgHandles[dwNewRequestIndex + 1] = INVALID_HANDLE_VALUE; - } - - hr = MemInsertIntoArray(reinterpret_cast(&pWaiterContext->rgRequests), dwNewRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), MON_ARRAY_GROWTH); - MonExitOnFailure(hr, "Failed to insert additional request struct"); - ++pWaiterContext->cRequests; - - pWaiterContext->rgRequests[dwNewRequestIndex] = pAddMessage->request; - pWaiterContext->rgHandles[dwNewRequestIndex + 1] = pAddMessage->handle; - - ReleaseNullMem(pAddMessage); - break; - - case MON_MESSAGE_REMOVE: - pRemoveMessage = reinterpret_cast(msg.wParam); - - // Find the request to remove - hr = FindRequestIndex(pWaiterContext, pRemoveMessage, &dwRequestIndex); - if (E_NOTFOUND == hr) - { - // Coordinator sends removes blindly to all waiter threads, so maybe this one wasn't intended for us - hr = S_OK; - } - else - { - MonExitOnFailure(hr, "Failed to find request index for remove message"); - - hr = RemoveRequest(pWaiterContext, dwRequestIndex); - MonExitOnFailure(hr, "Failed to remove request after request from coordinator thread."); - } - - MonRemoveMessageDestroy(pRemoveMessage); - pRemoveMessage = NULL; - break; - - case MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS: - if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, PM_NOREMOVE)) - { - // If there is another a pending retry failed wait message, skip this one - continue; - } - - ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (rgfProcessedIndex[i]) - { - // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it - continue; - } - - if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && FAILED(pWaiterContext->rgRequests[i].hrStatus)) - { - // This is not a failure, just record this in the request's status - hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); - - hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - MonExitOnFailure(hr, "Failed to update wait status"); - hrTemp = S_OK; - - if (dwNewRequestIndex != i) - { - // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping - rgfProcessedIndex[dwNewRequestIndex] = true; - --i; - } - } - } - break; - - case MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS: - if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, PM_NOREMOVE)) - { - // If there is another a pending retry successful wait message, skip this one - continue; - } - - ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (rgfProcessedIndex[i]) - { - // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it - continue; - } - - if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && SUCCEEDED(pWaiterContext->rgRequests[i].hrStatus)) - { - // This is not a failure, just record this in the request's status - hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); - - hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - MonExitOnFailure(hr, "Failed to update wait status"); - hrTemp = S_OK; - - if (dwNewRequestIndex != i) - { - // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping - rgfProcessedIndex[dwNewRequestIndex] = true; - --i; - } - } - } - break; - - case MON_MESSAGE_NETWORK_STATUS_UPDATE: - if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_STATUS_UPDATE, MON_MESSAGE_NETWORK_STATUS_UPDATE, PM_NOREMOVE)) - { - // If there is another a pending network status update message, skip this one - continue; - } - - ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (rgfProcessedIndex[i]) - { - // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it - continue; - } - - if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork) - { - // Failures here get recorded in the request's status - hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); - - hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - MonExitOnFailure(hr, "Failed to update wait status"); - hrTemp = S_OK; - - if (dwNewRequestIndex != i) - { - // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping - rgfProcessedIndex[dwNewRequestIndex] = true; - --i; - } - } - } - break; - - case MON_MESSAGE_DRIVE_STATUS_UPDATE: - ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (rgfProcessedIndex[i]) - { - // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it - continue; - } - - if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].sczOriginalPathRequest[0] == static_cast(msg.wParam)) - { - // Failures here get recorded in the request's status - if (static_cast(msg.lParam)) - { - hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); - } - else - { - // If the message says the drive is disconnected, don't even try to wait, just mark it as gone - hrTemp = E_PATHNOTFOUND; - } - - hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - MonExitOnFailure(hr, "Failed to update wait status"); - hrTemp = S_OK; - - if (dwNewRequestIndex != i) - { - // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping - rgfProcessedIndex[dwNewRequestIndex] = true; - --i; - } - } - } - break; - - case MON_MESSAGE_DRIVE_QUERY_REMOVE: - pInternalWait = reinterpret_cast(msg.wParam); - // Only do any work if message is not yet out of date - // While it could become out of date while doing this processing, sending thread will check response to guard against this - if (pInternalWait->dwSendIteration == static_cast(msg.lParam)) - { - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgHandles[i + 1] == reinterpret_cast(pInternalWait->pvContext)) - { - // Release handles ASAP so the remove request will succeed - if (pWaiterContext->rgRequests[i].hNotify) - { - UnregisterDeviceNotification(pWaiterContext->rgRequests[i].hNotify); - pWaiterContext->rgRequests[i].hNotify = NULL; - } - ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]); - pWaiterContext->rgHandles[i + 1] = INVALID_HANDLE_VALUE; - - // Reply to unblock our reply to the remove request - pInternalWait->dwReceiveIteration = static_cast(msg.lParam); - if (!::SetEvent(pInternalWait->hWait)) - { - TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to set event to notify coordinator thread that removable device handle was released, this could be due to wndproc no longer waiting for waiter thread's response"); - } - - // Drive is disconnecting, don't even try to wait, just mark it as gone - hrTemp = E_PATHNOTFOUND; - - hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); - MonExitOnFailure(hr, "Failed to update wait status"); - hrTemp = S_OK; - break; - } - } - } - break; - - case MON_MESSAGE_STOP: - // Stop requested, so abort the whole thread - Trace(REPORT_DEBUG, "Waiter thread was told to stop"); - fAgain = FALSE; - fContinue = FALSE; - ExitFunction1(hr = static_cast(msg.wParam)); - - default: - Assert(false); - break; - } - } - } while (fAgain); - } - else if (dwRet > WAIT_OBJECT_0 && dwRet - WAIT_OBJECT_0 < pWaiterContext->cHandles) - { - // OK a handle fired - only notify if it's the actual target, and not just some parent waiting for the target child to exist - dwRequestIndex = dwRet - WAIT_OBJECT_0 - 1; - fNotify = (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1); - - // Initiate re-waits before we notify callback, to ensure we don't miss a single update - hrTemp = InitiateWait(pWaiterContext->rgRequests + dwRequestIndex, pWaiterContext->rgHandles + dwRequestIndex + 1); - hr = UpdateWaitStatus(hrTemp, pWaiterContext, dwRequestIndex, &dwRequestIndex); - MonExitOnFailure(hr, "Failed to update wait status"); - hrTemp = S_OK; - - // If there were no errors and we were already waiting on the right target, or if we weren't yet but are able to now, it's a successful notify - if (SUCCEEDED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus) && (fNotify || (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1))) - { - Trace(REPORT_DEBUG, "Changes detected, waiting for silence period index %u", dwRequestIndex); - - if (0 < pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs) - { - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs = 0; - pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = TRUE; - - if (!pWaiterContext->rgRequests[dwRequestIndex].fPendingFire) - { - pWaiterContext->rgRequests[dwRequestIndex].fPendingFire = TRUE; - ++pWaiterContext->cRequestsPending; - } - } - else - { - // If no silence period, notify immediately - Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex); - } - } - } - else if (WAIT_TIMEOUT != dwRet) - { - MonExitWithLastError(hr, "Failed to wait for multiple objects with return code %u", dwRet); - } - - // OK, now that we've checked all triggered handles (resetting silence period timers appropriately), check for any pending notifications that we can finally fire - // And set dwWait appropriately so we awaken at the right time to fire the next pending notification (in case no further writes occur during that time) - if (0 < pWaiterContext->cRequestsPending) - { - // Start at max value and find the lowest wait we can below that - dwWait = DWORD_MAX; - cRequestsPendingBeforeLoop = pWaiterContext->cRequestsPending; - - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (pWaiterContext->rgRequests[i].fPendingFire) - { - if (0 == cRequestsPendingBeforeLoop) - { - Assert(FALSE); - hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT); - MonExitOnFailure(hr, "Phantom pending fires were found!"); - } - --cRequestsPendingBeforeLoop; - - dwRequestIndex = i; - - if (pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd) - { - pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = FALSE; - } - else - { - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs += uDeltaInMs; - } - - // silence period has elapsed without further notifications, so reset pending-related variables, and finally fire a notify! - if (pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs >= pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs) - { - Trace(REPORT_DEBUG, "Silence period surpassed, notifying %u ms late", pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs); - Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex); - } - else - { - // set dwWait to the shortest interval period so that if no changes occur, WaitForMultipleObjects - // wakes the thread back up when it's time to fire the next pending notification - if (dwWait > pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs) - { - dwWait = pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs; - } - } - } - } - - // Some post-loop list validation for sanity checking - if (0 < cRequestsPendingBeforeLoop) - { - Assert(FALSE); - hr = HRESULT_FROM_WIN32(PEERDIST_ERROR_MISSING_DATA); - MonExitOnFailure(hr, "Missing %u pending fires! Total pending fires: %u, wait: %u", cRequestsPendingBeforeLoop, pWaiterContext->cRequestsPending, dwWait); - } - if (0 < pWaiterContext->cRequestsPending && DWORD_MAX == dwWait) - { - Assert(FALSE); - hr = HRESULT_FROM_WIN32(ERROR_CANT_WAIT); - MonExitOnFailure(hr, "Pending fires exist (%u), but wait was infinite", cRequestsPendingBeforeLoop); - } - } - } while (fContinue); - - // Don't bother firing pending notifications. We were told to stop monitoring, so client doesn't care. - -LExit: - ReleaseStr(sczDirectory); - MonAddMessageDestroy(pAddMessage); - MonRemoveMessageDestroy(pRemoveMessage); - - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - MonRequestDestroy(pWaiterContext->rgRequests + i); - - switch (pWaiterContext->rgRequests[i].type) - { - case MON_DIRECTORY: - if (INVALID_HANDLE_VALUE != pWaiterContext->rgHandles[i + 1]) - { - ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]); - } - break; - case MON_REGKEY: - ReleaseHandle(pWaiterContext->rgHandles[i + 1]); - break; - default: - Assert(false); - } - } - - if (FAILED(hr)) - { - // If waiter thread fails, notify general callback of an error - Assert(pWaiterContext->vpfMonGeneral); - pWaiterContext->vpfMonGeneral(hr, pWaiterContext->pvContext); - - // And tell coordinator to shut all other waiters down - if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0)) - { - TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to coordinator thread to stop (due to general failure)."); - } - } - - return hr; -} - -static void Notify( - __in HRESULT hr, - __in MON_WAITER_CONTEXT *pWaiterContext, - __in MON_REQUEST *pRequest - ) -{ - if (pRequest->fPendingFire) - { - --pWaiterContext->cRequestsPending; - } - - pRequest->fPendingFire = FALSE; - pRequest->fSkipDeltaAdd = FALSE; - pRequest->dwSilencePeriodInMs = 0; - - switch (pRequest->type) - { - case MON_DIRECTORY: - Assert(pWaiterContext->vpfMonDirectory); - pWaiterContext->vpfMonDirectory(hr, pRequest->sczOriginalPathRequest, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext); - break; - case MON_REGKEY: - Assert(pWaiterContext->vpfMonRegKey); - pWaiterContext->vpfMonRegKey(hr, pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1], pRequest->regkey.kbKeyBitness, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext); - break; - default: - Assert(false); - } -} - -static BOOL GetRecursiveFlag( - __in MON_REQUEST *pRequest, - __in DWORD dwIndex - ) -{ - if (pRequest->cPathHierarchy - 1 == dwIndex) - { - return pRequest->fRecursive; - } - else - { - return FALSE; - } -} - -static HRESULT FindRequestIndex( - __in MON_WAITER_CONTEXT *pWaiterContext, - __in MON_REMOVE_MESSAGE *pMessage, - __out DWORD *pdwIndex - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) - { - if (pWaiterContext->rgRequests[i].type == pMessage->type) - { - switch (pWaiterContext->rgRequests[i].type) - { - case MON_DIRECTORY: - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pWaiterContext->rgRequests[i].rgsczPathHierarchy[pWaiterContext->rgRequests[i].cPathHierarchy - 1], -1, pMessage->directory.sczDirectory, -1) && pWaiterContext->rgRequests[i].fRecursive == pMessage->fRecursive) - { - *pdwIndex = i; - ExitFunction1(hr = S_OK); - } - break; - case MON_REGKEY: - if (reinterpret_cast(pMessage->regkey.hkRoot) == reinterpret_cast(pWaiterContext->rgRequests[i].regkey.hkRoot) && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pWaiterContext->rgRequests[i].rgsczPathHierarchy[pWaiterContext->rgRequests[i].cPathHierarchy - 1], -1, pMessage->regkey.sczSubKey, -1) && pWaiterContext->rgRequests[i].fRecursive == pMessage->fRecursive && pWaiterContext->rgRequests[i].regkey.kbKeyBitness == pMessage->regkey.kbKeyBitness) - { - *pdwIndex = i; - ExitFunction1(hr = S_OK); - } - break; - default: - Assert(false); - } - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - -static HRESULT RemoveRequest( - __inout MON_WAITER_CONTEXT *pWaiterContext, - __in DWORD dwRequestIndex - ) -{ - HRESULT hr = S_OK; - - MonRequestDestroy(pWaiterContext->rgRequests + dwRequestIndex); - - switch (pWaiterContext->rgRequests[dwRequestIndex].type) - { - case MON_DIRECTORY: - if (pWaiterContext->rgHandles[dwRequestIndex + 1] != INVALID_HANDLE_VALUE) - { - ::FindCloseChangeNotification(pWaiterContext->rgHandles[dwRequestIndex + 1]); - } - break; - case MON_REGKEY: - ReleaseHandle(pWaiterContext->rgHandles[dwRequestIndex + 1]); - break; - default: - Assert(false); - } - - if (pWaiterContext->rgRequests[dwRequestIndex].fPendingFire) - { - --pWaiterContext->cRequestsPending; - } - - if (FAILED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus)) - { - --pWaiterContext->cRequestsFailing; - } - - MemRemoveFromArray(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), TRUE); - --pWaiterContext->cHandles; - MemRemoveFromArray(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), TRUE); - --pWaiterContext->cRequests; - - // Notify coordinator thread that a wait was removed - if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_REMOVED, static_cast(::GetCurrentThreadId()), 0)) - { - MonExitWithLastError(hr, "Failed to send message to coordinator thread to confirm directory was removed."); - } - -LExit: - return hr; -} - -static REGSAM GetRegKeyBitness( - __in MON_REQUEST *pRequest - ) -{ - if (REG_KEY_32BIT == pRequest->regkey.kbKeyBitness) - { - return KEY_WOW64_32KEY; - } - else if (REG_KEY_64BIT == pRequest->regkey.kbKeyBitness) - { - return KEY_WOW64_64KEY; - } - else - { - return 0; - } -} - -static HRESULT DuplicateRemoveMessage( - __in MON_REMOVE_MESSAGE *pMessage, - __out MON_REMOVE_MESSAGE **ppMessage - ) -{ - HRESULT hr = S_OK; - - *ppMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); - MonExitOnNull(*ppMessage, hr, E_OUTOFMEMORY, "Failed to allocate copy of remove message"); - - (*ppMessage)->type = pMessage->type; - (*ppMessage)->fRecursive = pMessage->fRecursive; - - switch (pMessage->type) - { - case MON_DIRECTORY: - hr = StrAllocString(&(*ppMessage)->directory.sczDirectory, pMessage->directory.sczDirectory, 0); - MonExitOnFailure(hr, "Failed to copy directory"); - break; - case MON_REGKEY: - (*ppMessage)->regkey.hkRoot = pMessage->regkey.hkRoot; - (*ppMessage)->regkey.kbKeyBitness = pMessage->regkey.kbKeyBitness; - hr = StrAllocString(&(*ppMessage)->regkey.sczSubKey, pMessage->regkey.sczSubKey, 0); - MonExitOnFailure(hr, "Failed to copy subkey"); - break; - default: - Assert(false); - break; - } - -LExit: - return hr; -} - -static LRESULT CALLBACK MonWndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - HRESULT hr = S_OK; - DEV_BROADCAST_HDR *pHdr = NULL; - DEV_BROADCAST_HANDLE *pHandle = NULL; - DEV_BROADCAST_VOLUME *pVolume = NULL; - DWORD dwUnitMask = 0; - DWORD er = ERROR_SUCCESS; - WCHAR chDrive = L'\0'; - BOOL fArrival = FALSE; - BOOL fReturnTrue = FALSE; - CREATESTRUCT *pCreateStruct = NULL; - MON_WAITER_CONTEXT *pWaiterContext = NULL; - MON_STRUCT *pm = NULL; - - // keep track of the MON_STRUCT pointer that was passed in on init, associate it with the window - if (WM_CREATE == uMsg) - { - pCreateStruct = reinterpret_cast(lParam); - if (pCreateStruct) - { - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pCreateStruct->lpCreateParams)); - } - } - else if (WM_NCDESTROY == uMsg) - { - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); - } - - // Note this message ONLY comes in through WndProc, it isn't visible from the GetMessage loop. - else if (WM_DEVICECHANGE == uMsg) - { - if (DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam) - { - fArrival = DBT_DEVICEARRIVAL == wParam; - - pHdr = reinterpret_cast(lParam); - if (DBT_DEVTYP_VOLUME == pHdr->dbch_devicetype) - { - pVolume = reinterpret_cast(lParam); - dwUnitMask = pVolume->dbcv_unitmask; - chDrive = L'a'; - while (0 < dwUnitMask) - { - if (dwUnitMask & 0x1) - { - // This drive had a status update, so send it out to all threads - if (!::PostThreadMessageW(::GetCurrentThreadId(), MON_MESSAGE_DRIVE_STATUS_UPDATE, static_cast(chDrive), static_cast(fArrival))) - { - MonExitWithLastError(hr, "Failed to send drive status update with drive %wc and arrival %ls", chDrive, fArrival ? L"TRUE" : L"FALSE"); - } - } - dwUnitMask >>= 1; - ++chDrive; - - if (chDrive == 'z') - { - hr = E_UNEXPECTED; - MonExitOnFailure(hr, "UnitMask showed drives beyond z:. Remaining UnitMask at this point: %u", dwUnitMask); - } - } - } - } - // We can only process device query remove messages if we have a MON_STRUCT pointer - else if (DBT_DEVICEQUERYREMOVE == wParam) - { - pm = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); - if (!pm) - { - hr = E_POINTER; - MonExitOnFailure(hr, "DBT_DEVICEQUERYREMOVE message received with no MON_STRUCT pointer, so message was ignored"); - } - - fReturnTrue = TRUE; - - pHdr = reinterpret_cast(lParam); - if (DBT_DEVTYP_HANDLE == pHdr->dbch_devicetype) - { - // We must wait for the actual wait handle to be released by waiter thread before telling windows to proceed with device removal, otherwise it could fail - // due to handles still being open, so use a MON_INTERNAL_TEMPORARY_WAIT struct to send and receive a reply from a waiter thread - pm->internalWait.hWait = ::CreateEventW(NULL, TRUE, FALSE, NULL); - MonExitOnNullWithLastError(pm->internalWait.hWait, hr, "Failed to create anonymous event for waiter to notify wndproc device can be removed"); - - pHandle = reinterpret_cast(lParam); - pm->internalWait.pvContext = pHandle->dbch_handle; - pm->internalWait.dwReceiveIteration = pm->internalWait.dwSendIteration - 1; - // This drive had a status update, so send it out to all threads - for (DWORD i = 0; i < pm->cWaiterThreads; ++i) - { - pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; - - if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_QUERY_REMOVE, reinterpret_cast(&pm->internalWait), static_cast(pm->internalWait.dwSendIteration))) - { - MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive query remove"); - } - - if (!::SetEvent(pWaiterContext->rgHandles[0])) - { - MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive query remove message"); - } - } - - er = ::WaitForSingleObject(pm->internalWait.hWait, MON_THREAD_WAIT_REMOVE_DEVICE); - // Make sure any waiter thread processing really old messages can immediately know that we're no longer waiting for a response - if (WAIT_OBJECT_0 == er) - { - // If the response ID matches what we sent, we actually got a valid reply! - if (pm->internalWait.dwReceiveIteration != pm->internalWait.dwSendIteration) - { - TraceError(HRESULT_FROM_WIN32(er), "Waiter thread received wrong ID reply"); - } - } - else if (WAIT_TIMEOUT == er) - { - TraceError(HRESULT_FROM_WIN32(er), "No response from any waiter thread for query remove message"); - } - else - { - MonExitWithLastError(hr, "WaitForSingleObject failed with non-timeout reason while waiting for response from waiter thread"); - } - ++pm->internalWait.dwSendIteration; - } - } - } - -LExit: - if (pm) - { - ReleaseHandle(pm->internalWait.hWait); - } - - if (fReturnTrue) - { - return TRUE; - } - else - { - return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); - } -} - -static HRESULT CreateMonWindow( - __in MON_STRUCT *pm, - __out HWND *pHwnd - ) -{ - HRESULT hr = S_OK; - WNDCLASSW wc = { }; - - wc.lpfnWndProc = MonWndProc; - wc.hInstance = ::GetModuleHandleW(NULL); - wc.lpszClassName = MONUTIL_WINDOW_CLASS; - if (!::RegisterClassW(&wc)) - { - if (ERROR_CLASS_ALREADY_EXISTS != ::GetLastError()) - { - MonExitWithLastError(hr, "Failed to register MonUtil window class."); - } - } - - *pHwnd = ::CreateWindowExW(0, wc.lpszClassName, L"", 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, HWND_DESKTOP, NULL, wc.hInstance, pm); - MonExitOnNullWithLastError(*pHwnd, hr, "Failed to create window."); - - // Rumor has it that drive arrival / removal events can be lost in the rare event that some other application higher up in z-order is hanging if we don't make our window topmost - // SWP_NOACTIVATE is important so the currently active window doesn't lose focus - SetWindowPos(*pHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_DEFERERASE | SWP_NOACTIVATE); - -LExit: - return hr; -} - -static HRESULT WaitForNetworkChanges( - __inout HANDLE *phMonitor, - __in MON_STRUCT *pm - ) -{ - HRESULT hr = S_OK; - int nResult = 0; - DWORD dwBytesReturned = 0; - WSACOMPLETION wsaCompletion = { }; - WSAQUERYSET qsRestrictions = { }; - - qsRestrictions.dwSize = sizeof(WSAQUERYSET); - qsRestrictions.dwNameSpace = NS_NLA; - - if (NULL != *phMonitor) - { - ::WSALookupServiceEnd(*phMonitor); - *phMonitor = NULL; - } - - if (::WSALookupServiceBegin(&qsRestrictions, LUP_RETURN_ALL, phMonitor)) - { - hr = HRESULT_FROM_WIN32(::WSAGetLastError()); - MonExitOnFailure(hr, "WSALookupServiceBegin() failed"); - } - - wsaCompletion.Type = NSP_NOTIFY_HWND; - wsaCompletion.Parameters.WindowMessage.hWnd = pm->hwnd; - wsaCompletion.Parameters.WindowMessage.uMsg = MON_MESSAGE_NETWORK_STATUS_UPDATE; - nResult = ::WSANSPIoctl(*phMonitor, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &dwBytesReturned, &wsaCompletion); - if (SOCKET_ERROR != nResult || WSA_IO_PENDING != ::WSAGetLastError()) - { - hr = HRESULT_FROM_WIN32(::WSAGetLastError()); - if (SUCCEEDED(hr)) - { - hr = E_FAIL; - } - MonExitOnFailure(hr, "WSANSPIoctl() failed with return code %i, wsa last error %u", nResult, ::WSAGetLastError()); - } - -LExit: - return hr; -} - -static HRESULT UpdateWaitStatus( - __in HRESULT hrNewStatus, - __inout MON_WAITER_CONTEXT *pWaiterContext, - __in DWORD dwRequestIndex, - __out_opt DWORD *pdwNewRequestIndex - ) -{ - HRESULT hr = S_OK; - DWORD dwNewRequestIndex; - MON_REQUEST *pRequest = pWaiterContext->rgRequests + dwRequestIndex; - - if (NULL != pdwNewRequestIndex) - { - *pdwNewRequestIndex = dwRequestIndex; - } - - if (SUCCEEDED(pRequest->hrStatus) || SUCCEEDED(hrNewStatus)) - { - // If it's a network wait, notify as long as it's new status is successful because we *may* have lost some changes - // before the wait was re-initiated. Otherwise, only notify if there was an interesting status change - if (SUCCEEDED(pRequest->hrStatus) != SUCCEEDED(hrNewStatus) || (pRequest->fNetwork && SUCCEEDED(hrNewStatus))) - { - Notify(hrNewStatus, pWaiterContext, pRequest); - } - - if (SUCCEEDED(pRequest->hrStatus) && FAILED(hrNewStatus)) - { - // If it's a network wait, notify coordinator thread that a network wait is failing - if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_FAILED, 0, 0)) - { - MonExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait started to fail"); - } - - // Move the failing wait to the end of the list of waits and increment cRequestsFailing so WaitForMultipleObjects isn't passed an invalid handle - ++pWaiterContext->cRequestsFailing; - dwNewRequestIndex = pWaiterContext->cRequests - 1; - MemArraySwapItems(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles)); - MemArraySwapItems(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests)); - // Reset pRequest to the newly swapped item - pRequest = pWaiterContext->rgRequests + dwNewRequestIndex; - if (NULL != pdwNewRequestIndex) - { - *pdwNewRequestIndex = dwNewRequestIndex; - } - } - else if (FAILED(pRequest->hrStatus) && SUCCEEDED(hrNewStatus)) - { - Assert(pWaiterContext->cRequestsFailing > 0); - // If it's a network wait, notify coordinator thread that a network wait is succeeding again - if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_SUCCEEDED, 0, 0)) - { - MonExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait is succeeding again"); - } - - --pWaiterContext->cRequestsFailing; - dwNewRequestIndex = 0; - MemArraySwapItems(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles)); - MemArraySwapItems(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests)); - // Reset pRequest to the newly swapped item - pRequest = pWaiterContext->rgRequests + dwNewRequestIndex; - if (NULL != pdwNewRequestIndex) - { - *pdwNewRequestIndex = dwNewRequestIndex; - } - } - } - - pRequest->hrStatus = hrNewStatus; - -LExit: - return hr; -} diff --git a/src/dutil/osutil.cpp b/src/dutil/osutil.cpp deleted file mode 100644 index 880ec3ea..00000000 --- a/src/dutil/osutil.cpp +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define OsExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) -#define OsExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) -#define OsExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) -#define OsExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) -#define OsExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) -#define OsExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) -#define OsExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__) -#define OsExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__) -#define OsExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__) -#define OsExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__) -#define OsExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_OSUTIL, e, x, s, __VA_ARGS__) -#define OsExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_OSUTIL, g, x, s, __VA_ARGS__) - -typedef NTSTATUS(NTAPI* PFN_RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation); - -OS_VERSION vOsVersion = OS_VERSION_UNKNOWN; -DWORD vdwOsServicePack = 0; -RTL_OSVERSIONINFOEXW vovix = { }; - -/******************************************************************** - OsGetVersion - -********************************************************************/ -extern "C" void DAPI OsGetVersion( - __out OS_VERSION* pVersion, - __out DWORD* pdwServicePack - ) -{ - OSVERSIONINFOEXW ovi = { }; - - if (OS_VERSION_UNKNOWN == vOsVersion) - { - ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); - -#pragma warning (push) -#pragma warning(suppress: 4996) // deprecated -#pragma warning (push) -#pragma warning(suppress: 28159)// deprecated, use other function instead - ::GetVersionExW(reinterpret_cast(&ovi)); // only fails if version info size is set incorrectly. -#pragma warning (pop) -#pragma warning (pop) - - vdwOsServicePack = static_cast(ovi.wServicePackMajor) << 16 | ovi.wServicePackMinor; - if (4 == ovi.dwMajorVersion) - { - vOsVersion = OS_VERSION_WINNT; - } - else if (5 == ovi.dwMajorVersion) - { - if (0 == ovi.dwMinorVersion) - { - vOsVersion = OS_VERSION_WIN2000; - } - else if (1 == ovi.dwMinorVersion) - { - vOsVersion = OS_VERSION_WINXP; - } - else if (2 == ovi.dwMinorVersion) - { - vOsVersion = OS_VERSION_WIN2003; - } - else - { - vOsVersion = OS_VERSION_FUTURE; - } - } - else if (6 == ovi.dwMajorVersion) - { - if (0 == ovi.dwMinorVersion) - { - vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_VISTA : OS_VERSION_WIN2008; - } - else if (1 == ovi.dwMinorVersion) - { - vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_WIN7 : OS_VERSION_WIN2008_R2; - } - else - { - vOsVersion = OS_VERSION_FUTURE; - } - } - else - { - vOsVersion = OS_VERSION_FUTURE; - } - } - - *pVersion = vOsVersion; - *pdwServicePack = vdwOsServicePack; -} - -extern "C" HRESULT DAPI OsCouldRunPrivileged( - __out BOOL* pfPrivileged - ) -{ - HRESULT hr = S_OK; - BOOL fUacEnabled = FALSE; - SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; - PSID AdministratorsGroup = NULL; - - // Do a best effort check to see if UAC is enabled on this machine. - OsIsUacEnabled(&fUacEnabled); - - // If UAC is enabled then the process could run privileged by asking to elevate. - if (fUacEnabled) - { - *pfPrivileged = TRUE; - } - else // no UAC so only privilged if user is in administrators group. - { - *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); - if (*pfPrivileged) - { - if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged)) - { - *pfPrivileged = FALSE; - } - } - } - - ReleaseSid(AdministratorsGroup); - return hr; -} - -extern "C" HRESULT DAPI OsIsRunningPrivileged( - __out BOOL* pfPrivileged - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - HANDLE hToken = NULL; - TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeDefault; - DWORD dwSize = 0; - SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; - PSID AdministratorsGroup = NULL; - - if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken)) - { - OsExitOnLastError(hr, "Failed to open process token."); - } - - if (::GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) - { - *pfPrivileged = (TokenElevationTypeFull == elevationType); - ExitFunction1(hr = S_OK); - } - - // If it's invalid argument, this means they don't support TokenElevationType, and we should fallback to another check - er = ::GetLastError(); - if (ERROR_INVALID_FUNCTION == er) - { - er = ERROR_SUCCESS; - } - OsExitOnWin32Error(er, hr, "Failed to get process token information."); - - // Fallback to this check for some OS's (like XP) - *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); - if (*pfPrivileged) - { - if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged)) - { - *pfPrivileged = FALSE; - } - } - -LExit: - ReleaseSid(AdministratorsGroup); - - if (hToken) - { - ::CloseHandle(hToken); - } - - return hr; -} - -extern "C" HRESULT DAPI OsIsUacEnabled( - __out BOOL* pfUacEnabled - ) -{ - HRESULT hr = S_OK; - HKEY hk = NULL; - DWORD dwUacEnabled = 0; - - *pfUacEnabled = FALSE; // assume UAC not enabled. - - hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ, &hk); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - OsExitOnFailure(hr, "Failed to open system policy key to detect UAC."); - - hr = RegReadNumber(hk, L"EnableLUA", &dwUacEnabled); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - OsExitOnFailure(hr, "Failed to read registry value to detect UAC."); - - *pfUacEnabled = (0 != dwUacEnabled); - -LExit: - ReleaseRegKey(hk); - - return hr; -} - -HRESULT DAPI OsRtlGetVersion( - __inout RTL_OSVERSIONINFOEXW* pOvix - ) -{ - HRESULT hr = S_OK; - HMODULE hNtdll = NULL; - PFN_RTL_GET_VERSION pfnRtlGetVersion = NULL; - - if (vovix.dwOSVersionInfoSize) - { - ExitFunction(); - } - - vovix.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); - - hr = LoadSystemLibrary(L"ntdll.dll", &hNtdll); - if (E_MODNOTFOUND == hr) - { - OsExitOnRootFailure(hr = E_NOTIMPL, "Failed to load ntdll.dll"); - } - OsExitOnFailure(hr, "Failed to load ntdll.dll."); - - pfnRtlGetVersion = reinterpret_cast(::GetProcAddress(hNtdll, "RtlGetVersion")); - OsExitOnNullWithLastError(pfnRtlGetVersion, hr, "Failed to locate RtlGetVersion."); - - hr = static_cast(pfnRtlGetVersion(&vovix)); - -LExit: - memcpy(pOvix, &vovix, sizeof(RTL_OSVERSIONINFOEXW)); - - if (hNtdll) - { - ::FreeLibrary(hNtdll); - } - - return hr; -} diff --git a/src/dutil/packages.config b/src/dutil/packages.config deleted file mode 100644 index 5bbcd994..00000000 --- a/src/dutil/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/dutil/path2utl.cpp b/src/dutil/path2utl.cpp deleted file mode 100644 index ff3a946d..00000000 --- a/src/dutil/path2utl.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) -#define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) -#define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) -#define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) -#define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__) -#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__) - - -DAPI_(HRESULT) PathCanonicalizePath( - __in_z LPCWSTR wzPath, - __deref_out_z LPWSTR* psczCanonicalized - ) -{ - HRESULT hr = S_OK; - int cch = MAX_PATH + 1; - - hr = StrAlloc(psczCanonicalized, cch); - PathExitOnFailure(hr, "Failed to allocate string for the canonicalized path."); - - if (::PathCanonicalizeW(*psczCanonicalized, wzPath)) - { - hr = S_OK; - } - else - { - ExitFunctionWithLastError(hr); - } - -LExit: - return hr; -} - -DAPI_(HRESULT) PathDirectoryContainsPath( - __in_z LPCWSTR wzDirectory, - __in_z LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPath = NULL; - LPWSTR sczDirectory = NULL; - LPWSTR sczOriginalPath = NULL; - LPWSTR sczOriginalDirectory = NULL; - - hr = PathCanonicalizePath(wzPath, &sczOriginalPath); - PathExitOnFailure(hr, "Failed to canonicalize the path."); - - hr = PathCanonicalizePath(wzDirectory, &sczOriginalDirectory); - PathExitOnFailure(hr, "Failed to canonicalize the directory."); - - if (!sczOriginalPath || !*sczOriginalPath) - { - ExitFunction1(hr = S_FALSE); - } - if (!sczOriginalDirectory || !*sczOriginalDirectory) - { - ExitFunction1(hr = S_FALSE); - } - - sczPath = sczOriginalPath; - sczDirectory = sczOriginalDirectory; - - for (; *sczDirectory;) - { - if (!*sczPath) - { - ExitFunction1(hr = S_FALSE); - } - - if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczDirectory, 1, sczPath, 1)) - { - ExitFunction1(hr = S_FALSE); - } - - ++sczDirectory; - ++sczPath; - } - - --sczDirectory; - if (('\\' == *sczDirectory && *sczPath) || '\\' == *sczPath) - { - hr = S_OK; - } - else - { - hr = S_FALSE; - } - -LExit: - ReleaseStr(sczOriginalPath); - ReleaseStr(sczOriginalDirectory); - return hr; -} diff --git a/src/dutil/pathutil.cpp b/src/dutil/pathutil.cpp deleted file mode 100644 index 7c3cfe06..00000000 --- a/src/dutil/pathutil.cpp +++ /dev/null @@ -1,1083 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) -#define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) -#define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) -#define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) -#define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) -#define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__) -#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__) - -#define PATH_GOOD_ENOUGH 64 - - -DAPI_(HRESULT) PathCommandLineAppend( - __deref_inout_z LPWSTR* psczCommandLine, - __in_z LPCWSTR wzArgument - ) -{ - HRESULT hr = S_OK; - LPWSTR sczQuotedArg = NULL; - BOOL fRequiresQuoting = FALSE; - DWORD dwMaxEscapedSize = 0; - - // Loop through the argument determining if it needs to be quoted and what the maximum - // size would be if there are escape characters required. - for (LPCWSTR pwz = wzArgument; *pwz; ++pwz) - { - // Arguments with whitespace need quoting. - if (L' ' == *pwz || L'\t' == *pwz || L'\n' == *pwz || L'\v' == *pwz) - { - fRequiresQuoting = TRUE; - } - else if (L'"' == *pwz) // quotes need quoting and sometimes escaping. - { - fRequiresQuoting = TRUE; - ++dwMaxEscapedSize; - } - else if (L'\\' == *pwz) // some backslashes need escaping, so we'll count them all to make sure there is room. - { - ++dwMaxEscapedSize; - } - - ++dwMaxEscapedSize; - } - - // If we found anything in the argument that requires our argument to be quoted - if (fRequiresQuoting) - { - hr = StrAlloc(&sczQuotedArg, dwMaxEscapedSize + 3); // plus three for the start and end quote plus null terminator. - PathExitOnFailure(hr, "Failed to allocate argument to be quoted."); - - LPCWSTR pwz = wzArgument; - LPWSTR pwzQuoted = sczQuotedArg; - - *pwzQuoted = L'"'; - ++pwzQuoted; - while (*pwz) - { - DWORD dwBackslashes = 0; - while (L'\\' == *pwz) - { - ++dwBackslashes; - ++pwz; - } - - // Escape all backslashes at the end of the string. - if (!*pwz) - { - dwBackslashes *= 2; - } - else if (L'"' == *pwz) // escape all backslashes before the quote and escape the quote itself. - { - dwBackslashes = dwBackslashes * 2 + 1; - } - // the backslashes don't have to be escaped. - - // Add the appropriate number of backslashes - for (DWORD i = 0; i < dwBackslashes; ++i) - { - *pwzQuoted = L'\\'; - ++pwzQuoted; - } - - // If there is a character, add it after all the escaped backslashes - if (*pwz) - { - *pwzQuoted = *pwz; - ++pwz; - ++pwzQuoted; - } - } - - *pwzQuoted = L'"'; - ++pwzQuoted; - *pwzQuoted = L'\0'; // ensure the arg is null terminated. - } - - // If there is already data in the command line, append a space before appending the - // argument. - if (*psczCommandLine && **psczCommandLine) - { - hr = StrAllocConcat(psczCommandLine, L" ", 0); - PathExitOnFailure(hr, "Failed to append space to command line with existing data."); - } - - hr = StrAllocConcat(psczCommandLine, sczQuotedArg ? sczQuotedArg : wzArgument, 0); - PathExitOnFailure(hr, "Failed to copy command line argument."); - -LExit: - ReleaseStr(sczQuotedArg); - - return hr; -} - - -DAPI_(LPWSTR) PathFile( - __in_z LPCWSTR wzPath - ) -{ - if (!wzPath) - { - return NULL; - } - - LPWSTR wzFile = const_cast(wzPath); - for (LPWSTR wz = wzFile; *wz; ++wz) - { - // valid delineators - // \ => Windows path - // / => unix and URL path - // : => relative path from mapped root - if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) - { - wzFile = wz + 1; - } - } - - return wzFile; -} - - -DAPI_(LPCWSTR) PathExtension( - __in_z LPCWSTR wzPath - ) -{ - if (!wzPath) - { - return NULL; - } - - // Find the last dot in the last thing that could be a file. - LPCWSTR wzExtension = NULL; - for (LPCWSTR wz = wzPath; *wz; ++wz) - { - if (L'\\' == *wz || L'/' == *wz || L':' == *wz) - { - wzExtension = NULL; - } - else if (L'.' == *wz) - { - wzExtension = wz; - } - } - - return wzExtension; -} - - -DAPI_(HRESULT) PathGetDirectory( - __in_z LPCWSTR wzPath, - __out_z LPWSTR *psczDirectory - ) -{ - HRESULT hr = S_OK; - size_t cchDirectory = SIZE_T_MAX; - - for (LPCWSTR wz = wzPath; *wz; ++wz) - { - // valid delineators: - // \ => Windows path - // / => unix and URL path - // : => relative path from mapped root - if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) - { - cchDirectory = static_cast(wz - wzPath) + 1; - } - } - - if (SIZE_T_MAX == cchDirectory) - { - // we were given just a file name, so there's no directory available - return S_FALSE; - } - - if (wzPath[0] == L'\"') - { - ++wzPath; - --cchDirectory; - } - - hr = StrAllocString(psczDirectory, wzPath, cchDirectory); - PathExitOnFailure(hr, "Failed to copy directory."); - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathGetParentPath( - __in_z LPCWSTR wzPath, - __out_z LPWSTR *psczParent - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzParent = NULL; - - for (LPCWSTR wz = wzPath; *wz; ++wz) - { - if (wz[1] && (L'\\' == *wz || L'/' == *wz)) - { - wzParent = wz; - } - } - - if (wzParent) - { - size_t cchPath = static_cast(wzParent - wzPath) + 1; - - hr = StrAllocString(psczParent, wzPath, cchPath); - PathExitOnFailure(hr, "Failed to copy directory."); - } - else - { - ReleaseNullStr(psczParent); - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathExpand( - __out LPWSTR *psczFullPath, - __in_z LPCWSTR wzRelativePath, - __in DWORD dwResolveFlags - ) -{ - Assert(wzRelativePath && *wzRelativePath); - - HRESULT hr = S_OK; - DWORD cch = 0; - LPWSTR sczExpandedPath = NULL; - DWORD cchExpandedPath = 0; - SIZE_T cbSize = 0; - - LPWSTR sczFullPath = NULL; - - // - // First, expand any environment variables. - // - if (dwResolveFlags & PATH_EXPAND_ENVIRONMENT) - { - cchExpandedPath = PATH_GOOD_ENOUGH; - - hr = StrAlloc(&sczExpandedPath, cchExpandedPath); - PathExitOnFailure(hr, "Failed to allocate space for expanded path."); - - cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); - if (0 == cch) - { - PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); - } - else if (cchExpandedPath < cch) - { - cchExpandedPath = cch; - hr = StrAlloc(&sczExpandedPath, cchExpandedPath); - PathExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); - - cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); - if (0 == cch) - { - PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); - } - else if (cchExpandedPath < cch) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - PathExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); - } - } - - if (MAX_PATH < cch) - { - hr = PathPrefix(&sczExpandedPath); // ignore invald arg from path prefix because this may not be a complete path yet - if (E_INVALIDARG == hr) - { - hr = S_OK; - } - PathExitOnFailure(hr, "Failed to prefix long path after expanding environment variables."); - - hr = StrMaxLength(sczExpandedPath, &cbSize); - PathExitOnFailure(hr, "Failed to get max length of expanded path."); - - cchExpandedPath = (DWORD)min(DWORD_MAX, cbSize); - } - } - - // - // Second, get the full path. - // - if (dwResolveFlags & PATH_EXPAND_FULLPATH) - { - LPWSTR wzFileName = NULL; - LPCWSTR wzPath = sczExpandedPath ? sczExpandedPath : wzRelativePath; - DWORD cchFullPath = max(PATH_GOOD_ENOUGH, cchExpandedPath); - - hr = StrAlloc(&sczFullPath, cchFullPath); - PathExitOnFailure(hr, "Failed to allocate space for full path."); - - cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); - if (0 == cch) - { - PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); - } - else if (cchFullPath < cch) - { - cchFullPath = cch < MAX_PATH ? cch : cch + 7; // ensure space for "\\?\UNC" prefix if needed - hr = StrAlloc(&sczFullPath, cchFullPath); - PathExitOnFailure(hr, "Failed to re-allocate more space for full path."); - - cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); - if (0 == cch) - { - PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); - } - else if (cchFullPath < cch) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - PathExitOnRootFailure(hr, "Failed to allocate buffer for full path."); - } - } - - if (MAX_PATH < cch) - { - hr = PathPrefix(&sczFullPath); - PathExitOnFailure(hr, "Failed to prefix long path after expanding."); - } - } - else - { - sczFullPath = sczExpandedPath; - sczExpandedPath = NULL; - } - - hr = StrAllocString(psczFullPath, sczFullPath? sczFullPath : wzRelativePath, 0); - PathExitOnFailure(hr, "Failed to copy relative path into full path."); - -LExit: - ReleaseStr(sczFullPath); - ReleaseStr(sczExpandedPath); - - return hr; -} - - -DAPI_(HRESULT) PathPrefix( - __inout LPWSTR *psczFullPath - ) -{ - Assert(psczFullPath && *psczFullPath); - - HRESULT hr = S_OK; - LPWSTR wzFullPath = *psczFullPath; - SIZE_T cbFullPath = 0; - - if (((L'a' <= wzFullPath[0] && L'z' >= wzFullPath[0]) || - (L'A' <= wzFullPath[0] && L'Z' >= wzFullPath[0])) && - L':' == wzFullPath[1] && - L'\\' == wzFullPath[2]) // normal path - { - hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); - PathExitOnFailure(hr, "Failed to add prefix to file path."); - } - else if (L'\\' == wzFullPath[0] && L'\\' == wzFullPath[1]) // UNC - { - // ensure that we're not already prefixed - if (!(L'?' == wzFullPath[2] && L'\\' == wzFullPath[3])) - { - hr = StrSize(*psczFullPath, &cbFullPath); - PathExitOnFailure(hr, "Failed to get size of full path."); - - memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR)); - - hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); - PathExitOnFailure(hr, "Failed to add prefix to UNC path."); - } - } - else - { - hr = E_INVALIDARG; - PathExitOnFailure(hr, "Invalid path provided to prefix: %ls.", wzFullPath); - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathFixedBackslashTerminate( - __inout_ecount_z(cchPath) LPWSTR wzPath, - __in SIZE_T cchPath - ) -{ - HRESULT hr = S_OK; - size_t cchLength = 0; - - hr = ::StringCchLengthW(wzPath, cchPath, &cchLength); - PathExitOnFailure(hr, "Failed to get length of path."); - - if (cchLength >= cchPath) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - } - else if (L'\\' != wzPath[cchLength - 1]) - { - wzPath[cchLength] = L'\\'; - wzPath[cchLength + 1] = L'\0'; - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathBackslashTerminate( - __inout LPWSTR* psczPath - ) -{ - Assert(psczPath && *psczPath); - - HRESULT hr = S_OK; - SIZE_T cchPath = 0; - size_t cchLength = 0; - - hr = StrMaxLength(*psczPath, &cchPath); - PathExitOnFailure(hr, "Failed to get size of path string."); - - hr = ::StringCchLengthW(*psczPath, cchPath, &cchLength); - PathExitOnFailure(hr, "Failed to get length of path."); - - if (L'\\' != (*psczPath)[cchLength - 1]) - { - hr = StrAllocConcat(psczPath, L"\\", 1); - PathExitOnFailure(hr, "Failed to concat backslash onto string."); - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathForCurrentProcess( - __inout LPWSTR *psczFullPath, - __in_opt HMODULE hModule - ) -{ - HRESULT hr = S_OK; - DWORD cch = MAX_PATH; - - do - { - hr = StrAlloc(psczFullPath, cch); - PathExitOnFailure(hr, "Failed to allocate string for module path."); - - DWORD cchRequired = ::GetModuleFileNameW(hModule, *psczFullPath, cch); - if (0 == cchRequired) - { - PathExitWithLastError(hr, "Failed to get path for executing process."); - } - else if (cchRequired == cch) - { - cch = cchRequired + 1; - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - } - else - { - hr = S_OK; - } - } while (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr); - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathRelativeToModule( - __inout LPWSTR *psczFullPath, - __in_opt LPCWSTR wzFileName, - __in_opt HMODULE hModule - ) -{ - HRESULT hr = PathForCurrentProcess(psczFullPath, hModule); - PathExitOnFailure(hr, "Failed to get current module path."); - - hr = PathGetDirectory(*psczFullPath, psczFullPath); - PathExitOnFailure(hr, "Failed to get current module directory."); - - if (wzFileName) - { - hr = PathConcat(*psczFullPath, wzFileName, psczFullPath); - PathExitOnFailure(hr, "Failed to append filename."); - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathCreateTempFile( - __in_opt LPCWSTR wzDirectory, - __in_opt __format_string LPCWSTR wzFileNameTemplate, - __in DWORD dwUniqueCount, - __in DWORD dwFileAttributes, - __out_opt LPWSTR* psczTempFile, - __out_opt HANDLE* phTempFile - ) -{ - AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); - - HRESULT hr = S_OK; - - LPWSTR sczTempPath = NULL; - DWORD cchTempPath = MAX_PATH; - - HANDLE hTempFile = INVALID_HANDLE_VALUE; - LPWSTR scz = NULL; - LPWSTR sczTempFile = NULL; - - if (wzDirectory && *wzDirectory) - { - hr = StrAllocString(&sczTempPath, wzDirectory, 0); - PathExitOnFailure(hr, "Failed to copy temp path."); - } - else - { - hr = StrAlloc(&sczTempPath, cchTempPath); - PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); - - if (!::GetTempPathW(cchTempPath, sczTempPath)) - { - PathExitWithLastError(hr, "Failed to get temp path."); - } - } - - if (wzFileNameTemplate && *wzFileNameTemplate) - { - for (DWORD i = 1; i <= dwUniqueCount && INVALID_HANDLE_VALUE == hTempFile; ++i) - { - hr = StrAllocFormatted(&scz, wzFileNameTemplate, i); - PathExitOnFailure(hr, "Failed to allocate memory for file template."); - - hr = StrAllocFormatted(&sczTempFile, L"%s%s", sczTempPath, scz); - PathExitOnFailure(hr, "Failed to allocate temp file name."); - - hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, dwFileAttributes, NULL); - if (INVALID_HANDLE_VALUE == hTempFile) - { - // if the file already exists, just try again - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) - { - hr = S_OK; - } - PathExitOnFailure(hr, "Failed to create file: %ls", sczTempFile); - } - } - } - - // If we were not able to or we did not try to create a temp file, ask - // the system to provide us a temp file using its built-in mechanism. - if (INVALID_HANDLE_VALUE == hTempFile) - { - hr = StrAlloc(&sczTempFile, MAX_PATH); - PathExitOnFailure(hr, "Failed to allocate memory for the temp path"); - - if (!::GetTempFileNameW(sczTempPath, L"TMP", 0, sczTempFile)) - { - PathExitWithLastError(hr, "Failed to create new temp file name."); - } - - hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, dwFileAttributes, NULL); - if (INVALID_HANDLE_VALUE == hTempFile) - { - PathExitWithLastError(hr, "Failed to open new temp file: %ls", sczTempFile); - } - } - - // If the caller wanted the temp file name or handle, return them here. - if (psczTempFile) - { - hr = StrAllocString(psczTempFile, sczTempFile, 0); - PathExitOnFailure(hr, "Failed to copy temp file string."); - } - - if (phTempFile) - { - *phTempFile = hTempFile; - hTempFile = INVALID_HANDLE_VALUE; - } - -LExit: - if (INVALID_HANDLE_VALUE != hTempFile) - { - ::CloseHandle(hTempFile); - } - - ReleaseStr(scz); - ReleaseStr(sczTempFile); - ReleaseStr(sczTempPath); - - return hr; -} - - -DAPI_(HRESULT) PathCreateTimeBasedTempFile( - __in_z_opt LPCWSTR wzDirectory, - __in_z LPCWSTR wzPrefix, - __in_z_opt LPCWSTR wzPostfix, - __in_z LPCWSTR wzExtension, - __deref_opt_out_z LPWSTR* psczTempFile, - __out_opt HANDLE* phTempFile - ) -{ - HRESULT hr = S_OK; - BOOL fRetry = FALSE; - WCHAR wzTempPath[MAX_PATH] = { }; - LPWSTR sczPrefix = NULL; - LPWSTR sczPrefixFolder = NULL; - SYSTEMTIME time = { }; - - LPWSTR sczTempPath = NULL; - HANDLE hTempFile = INVALID_HANDLE_VALUE; - DWORD dwAttempts = 0; - - if (wzDirectory && *wzDirectory) - { - hr = PathConcat(wzDirectory, wzPrefix, &sczPrefix); - PathExitOnFailure(hr, "Failed to combine directory and log prefix."); - } - else - { - if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) - { - PathExitWithLastError(hr, "Failed to get temp folder."); - } - - hr = PathConcat(wzTempPath, wzPrefix, &sczPrefix); - PathExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix."); - } - - hr = PathGetDirectory(sczPrefix, &sczPrefixFolder); - if (S_OK == hr) - { - hr = DirEnsureExists(sczPrefixFolder, NULL); - PathExitOnFailure(hr, "Failed to ensure temp file path exists: %ls", sczPrefixFolder); - } - - if (!wzPostfix) - { - wzPostfix = L""; - } - - do - { - fRetry = FALSE; - ++dwAttempts; - - ::GetLocalTime(&time); - - // Log format: pre YYYY MM dd hh mm ss post ext - hr = StrAllocFormatted(&sczTempPath, L"%ls_%04u%02u%02u%02u%02u%02u%ls%ls%ls", sczPrefix, time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, wzPostfix, L'.' == *wzExtension ? L"" : L".", wzExtension); - PathExitOnFailure(hr, "failed to allocate memory for the temp path"); - - hTempFile = ::CreateFileW(sczTempPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hTempFile) - { - // If the file already exists, just try again. - DWORD er = ::GetLastError(); - if (ERROR_FILE_EXISTS == er || ERROR_ACCESS_DENIED == er) - { - ::Sleep(100); - - if (10 > dwAttempts) - { - er = ERROR_SUCCESS; - fRetry = TRUE; - } - } - - hr = HRESULT_FROM_WIN32(er); - PathExitOnFailureDebugTrace(hr, "Failed to create temp file: %ls", sczTempPath); - } - } while (fRetry); - - if (psczTempFile) - { - hr = StrAllocString(psczTempFile, sczTempPath, 0); - PathExitOnFailure(hr, "Failed to copy temp path to return."); - } - - if (phTempFile) - { - *phTempFile = hTempFile; - hTempFile = INVALID_HANDLE_VALUE; - } - -LExit: - ReleaseFile(hTempFile); - ReleaseStr(sczTempPath); - ReleaseStr(sczPrefixFolder); - ReleaseStr(sczPrefix); - - return hr; -} - - -DAPI_(HRESULT) PathCreateTempDirectory( - __in_opt LPCWSTR wzDirectory, - __in __format_string LPCWSTR wzDirectoryNameTemplate, - __in DWORD dwUniqueCount, - __out LPWSTR* psczTempDirectory - ) -{ - AssertSz(wzDirectoryNameTemplate && *wzDirectoryNameTemplate, "DirectoryNameTemplate must be specified."); - AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); - - HRESULT hr = S_OK; - - LPWSTR sczTempPath = NULL; - DWORD cchTempPath = MAX_PATH; - - LPWSTR scz = NULL; - - if (wzDirectory && *wzDirectory) - { - hr = StrAllocString(&sczTempPath, wzDirectory, 0); - PathExitOnFailure(hr, "Failed to copy temp path."); - - hr = PathBackslashTerminate(&sczTempPath); - PathExitOnFailure(hr, "Failed to ensure path ends in backslash: %ls", wzDirectory); - } - else - { - hr = StrAlloc(&sczTempPath, cchTempPath); - PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); - - if (!::GetTempPathW(cchTempPath, sczTempPath)) - { - PathExitWithLastError(hr, "Failed to get temp path."); - } - } - - for (DWORD i = 1; i <= dwUniqueCount; ++i) - { - hr = StrAllocFormatted(&scz, wzDirectoryNameTemplate, i); - PathExitOnFailure(hr, "Failed to allocate memory for directory name template."); - - hr = StrAllocFormatted(psczTempDirectory, L"%s%s", sczTempPath, scz); - PathExitOnFailure(hr, "Failed to allocate temp directory name."); - - if (!::CreateDirectoryW(*psczTempDirectory, NULL)) - { - DWORD er = ::GetLastError(); - if (ERROR_ALREADY_EXISTS == er) - { - hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); - continue; - } - else if (ERROR_PATH_NOT_FOUND == er) - { - hr = DirEnsureExists(*psczTempDirectory, NULL); - break; - } - else - { - hr = HRESULT_FROM_WIN32(er); - break; - } - } - else - { - hr = S_OK; - break; - } - } - PathExitOnFailure(hr, "Failed to create temp directory."); - - hr = PathBackslashTerminate(psczTempDirectory); - PathExitOnFailure(hr, "Failed to ensure temp directory is backslash terminated."); - -LExit: - ReleaseStr(scz); - ReleaseStr(sczTempPath); - - return hr; -} - - -DAPI_(HRESULT) PathGetKnownFolder( - __in int csidl, - __out LPWSTR* psczKnownFolder - ) -{ - HRESULT hr = S_OK; - - hr = StrAlloc(psczKnownFolder, MAX_PATH); - PathExitOnFailure(hr, "Failed to allocate memory for known folder."); - - hr = ::SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, *psczKnownFolder); - PathExitOnFailure(hr, "Failed to get known folder path."); - - hr = PathBackslashTerminate(psczKnownFolder); - PathExitOnFailure(hr, "Failed to ensure known folder path is backslash terminated."); - -LExit: - return hr; -} - - -DAPI_(BOOL) PathIsAbsolute( - __in_z LPCWSTR wzPath - ) -{ - return wzPath && wzPath[0] && wzPath[1] && (wzPath[0] == L'\\') || (wzPath[1] == L':'); -} - - -DAPI_(HRESULT) PathConcat( - __in_opt LPCWSTR wzPath1, - __in_opt LPCWSTR wzPath2, - __deref_out_z LPWSTR* psczCombined - ) -{ - return PathConcatCch(wzPath1, 0, wzPath2, 0, psczCombined); -} - - -DAPI_(HRESULT) PathConcatCch( - __in_opt LPCWSTR wzPath1, - __in SIZE_T cchPath1, - __in_opt LPCWSTR wzPath2, - __in SIZE_T cchPath2, - __deref_out_z LPWSTR* psczCombined - ) -{ - HRESULT hr = S_OK; - - if (!wzPath2 || !*wzPath2) - { - hr = StrAllocString(psczCombined, wzPath1, cchPath1); - PathExitOnFailure(hr, "Failed to copy just path1 to output."); - } - else if (!wzPath1 || !*wzPath1 || PathIsAbsolute(wzPath2)) - { - hr = StrAllocString(psczCombined, wzPath2, cchPath2); - PathExitOnFailure(hr, "Failed to copy just path2 to output."); - } - else - { - hr = StrAllocString(psczCombined, wzPath1, cchPath1); - PathExitOnFailure(hr, "Failed to copy path1 to output."); - - hr = PathBackslashTerminate(psczCombined); - PathExitOnFailure(hr, "Failed to backslashify."); - - hr = StrAllocConcat(psczCombined, wzPath2, cchPath2); - PathExitOnFailure(hr, "Failed to append path2 to output."); - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) PathEnsureQuoted( - __inout LPWSTR* ppszPath, - __in BOOL fDirectory - ) -{ - Assert(ppszPath && *ppszPath); - - HRESULT hr = S_OK; - size_t cchPath = 0; - - hr = ::StringCchLengthW(*ppszPath, STRSAFE_MAX_CCH, &cchPath); - PathExitOnFailure(hr, "Failed to get the length of the path."); - - // Handle simple special cases. - if (0 == cchPath || (1 == cchPath && L'"' == (*ppszPath)[0])) - { - hr = StrAllocString(ppszPath, L"\"\"", 2); - PathExitOnFailure(hr, "Failed to allocate a quoted empty string."); - - ExitFunction(); - } - - if (L'"' != (*ppszPath)[0]) - { - hr = StrAllocPrefix(ppszPath, L"\"", 1); - PathExitOnFailure(hr, "Failed to allocate an opening quote."); - - // Add a char for the opening quote. - ++cchPath; - } - - if (L'"' != (*ppszPath)[cchPath - 1]) - { - hr = StrAllocConcat(ppszPath, L"\"", 1); - PathExitOnFailure(hr, "Failed to allocate a closing quote."); - - // Add a char for the closing quote. - ++cchPath; - } - - if (fDirectory) - { - if (L'\\' != (*ppszPath)[cchPath - 2]) - { - // Change the last char to a backslash and re-append the closing quote. - (*ppszPath)[cchPath - 1] = L'\\'; - - hr = StrAllocConcat(ppszPath, L"\"", 1); - PathExitOnFailure(hr, "Failed to allocate another closing quote after the backslash."); - } - } - -LExit: - - return hr; -} - - -DAPI_(HRESULT) PathCompare( - __in_z LPCWSTR wzPath1, - __in_z LPCWSTR wzPath2, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPath1 = NULL; - LPWSTR sczPath2 = NULL; - - hr = PathExpand(&sczPath1, wzPath1, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - PathExitOnFailure(hr, "Failed to expand path1."); - - hr = PathExpand(&sczPath2, wzPath2, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - PathExitOnFailure(hr, "Failed to expand path2."); - - *pnResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczPath1, -1, sczPath2, -1); - -LExit: - ReleaseStr(sczPath2); - ReleaseStr(sczPath1); - - return hr; -} - - -DAPI_(HRESULT) PathCompress( - __in_z LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - HANDLE hPath = INVALID_HANDLE_VALUE; - - hPath = ::CreateFileW(wzPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (INVALID_HANDLE_VALUE == hPath) - { - PathExitWithLastError(hr, "Failed to open path %ls for compression.", wzPath); - } - - DWORD dwBytesReturned = 0; - USHORT usCompressionFormat = COMPRESSION_FORMAT_DEFAULT; - if (0 == ::DeviceIoControl(hPath, FSCTL_SET_COMPRESSION, &usCompressionFormat, sizeof(usCompressionFormat), NULL, 0, &dwBytesReturned, NULL)) - { - // ignore compression attempts on file systems that don't support it - DWORD er = ::GetLastError(); - if (ERROR_INVALID_FUNCTION != er) - { - PathExitOnWin32Error(er, hr, "Failed to set compression state for path %ls.", wzPath); - } - } - -LExit: - ReleaseFile(hPath); - - return hr; -} - -DAPI_(HRESULT) PathGetHierarchyArray( - __in_z LPCWSTR wzPath, - __deref_inout_ecount_opt(*pcPathArray) LPWSTR **prgsczPathArray, - __inout LPUINT pcPathArray - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPathCopy = NULL; - LPWSTR sczNewPathCopy = NULL; - DWORD cArraySpacesNeeded = 0; - size_t cchPath = 0; - - hr = ::StringCchLengthW(wzPath, STRSAFE_MAX_LENGTH, &cchPath); - PathExitOnRootFailure(hr, "Failed to get string length of path: %ls", wzPath); - - if (!cchPath) - { - ExitFunction1(hr = E_INVALIDARG); - } - - for (size_t i = 0; i < cchPath; ++i) - { - if (wzPath[i] == L'\\') - { - ++cArraySpacesNeeded; - } - } - - if (wzPath[cchPath - 1] != L'\\') - { - ++cArraySpacesNeeded; - } - - // If it's a UNC path, cut off the first three paths, 2 because it starts with a double backslash, and another because the first ("\\servername\") isn't a path. - if (wzPath[0] == L'\\' && wzPath[1] == L'\\') - { - cArraySpacesNeeded -= 3; - } - - Assert(cArraySpacesNeeded >= 1); - - hr = MemEnsureArraySize(reinterpret_cast(prgsczPathArray), cArraySpacesNeeded, sizeof(LPWSTR), 0); - PathExitOnFailure(hr, "Failed to allocate array of size %u for parent directories", cArraySpacesNeeded); - *pcPathArray = cArraySpacesNeeded; - - hr = StrAllocString(&sczPathCopy, wzPath, 0); - PathExitOnFailure(hr, "Failed to allocate copy of original path"); - - for (DWORD i = 0; i < cArraySpacesNeeded; ++i) - { - hr = StrAllocString((*prgsczPathArray) + cArraySpacesNeeded - 1 - i, sczPathCopy, 0); - PathExitOnFailure(hr, "Failed to copy path"); - - DWORD cchPathCopy = lstrlenW(sczPathCopy); - - // If it ends in a backslash, it's a directory path, so cut off everything the last backslash before we get the directory portion of the path - if (wzPath[cchPathCopy - 1] == L'\\') - { - sczPathCopy[cchPathCopy - 1] = L'\0'; - } - - hr = PathGetDirectory(sczPathCopy, &sczNewPathCopy); - PathExitOnFailure(hr, "Failed to get directory portion of path"); - - ReleaseStr(sczPathCopy); - sczPathCopy = sczNewPathCopy; - sczNewPathCopy = NULL; - } - - hr = S_OK; - -LExit: - ReleaseStr(sczPathCopy); - - return hr; -} diff --git a/src/dutil/perfutil.cpp b/src/dutil/perfutil.cpp deleted file mode 100644 index bc138d34..00000000 --- a/src/dutil/perfutil.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define PerfExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) -#define PerfExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) -#define PerfExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) -#define PerfExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) -#define PerfExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) -#define PerfExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) -#define PerfExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PERFUTIL, p, x, e, s, __VA_ARGS__) -#define PerfExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, p, x, s, __VA_ARGS__) -#define PerfExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PERFUTIL, p, x, e, s, __VA_ARGS__) -#define PerfExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, p, x, s, __VA_ARGS__) -#define PerfExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PERFUTIL, e, x, s, __VA_ARGS__) -#define PerfExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PERFUTIL, g, x, s, __VA_ARGS__) - -static BOOL vfHighPerformanceCounter = TRUE; // assume the system has a high performance counter -static double vdFrequency = 1; - - -/******************************************************************** - PerfInitialize - initializes internal static variables - -********************************************************************/ -extern "C" void DAPI PerfInitialize( - ) -{ - LARGE_INTEGER liFrequency = { }; - - // - // check for high perf counter - // - if (!::QueryPerformanceFrequency(&liFrequency)) - { - vfHighPerformanceCounter = FALSE; - vdFrequency = 1000; // ticks are measured in milliseconds - } - else - vdFrequency = static_cast(liFrequency.QuadPart); -} - - -/******************************************************************** - PerfClickTime - resets the clicker, or returns elapsed time since last call - - NOTE: if pliElapsed is NULL, resets the elapsed time - if pliElapsed is not NULL, returns perf number since last call to PerfClickTime() -********************************************************************/ -extern "C" void DAPI PerfClickTime( - __out_opt LARGE_INTEGER* pliElapsed - ) -{ - static LARGE_INTEGER liStart = { }; - LARGE_INTEGER* pli = pliElapsed; - - if (!pli) // if elapsed time time was not requested, reset the start time - pli = &liStart; - - if (vfHighPerformanceCounter) - ::QueryPerformanceCounter(pli); - else - pli->QuadPart = ::GetTickCount(); - - if (pliElapsed) - pliElapsed->QuadPart -= liStart.QuadPart; -} - - -/******************************************************************** - PerfConvertToSeconds - converts perf number to seconds - -********************************************************************/ -extern "C" double DAPI PerfConvertToSeconds( - __in const LARGE_INTEGER* pli - ) -{ - Assert(0 < vdFrequency); - return pli->QuadPart / vdFrequency; -} diff --git a/src/dutil/polcutil.cpp b/src/dutil/polcutil.cpp deleted file mode 100644 index 1fdfa18c..00000000 --- a/src/dutil/polcutil.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define PolcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) -#define PolcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) -#define PolcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) -#define PolcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) -#define PolcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) -#define PolcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) -#define PolcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_POLCUTIL, p, x, e, s, __VA_ARGS__) -#define PolcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, p, x, s, __VA_ARGS__) -#define PolcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_POLCUTIL, p, x, e, s, __VA_ARGS__) -#define PolcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, p, x, s, __VA_ARGS__) -#define PolcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_POLCUTIL, e, x, s, __VA_ARGS__) -#define PolcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_POLCUTIL, g, x, s, __VA_ARGS__) - -const LPCWSTR REGISTRY_POLICIES_KEY = L"SOFTWARE\\Policies\\"; - -static HRESULT OpenPolicyKey( - __in_z LPCWSTR wzPolicyPath, - __out HKEY* phk - ); - - -extern "C" HRESULT DAPI PolcReadNumber( - __in_z LPCWSTR wzPolicyPath, - __in_z LPCWSTR wzPolicyName, - __in DWORD dwDefault, - __out DWORD* pdw - ) -{ - HRESULT hr = S_OK; - HKEY hk = NULL; - - hr = OpenPolicyKey(wzPolicyPath, &hk); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - ExitFunction1(hr = S_FALSE); - } - PolcExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); - - hr = RegReadNumber(hk, wzPolicyName, pdw); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - ExitFunction1(hr = S_FALSE); - } - PolcExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); - -LExit: - ReleaseRegKey(hk); - - if (S_FALSE == hr || FAILED(hr)) - { - *pdw = dwDefault; - } - - return hr; -} - -extern "C" HRESULT DAPI PolcReadString( - __in_z LPCWSTR wzPolicyPath, - __in_z LPCWSTR wzPolicyName, - __in_z_opt LPCWSTR wzDefault, - __deref_out_z LPWSTR* pscz - ) -{ - HRESULT hr = S_OK; - HKEY hk = NULL; - - hr = OpenPolicyKey(wzPolicyPath, &hk); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - ExitFunction1(hr = S_FALSE); - } - PolcExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); - - hr = RegReadString(hk, wzPolicyName, pscz); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - ExitFunction1(hr = S_FALSE); - } - PolcExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); - -LExit: - ReleaseRegKey(hk); - - if (S_FALSE == hr || FAILED(hr)) - { - if (NULL == wzDefault) - { - ReleaseNullStr(*pscz); - } - else - { - hr = StrAllocString(pscz, wzDefault, 0); - } - } - - return hr; -} - - -// internal functions - -static HRESULT OpenPolicyKey( - __in_z LPCWSTR wzPolicyPath, - __out HKEY* phk - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPath = NULL; - - hr = PathConcat(REGISTRY_POLICIES_KEY, wzPolicyPath, &sczPath); - PolcExitOnFailure(hr, "Failed to combine logging path with root path."); - - hr = RegOpen(HKEY_LOCAL_MACHINE, sczPath, KEY_READ, phk); - PolcExitOnFailure(hr, "Failed to open policy registry key."); - -LExit: - ReleaseStr(sczPath); - - return hr; -} diff --git a/src/dutil/precomp.h b/src/dutil/precomp.h deleted file mode 100644 index f8f3b944..00000000 --- a/src/dutil/precomp.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0500 -#endif - -#ifndef _WIN32_MSI -#define _WIN32_MSI 200 -#endif - -#define JET_VERSION 0x0501 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dutilsources.h" -#include "dutil.h" -#include "verutil.h" -#include "aclutil.h" -#include "atomutil.h" -#include "buffutil.h" -#include "butil.h" -#include "cabcutil.h" -#include "cabutil.h" -#include "conutil.h" -#include "cryputil.h" -#include "eseutil.h" -#include "dirutil.h" -#include "dlutil.h" -#include "dpiutil.h" -#include "fileutil.h" -#include "guidutil.h" -#include "gdiputil.h" -#include "dictutil.h" -#include "deputil.h" // NOTE: This must come after dictutil.h since it uses it. -#include "inetutil.h" -#include "iniutil.h" -#include "jsonutil.h" -#include "locutil.h" -#include "logutil.h" -#include "memutil.h" // NOTE: almost everying is inlined so there is a small .cpp file -//#include "metautil.h" - see metautil.cpp why this *must* be commented out -#include "monutil.h" -#include "osutil.h" -#include "pathutil.h" -#include "perfutil.h" -#include "polcutil.h" -#include "procutil.h" -#include "regutil.h" -#include "resrutil.h" -#include "reswutil.h" -#include "rmutil.h" -#include "rssutil.h" -#include "apuputil.h" // NOTE: this must come after atomutil.h and rssutil.h since it uses them. -#include "shelutil.h" -//#include "sqlutil.h" - see sqlutil.cpp why this *must* be commented out -#include "srputil.h" -#include "strutil.h" -#include "timeutil.h" -#include "timeutil.h" -#include "thmutil.h" -#include "uncutil.h" -#include "uriutil.h" -#include "userutil.h" -#include "wiutil.h" -#include "wuautil.h" -#include // This header is needed for msxml2.h to compile correctly -#include // This file is needed to include xmlutil.h -#include "xmlutil.h" - diff --git a/src/dutil/proc2utl.cpp b/src/dutil/proc2utl.cpp deleted file mode 100644 index a59d2ffc..00000000 --- a/src/dutil/proc2utl.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) -#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) -#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) -#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) -#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) -#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) - -/******************************************************************** - ProcFindAllIdsFromExeName() - returns an array of process ids that are running specified executable. - -*******************************************************************/ -extern "C" HRESULT DAPI ProcFindAllIdsFromExeName( - __in_z LPCWSTR wzExeName, - __out DWORD** ppdwProcessIds, - __out DWORD* pcProcessIds - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - HANDLE hSnap = INVALID_HANDLE_VALUE; - BOOL fContinue = FALSE; - PROCESSENTRY32W peData = { sizeof(peData) }; - - hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (INVALID_HANDLE_VALUE == hSnap) - { - ProcExitWithLastError(hr, "Failed to create snapshot of processes on system"); - } - - fContinue = ::Process32FirstW(hSnap, &peData); - - while (fContinue) - { - if (0 == lstrcmpiW((LPCWSTR)&(peData.szExeFile), wzExeName)) - { - if (!*ppdwProcessIds) - { - *ppdwProcessIds = static_cast(MemAlloc(sizeof(DWORD), TRUE)); - ProcExitOnNull(ppdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for returned process IDs."); - } - else - { - DWORD* pdwReAllocReturnedPids = NULL; - pdwReAllocReturnedPids = static_cast(MemReAlloc(*ppdwProcessIds, sizeof(DWORD) * ((*pcProcessIds) + 1), TRUE)); - ProcExitOnNull(pdwReAllocReturnedPids, hr, E_OUTOFMEMORY, "Failed to re-allocate array for returned process IDs."); - - *ppdwProcessIds = pdwReAllocReturnedPids; - } - - (*ppdwProcessIds)[*pcProcessIds] = peData.th32ProcessID; - ++(*pcProcessIds); - } - - fContinue = ::Process32NextW(hSnap, &peData); - } - - er = ::GetLastError(); - if (ERROR_NO_MORE_FILES == er) - { - hr = S_OK; - } - else - { - hr = HRESULT_FROM_WIN32(er); - } - -LExit: - ReleaseFile(hSnap); - - return hr; -} diff --git a/src/dutil/proc3utl.cpp b/src/dutil/proc3utl.cpp deleted file mode 100644 index 6d3cbc67..00000000 --- a/src/dutil/proc3utl.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) -#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) -#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) -#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) -#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) -#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) - -static HRESULT GetActiveSessionUserToken( - __out HANDLE *phToken - ); - - -/******************************************************************** - ProcExecuteAsInteractiveUser() - runs process as currently logged in - user. -*******************************************************************/ -extern "C" HRESULT DAPI ProcExecuteAsInteractiveUser( - __in_z LPCWSTR wzExecutablePath, - __in_z LPCWSTR wzCommandLine, - __out HANDLE *phProcess - ) -{ - HRESULT hr = S_OK; - HANDLE hToken = NULL; - LPVOID pEnvironment = NULL; - LPWSTR sczFullCommandLine = NULL; - STARTUPINFOW si = { }; - PROCESS_INFORMATION pi = { }; - - hr = GetActiveSessionUserToken(&hToken); - ProcExitOnFailure(hr, "Failed to get active session user token."); - - if (!::CreateEnvironmentBlock(&pEnvironment, hToken, FALSE)) - { - ProcExitWithLastError(hr, "Failed to create environment block for UI process."); - } - - hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine); - ProcExitOnFailure(hr, "Failed to allocate full command-line."); - - si.cb = sizeof(si); - if (!::CreateProcessAsUserW(hToken, wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, pEnvironment, NULL, &si, &pi)) - { - ProcExitWithLastError(hr, "Failed to create UI process: %ls", sczFullCommandLine); - } - - *phProcess = pi.hProcess; - pi.hProcess = NULL; - -LExit: - ReleaseHandle(pi.hThread); - ReleaseHandle(pi.hProcess); - ReleaseStr(sczFullCommandLine); - - if (pEnvironment) - { - ::DestroyEnvironmentBlock(pEnvironment); - } - - ReleaseHandle(hToken); - - return hr; -} - - -static HRESULT GetActiveSessionUserToken( - __out HANDLE *phToken - ) -{ - HRESULT hr = S_OK; - PWTS_SESSION_INFO pSessionInfo = NULL; - DWORD cSessions = 0; - DWORD dwSessionId = 0; - BOOL fSessionFound = FALSE; - HANDLE hToken = NULL; - - // Loop through the sessions looking for the active one. - if (!::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &cSessions)) - { - ProcExitWithLastError(hr, "Failed to enumerate sessions."); - } - - for (DWORD i = 0; i < cSessions; ++i) - { - if (WTSActive == pSessionInfo[i].State) - { - dwSessionId = pSessionInfo[i].SessionId; - fSessionFound = TRUE; - - break; - } - } - - if (!fSessionFound) - { - ExitFunction1(hr = E_NOTFOUND); - } - - // Get the user token from the active session. - if (!::WTSQueryUserToken(dwSessionId, &hToken)) - { - ProcExitWithLastError(hr, "Failed to get active session user token."); - } - - *phToken = hToken; - hToken = NULL; - -LExit: - ReleaseHandle(hToken); - - if (pSessionInfo) - { - ::WTSFreeMemory(pSessionInfo); - } - - return hr; -} diff --git a/src/dutil/procutil.cpp b/src/dutil/procutil.cpp deleted file mode 100644 index 6bfe5017..00000000 --- a/src/dutil/procutil.cpp +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) -#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) -#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) -#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) -#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) -#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) -#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) - - -// private functions -static HRESULT CreatePipes( - __out HANDLE *phOutRead, - __out HANDLE *phOutWrite, - __out HANDLE *phErrWrite, - __out HANDLE *phInRead, - __out HANDLE *phInWrite - ); - -static BOOL CALLBACK CloseWindowEnumCallback( - __in HWND hWnd, - __in LPARAM lParam - ); - - -extern "C" HRESULT DAPI ProcElevated( - __in HANDLE hProcess, - __out BOOL* pfElevated - ) -{ - HRESULT hr = S_OK; - HANDLE hToken = NULL; - TOKEN_ELEVATION tokenElevated = { }; - DWORD cbToken = 0; - - if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) - { - ProcExitWithLastError(hr, "Failed to open process token."); - } - - if (::GetTokenInformation(hToken, TokenElevation, &tokenElevated, sizeof(TOKEN_ELEVATION), &cbToken)) - { - *pfElevated = (0 != tokenElevated.TokenIsElevated); - } - else - { - DWORD er = ::GetLastError(); - hr = HRESULT_FROM_WIN32(er); - - // If it's invalid argument, this means the OS doesn't support TokenElevation, so we're not elevated. - if (E_INVALIDARG == hr) - { - *pfElevated = FALSE; - hr = S_OK; - } - else - { - ProcExitOnRootFailure(hr, "Failed to get elevation token from process."); - } - } - -LExit: - ReleaseHandle(hToken); - - return hr; -} - -extern "C" HRESULT DAPI ProcWow64( - __in HANDLE hProcess, - __out BOOL* pfWow64 - ) -{ - HRESULT hr = S_OK; - BOOL fIsWow64 = FALSE; - - typedef BOOL(WINAPI* LPFN_ISWOW64PROCESS2)(HANDLE, USHORT *, USHORT *); - LPFN_ISWOW64PROCESS2 pfnIsWow64Process2 = (LPFN_ISWOW64PROCESS2)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process2"); - - if (pfnIsWow64Process2) - { - USHORT pProcessMachine = IMAGE_FILE_MACHINE_UNKNOWN; - if (!pfnIsWow64Process2(hProcess, &pProcessMachine, nullptr)) - { - ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process2."); - } - - if (pProcessMachine != IMAGE_FILE_MACHINE_UNKNOWN) - { - fIsWow64 = TRUE; - } - } - else - { - typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); - LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process"); - - if (pfnIsWow64Process) - { - if (!pfnIsWow64Process(hProcess, &fIsWow64)) - { - ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process."); - } - } - } - - *pfWow64 = fIsWow64; - -LExit: - return hr; -} - -extern "C" HRESULT DAPI ProcDisableWowFileSystemRedirection( - __in PROC_FILESYSTEMREDIRECTION* pfsr - ) -{ - AssertSz(!pfsr->fDisabled, "File system redirection was already disabled."); - HRESULT hr = S_OK; - - typedef BOOL (WINAPI *LPFN_Wow64DisableWow64FsRedirection)(PVOID *); - LPFN_Wow64DisableWow64FsRedirection pfnWow64DisableWow64FsRedirection = (LPFN_Wow64DisableWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64DisableWow64FsRedirection"); - - if (!pfnWow64DisableWow64FsRedirection) - { - ExitFunction1(hr = E_NOTIMPL); - } - - if (!pfnWow64DisableWow64FsRedirection(&pfsr->pvRevertState)) - { - ProcExitWithLastError(hr, "Failed to disable file system redirection."); - } - - pfsr->fDisabled = TRUE; - -LExit: - return hr; -} - -extern "C" HRESULT DAPI ProcRevertWowFileSystemRedirection( - __in PROC_FILESYSTEMREDIRECTION* pfsr - ) -{ - HRESULT hr = S_OK; - - if (pfsr->fDisabled) - { - typedef BOOL (WINAPI *LPFN_Wow64RevertWow64FsRedirection)(PVOID); - LPFN_Wow64RevertWow64FsRedirection pfnWow64RevertWow64FsRedirection = (LPFN_Wow64RevertWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64RevertWow64FsRedirection"); - - if (!pfnWow64RevertWow64FsRedirection(pfsr->pvRevertState)) - { - ProcExitWithLastError(hr, "Failed to revert file system redirection."); - } - - pfsr->fDisabled = FALSE; - pfsr->pvRevertState = NULL; - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI ProcExec( - __in_z LPCWSTR wzExecutablePath, - __in_z_opt LPCWSTR wzCommandLine, - __in int nCmdShow, - __out HANDLE *phProcess - ) -{ - HRESULT hr = S_OK; - LPWSTR sczFullCommandLine = NULL; - STARTUPINFOW si = { }; - PROCESS_INFORMATION pi = { }; - - hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine ? wzCommandLine : L""); - ProcExitOnFailure(hr, "Failed to allocate full command-line."); - - si.cb = sizeof(si); - si.wShowWindow = static_cast(nCmdShow); - if (!::CreateProcessW(wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi)) - { - ProcExitWithLastError(hr, "Failed to create process: %ls", sczFullCommandLine); - } - - *phProcess = pi.hProcess; - pi.hProcess = NULL; - -LExit: - ReleaseHandle(pi.hThread); - ReleaseHandle(pi.hProcess); - ReleaseStr(sczFullCommandLine); - - return hr; -} - - -/******************************************************************** - ProcExecute() - executes a command-line. - -*******************************************************************/ -extern "C" HRESULT DAPI ProcExecute( - __in_z LPWSTR wzCommand, - __out HANDLE *phProcess, - __out_opt HANDLE *phChildStdIn, - __out_opt HANDLE *phChildStdOutErr - ) -{ - HRESULT hr = S_OK; - - PROCESS_INFORMATION pi = { }; - STARTUPINFOW si = { }; - - HANDLE hOutRead = INVALID_HANDLE_VALUE; - HANDLE hOutWrite = INVALID_HANDLE_VALUE; - HANDLE hErrWrite = INVALID_HANDLE_VALUE; - HANDLE hInRead = INVALID_HANDLE_VALUE; - HANDLE hInWrite = INVALID_HANDLE_VALUE; - - // Create redirect pipes. - hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite); - ProcExitOnFailure(hr, "failed to create output pipes"); - - // Set up startup structure. - si.cb = sizeof(STARTUPINFOW); - si.dwFlags = STARTF_USESTDHANDLES; - si.hStdInput = hInRead; - si.hStdOutput = hOutWrite; - si.hStdError = hErrWrite; - -#pragma prefast(push) -#pragma prefast(disable:25028) - if (::CreateProcessW(NULL, - wzCommand, // command line - NULL, // security info - NULL, // thread info - TRUE, // inherit handles - ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags - NULL, // environment - NULL, // cur dir - &si, - &pi)) -#pragma prefast(pop) - { - // Close child process output/input handles so child doesn't hang - // while waiting for input from parent process. - ::CloseHandle(hOutWrite); - hOutWrite = INVALID_HANDLE_VALUE; - - ::CloseHandle(hErrWrite); - hErrWrite = INVALID_HANDLE_VALUE; - - ::CloseHandle(hInRead); - hInRead = INVALID_HANDLE_VALUE; - } - else - { - ProcExitWithLastError(hr, "Process failed to execute."); - } - - *phProcess = pi.hProcess; - pi.hProcess = 0; - - if (phChildStdIn) - { - *phChildStdIn = hInWrite; - hInWrite = INVALID_HANDLE_VALUE; - } - - if (phChildStdOutErr) - { - *phChildStdOutErr = hOutRead; - hOutRead = INVALID_HANDLE_VALUE; - } - -LExit: - if (pi.hThread) - { - ::CloseHandle(pi.hThread); - } - - if (pi.hProcess) - { - ::CloseHandle(pi.hProcess); - } - - ReleaseFileHandle(hOutRead); - ReleaseFileHandle(hOutWrite); - ReleaseFileHandle(hErrWrite); - ReleaseFileHandle(hInRead); - ReleaseFileHandle(hInWrite); - - return hr; -} - - -/******************************************************************** - ProcWaitForCompletion() - waits for process to complete and gets return code. - -*******************************************************************/ -extern "C" HRESULT DAPI ProcWaitForCompletion( - __in HANDLE hProcess, - __in DWORD dwTimeout, - __out DWORD *pReturnCode - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - // Wait for everything to finish - er = ::WaitForSingleObject(hProcess, dwTimeout); - if (WAIT_FAILED == er) - { - ProcExitWithLastError(hr, "Failed to wait for process to complete."); - } - else if (WAIT_TIMEOUT == er) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(er)); - } - - if (!::GetExitCodeProcess(hProcess, &er)) - { - ProcExitWithLastError(hr, "Failed to get process return code."); - } - - *pReturnCode = er; - -LExit: - return hr; -} - -/******************************************************************** - ProcWaitForIds() - waits for multiple processes to complete. - -*******************************************************************/ -extern "C" HRESULT DAPI ProcWaitForIds( - __in_ecount(cProcessIds) const DWORD rgdwProcessIds[], - __in DWORD cProcessIds, - __in DWORD dwMilliseconds - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - HANDLE hProcess = NULL; - HANDLE * rghProcesses = NULL; - DWORD cProcesses = 0; - - rghProcesses = static_cast(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE)); - ProcExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles."); - - for (DWORD i = 0; i < cProcessIds; ++i) - { - hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, rgdwProcessIds[i]); - if (hProcess != NULL) - { - rghProcesses[cProcesses++] = hProcess; - } - } - - er = ::WaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds); - if (WAIT_FAILED == er) - { - ProcExitWithLastError(hr, "Failed to wait for process to complete."); - } - else if (WAIT_TIMEOUT == er) - { - ProcExitOnWin32Error(er, hr, "Timed out while waiting for process to complete."); - } - -LExit: - if (rghProcesses) - { - for (DWORD i = 0; i < cProcesses; ++i) - { - if (NULL != rghProcesses[i]) - { - ::CloseHandle(rghProcesses[i]); - } - } - - MemFree(rghProcesses); - } - - return hr; -} - -/******************************************************************** - ProcCloseIds() - sends WM_CLOSE messages to all process ids. - -*******************************************************************/ -extern "C" HRESULT DAPI ProcCloseIds( - __in_ecount(cProcessIds) const DWORD* pdwProcessIds, - __in DWORD cProcessIds - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < cProcessIds; ++i) - { - if (!::EnumWindows(&CloseWindowEnumCallback, pdwProcessIds[i])) - { - ProcExitWithLastError(hr, "Failed to enumerate windows."); - } - } - -LExit: - return hr; -} - - -static HRESULT CreatePipes( - __out HANDLE *phOutRead, - __out HANDLE *phOutWrite, - __out HANDLE *phErrWrite, - __out HANDLE *phInRead, - __out HANDLE *phInWrite - ) -{ - HRESULT hr = S_OK; - SECURITY_ATTRIBUTES sa; - HANDLE hOutTemp = INVALID_HANDLE_VALUE; - HANDLE hInTemp = INVALID_HANDLE_VALUE; - - HANDLE hOutRead = INVALID_HANDLE_VALUE; - HANDLE hOutWrite = INVALID_HANDLE_VALUE; - HANDLE hErrWrite = INVALID_HANDLE_VALUE; - HANDLE hInRead = INVALID_HANDLE_VALUE; - HANDLE hInWrite = INVALID_HANDLE_VALUE; - - // Fill out security structure so we can inherit handles - ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = TRUE; - sa.lpSecurityDescriptor = NULL; - - // Create pipes - if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0)) - { - ProcExitWithLastError(hr, "failed to create output pipe"); - } - - if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0)) - { - ProcExitWithLastError(hr, "failed to create input pipe"); - } - - // Duplicate output pipe so standard error and standard output write to the same pipe. - if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) - { - ProcExitWithLastError(hr, "failed to duplicate write handle"); - } - - // We need to create new "output read" and "input write" handles that are non inheritable. Otherwise CreateProcess will creates handles in - // the child process that can't be closed. - if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - ProcExitWithLastError(hr, "failed to duplicate output pipe"); - } - - if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - ProcExitWithLastError(hr, "failed to duplicate input pipe"); - } - - // now that everything has succeeded, assign to the outputs - *phOutRead = hOutRead; - hOutRead = INVALID_HANDLE_VALUE; - - *phOutWrite = hOutWrite; - hOutWrite = INVALID_HANDLE_VALUE; - - *phErrWrite = hErrWrite; - hErrWrite = INVALID_HANDLE_VALUE; - - *phInRead = hInRead; - hInRead = INVALID_HANDLE_VALUE; - - *phInWrite = hInWrite; - hInWrite = INVALID_HANDLE_VALUE; - -LExit: - ReleaseFileHandle(hOutRead); - ReleaseFileHandle(hOutWrite); - ReleaseFileHandle(hErrWrite); - ReleaseFileHandle(hInRead); - ReleaseFileHandle(hInWrite); - ReleaseFileHandle(hOutTemp); - ReleaseFileHandle(hInTemp); - - return hr; -} - - -/******************************************************************** - CloseWindowEnumCallback() - outputs trace and log info - -*******************************************************************/ -static BOOL CALLBACK CloseWindowEnumCallback( - __in HWND hWnd, - __in LPARAM lParam - ) -{ - DWORD dwPid = static_cast(lParam); - DWORD dwProcessId = 0; - - ::GetWindowThreadProcessId(hWnd, &dwProcessId); - if (dwPid == dwProcessId) - { - ::SendMessageW(hWnd, WM_CLOSE, 0, 0); - } - - return TRUE; -} diff --git a/src/dutil/regutil.cpp b/src/dutil/regutil.cpp deleted file mode 100644 index cb617932..00000000 --- a/src/dutil/regutil.cpp +++ /dev/null @@ -1,1035 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define RegExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) -#define RegExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) -#define RegExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) -#define RegExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) -#define RegExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) -#define RegExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) -#define RegExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__) -#define RegExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__) -#define RegExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__) -#define RegExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__) -#define RegExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REGUTIL, e, x, s, __VA_ARGS__) -#define RegExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REGUTIL, g, x, s, __VA_ARGS__) - -static PFN_REGCREATEKEYEXW vpfnRegCreateKeyExW = ::RegCreateKeyExW; -static PFN_REGOPENKEYEXW vpfnRegOpenKeyExW = ::RegOpenKeyExW; -static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExW = NULL; -static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExWFromLibrary = NULL; -static PFN_REGDELETEKEYW vpfnRegDeleteKeyW = ::RegDeleteKeyW; -static PFN_REGENUMKEYEXW vpfnRegEnumKeyExW = ::RegEnumKeyExW; -static PFN_REGENUMVALUEW vpfnRegEnumValueW = ::RegEnumValueW; -static PFN_REGQUERYINFOKEYW vpfnRegQueryInfoKeyW = ::RegQueryInfoKeyW; -static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW; -static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW; -static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW; - -static HMODULE vhAdvApi32Dll = NULL; -static BOOL vfRegInitialized = FALSE; - -static HRESULT WriteStringToRegistry( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_z_opt LPCWSTR wzValue, - __in DWORD dwType -); - -/******************************************************************** - RegInitialize - initializes regutil - -*********************************************************************/ -extern "C" HRESULT DAPI RegInitialize( - ) -{ - HRESULT hr = S_OK; - - hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); - RegExitOnFailure(hr, "Failed to load AdvApi32.dll"); - - // ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW - vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW")); - - if (NULL == vpfnRegDeleteKeyExW) - { - vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary; - } - - vfRegInitialized = TRUE; - -LExit: - return hr; -} - - -/******************************************************************** - RegUninitialize - uninitializes regutil - -*********************************************************************/ -extern "C" void DAPI RegUninitialize( - ) -{ - if (vhAdvApi32Dll) - { - ::FreeLibrary(vhAdvApi32Dll); - vhAdvApi32Dll = NULL; - vpfnRegDeleteKeyExWFromLibrary = NULL; - vpfnRegDeleteKeyExW = NULL; - } - - vfRegInitialized = FALSE; -} - - -/******************************************************************** - RegFunctionOverride - overrides the registry functions. Typically used - for unit testing. - -*********************************************************************/ -extern "C" void DAPI RegFunctionOverride( - __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW, - __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW, - __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW, - __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW, - __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW, - __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, - __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, - __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, - __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW - ) -{ - vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW; - vpfnRegOpenKeyExW = pfnRegOpenKeyExW ? pfnRegOpenKeyExW : ::RegOpenKeyExW; - vpfnRegDeleteKeyExW = pfnRegDeleteKeyExW ? pfnRegDeleteKeyExW : vpfnRegDeleteKeyExWFromLibrary; - vpfnRegEnumKeyExW = pfnRegEnumKeyExW ? pfnRegEnumKeyExW : ::RegEnumKeyExW; - vpfnRegEnumValueW = pfnRegEnumValueW ? pfnRegEnumValueW : ::RegEnumValueW; - vpfnRegQueryInfoKeyW = pfnRegQueryInfoKeyW ? pfnRegQueryInfoKeyW : ::RegQueryInfoKeyW; - vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW; - vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW; - vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW; -} - - -/******************************************************************** - RegCreate - creates a registry key. - -*********************************************************************/ -extern "C" HRESULT DAPI RegCreate( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in DWORD dwAccess, - __out HKEY* phk - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, dwAccess, NULL, phk, NULL); - RegExitOnWin32Error(er, hr, "Failed to create registry key."); - -LExit: - return hr; -} - - -/******************************************************************** - RegCreate - creates a registry key with extra options. - -*********************************************************************/ -HRESULT DAPI RegCreateEx( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in DWORD dwAccess, - __in BOOL fVolatile, - __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes, - __out HKEY* phk, - __out_opt BOOL* pfCreated - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD dwDisposition; - - er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, fVolatile ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE, dwAccess, pSecurityAttributes, phk, &dwDisposition); - RegExitOnWin32Error(er, hr, "Failed to create registry key."); - - if (pfCreated) - { - *pfCreated = (REG_CREATED_NEW_KEY == dwDisposition); - } - -LExit: - return hr; -} - - -/******************************************************************** - RegOpen - opens a registry key. - -*********************************************************************/ -extern "C" HRESULT DAPI RegOpen( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in DWORD dwAccess, - __out HKEY* phk - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegOpenKeyExW(hkRoot, wzSubKey, 0, dwAccess, phk); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to open registry key."); - -LExit: - return hr; -} - - -/******************************************************************** - RegDelete - deletes a registry key (and optionally it's whole tree). - -*********************************************************************/ -extern "C" HRESULT DAPI RegDelete( - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in BOOL fDeleteTree - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - LPWSTR pszEnumeratedSubKey = NULL; - LPWSTR pszRecursiveSubKey = NULL; - HKEY hkKey = NULL; - REGSAM samDesired = 0; - - if (!vfRegInitialized && REG_KEY_DEFAULT != kbKeyBitness) - { - hr = E_INVALIDARG; - RegExitOnFailure(hr, "RegInitialize must be called first in order to RegDelete() a key with non-default bit attributes!"); - } - - switch (kbKeyBitness) - { - case REG_KEY_32BIT: - samDesired = KEY_WOW64_32KEY; - break; - case REG_KEY_64BIT: - samDesired = KEY_WOW64_64KEY; - break; - case REG_KEY_DEFAULT: - // Nothing to do - break; - } - - if (fDeleteTree) - { - hr = RegOpen(hkRoot, wzSubKey, KEY_READ | samDesired, &hkKey); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - RegExitOnFailure(hr, "Failed to open this key for enumerating subkeys: %ls", wzSubKey); - - // Yes, keep enumerating the 0th item, because we're deleting it every time - while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, 0, &pszEnumeratedSubKey))) - { - RegExitOnFailure(hr, "Failed to enumerate key 0"); - - hr = PathConcat(wzSubKey, pszEnumeratedSubKey, &pszRecursiveSubKey); - RegExitOnFailure(hr, "Failed to concatenate paths while recursively deleting subkeys. Path1: %ls, Path2: %ls", wzSubKey, pszEnumeratedSubKey); - - hr = RegDelete(hkRoot, pszRecursiveSubKey, kbKeyBitness, fDeleteTree); - RegExitOnFailure(hr, "Failed to recursively delete subkey: %ls", pszRecursiveSubKey); - } - - hr = S_OK; - } - - if (NULL != vpfnRegDeleteKeyExW) - { - er = vpfnRegDeleteKeyExW(hkRoot, wzSubKey, samDesired, 0); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to delete registry key (ex)."); - } - else - { - er = vpfnRegDeleteKeyW(hkRoot, wzSubKey); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to delete registry key."); - } - -LExit: - ReleaseRegKey(hkKey); - ReleaseStr(pszEnumeratedSubKey); - ReleaseStr(pszRecursiveSubKey); - - return hr; -} - - -/******************************************************************** - RegKeyEnum - enumerates child registry keys. - -*********************************************************************/ -extern "C" HRESULT DAPI RegKeyEnum( - __in HKEY hk, - __in DWORD dwIndex, - __deref_out_z LPWSTR* psczKey - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - SIZE_T cb = 0; - DWORD cch = 0; - - if (psczKey && *psczKey) - { - hr = StrMaxLength(*psczKey, &cb); - RegExitOnFailure(hr, "Failed to determine length of string."); - - cch = (DWORD)min(DWORD_MAX, cb); - } - - if (2 > cch) - { - cch = 2; - - hr = StrAlloc(psczKey, cch); - RegExitOnFailure(hr, "Failed to allocate string to minimum size."); - } - - er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); - if (ERROR_MORE_DATA == er) - { - er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, &cch, NULL, NULL, NULL, NULL, NULL, NULL); - RegExitOnWin32Error(er, hr, "Failed to get max size of subkey name under registry key."); - - ++cch; // add one because RegQueryInfoKeyW() returns the length of the subkeys without the null terminator. - hr = StrAlloc(psczKey, cch); - RegExitOnFailure(hr, "Failed to allocate string bigger for enum registry key."); - - er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); - } - else if (ERROR_NO_MORE_ITEMS == er) - { - ExitFunction1(hr = E_NOMOREITEMS); - } - RegExitOnWin32Error(er, hr, "Failed to enum registry key."); - - // Always ensure the registry key name is null terminated. -#pragma prefast(push) -#pragma prefast(disable:26018) - (*psczKey)[cch] = L'\0'; // note that cch will always be one less than the size of the buffer because that's how RegEnumKeyExW() works. -#pragma prefast(pop) - -LExit: - return hr; -} - - -/******************************************************************** - RegValueEnum - enumerates registry values. - -*********************************************************************/ -HRESULT DAPI RegValueEnum( - __in HKEY hk, - __in DWORD dwIndex, - __deref_out_z LPWSTR* psczName, - __out_opt DWORD *pdwType - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD cbValueName = 0; - - er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cbValueName, NULL, NULL, NULL); - RegExitOnWin32Error(er, hr, "Failed to get max size of value name under registry key."); - - // Add one for null terminator - ++cbValueName; - - hr = StrAlloc(psczName, cbValueName); - RegExitOnFailure(hr, "Failed to allocate array for registry value name"); - - er = vpfnRegEnumValueW(hk, dwIndex, *psczName, &cbValueName, NULL, pdwType, NULL, NULL); - if (ERROR_NO_MORE_ITEMS == er) - { - ExitFunction1(hr = E_NOMOREITEMS); - } - RegExitOnWin32Error(er, hr, "Failed to enumerate registry value"); - -LExit: - return hr; -} - -/******************************************************************** - RegGetType - reads a registry key value type. - *********************************************************************/ -HRESULT DAPI RegGetType( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD *pdwType - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegQueryValueExW(hk, wzName, NULL, pdwType, NULL, NULL); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to read registry value."); -LExit: - - return hr; -} - -/******************************************************************** - RegReadBinary - reads a registry key binary value. - NOTE: caller is responsible for freeing *ppbBuffer -*********************************************************************/ -HRESULT DAPI RegReadBinary( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, - __out SIZE_T *pcbBuffer - ) -{ - HRESULT hr = S_OK; - LPBYTE pbBuffer = NULL; - DWORD er = ERROR_SUCCESS; - DWORD cb = 0; - DWORD dwType = 0; - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, &cb); - RegExitOnWin32Error(er, hr, "Failed to get size of registry value."); - - // Zero-length binary values can exist - if (0 < cb) - { - pbBuffer = static_cast(MemAlloc(cb, FALSE)); - RegExitOnNull(pbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for binary registry value."); - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, pbBuffer, &cb); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to read registry value."); - } - - if (REG_BINARY == dwType) - { - *ppbBuffer = pbBuffer; - pbBuffer = NULL; - *pcbBuffer = cb; - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - RegExitOnRootFailure(hr, "Error reading binary registry value due to unexpected data type: %u", dwType); - } - -LExit: - ReleaseMem(pbBuffer); - - return hr; -} - - -/******************************************************************** - RegReadString - reads a registry key value as a string. - -*********************************************************************/ -extern "C" HRESULT DAPI RegReadString( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __deref_out_z LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - SIZE_T cbValue = 0; - DWORD cch = 0; - DWORD cb = 0; - DWORD dwType = 0; - LPWSTR sczExpand = NULL; - - if (psczValue && *psczValue) - { - hr = StrMaxLength(*psczValue, &cbValue); - RegExitOnFailure(hr, "Failed to determine length of string."); - - cch = (DWORD)min(DWORD_MAX, cbValue); - } - - if (2 > cch) - { - cch = 2; - - hr = StrAlloc(psczValue, cch); - RegExitOnFailure(hr, "Failed to allocate string to minimum size."); - } - - cb = sizeof(WCHAR) * (cch - 1); // subtract one to ensure there will be a space at the end of the string for the null terminator. - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*psczValue), &cb); - if (ERROR_MORE_DATA == er) - { - cch = cb / sizeof(WCHAR) + 1; // add one to ensure there will be space at the end for the null terminator - hr = StrAlloc(psczValue, cch); - RegExitOnFailure(hr, "Failed to allocate string bigger for registry value."); - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*psczValue), &cb); - } - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to read registry key."); - - if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) - { - // Always ensure the registry value is null terminated. - (*psczValue)[cch - 1] = L'\0'; - - if (REG_EXPAND_SZ == dwType) - { - hr = StrAllocString(&sczExpand, *psczValue, 0); - RegExitOnFailure(hr, "Failed to copy registry value to expand."); - - hr = PathExpand(psczValue, sczExpand, PATH_EXPAND_ENVIRONMENT); - RegExitOnFailure(hr, "Failed to expand registry value: %ls", *psczValue); - } - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - RegExitOnRootFailure(hr, "Error reading string registry value due to unexpected data type: %u", dwType); - } - -LExit: - ReleaseStr(sczExpand); - - return hr; -} - - -/******************************************************************** - RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array. - -*********************************************************************/ -HRESULT DAPI RegReadStringArray( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings, - __out DWORD *pcStrings - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD dwNullCharacters = 0; - DWORD dwType = 0; - DWORD cb = 0; - DWORD cch = 0; - LPCWSTR wzSource = NULL; - LPWSTR sczValue = NULL; - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(sczValue), &cb); - if (0 < cb) - { - cch = cb / sizeof(WCHAR); - hr = StrAlloc(&sczValue, cch); - RegExitOnFailure(hr, "Failed to allocate string for registry value."); - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(sczValue), &cb); - } - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to read registry key."); - - if (cb / sizeof(WCHAR) != cch) - { - hr = E_UNEXPECTED; - RegExitOnFailure(hr, "The size of registry value %ls unexpected changed between 2 reads", wzName); - } - - if (REG_MULTI_SZ != dwType) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - RegExitOnRootFailure(hr, "Tried to read string array, but registry value %ls is of an incorrect type", wzName); - } - - // Value exists, but is empty, so no strings to return. - if (2 > cch) - { - *prgsczStrings = NULL; - *pcStrings = 0; - ExitFunction1(hr = S_OK); - } - - // The docs specifically say if the value was written without double-null-termination, it'll get read back without it too. - if (L'\0' != sczValue[cch-1] || L'\0' != sczValue[cch-2]) - { - hr = E_INVALIDARG; - RegExitOnFailure(hr, "Tried to read string array, but registry value %ls is invalid (isn't double-null-terminated)", wzName); - } - - cch = cb / sizeof(WCHAR); - for (DWORD i = 0; i < cch; ++i) - { - if (L'\0' == sczValue[i]) - { - ++dwNullCharacters; - } - } - - // There's one string for every null character encountered (except the extra 1 at the end of the string) - *pcStrings = dwNullCharacters - 1; - hr = MemEnsureArraySize(reinterpret_cast(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0); - RegExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value"); - -#pragma prefast(push) -#pragma prefast(disable:26010) - wzSource = sczValue; - for (DWORD i = 0; i < *pcStrings; ++i) - { - hr = StrAllocString(&(*prgsczStrings)[i], wzSource, 0); - RegExitOnFailure(hr, "Failed to allocate copy of string"); - - // Skip past this string - wzSource += lstrlenW(wzSource) + 1; - } -#pragma prefast(pop) - -LExit: - ReleaseStr(sczValue); - - return hr; -} - - -/******************************************************************** - RegReadVersion - reads a registry key value as a version. - -*********************************************************************/ -extern "C" HRESULT DAPI RegReadVersion( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD64* pdw64Version - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD dwType = 0; - DWORD cb = 0; - LPWSTR sczVersion = NULL; - - cb = sizeof(DWORD64); - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*pdw64Version), &cb); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) - { - hr = RegReadString(hk, wzName, &sczVersion); - RegExitOnFailure(hr, "Failed to read registry version as string."); - - hr = FileVersionFromStringEx(sczVersion, 0, pdw64Version); - RegExitOnFailure(hr, "Failed to convert registry string to version."); - } - else if (REG_QWORD == dwType) - { - RegExitOnWin32Error(er, hr, "Failed to read registry key."); - } - else // unexpected data type - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); - } - -LExit: - ReleaseStr(sczVersion); - - return hr; -} - - -/******************************************************************** - RegReadNumber - reads a DWORD registry key value as a number. - -*********************************************************************/ -extern "C" HRESULT DAPI RegReadNumber( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD* pdwValue - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD dwType = 0; - DWORD cb = sizeof(DWORD); - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(pdwValue), &cb); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to query registry key value."); - - if (REG_DWORD != dwType) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); - } - -LExit: - return hr; -} - - -/******************************************************************** - RegReadQword - reads a QWORD registry key value as a number. - -*********************************************************************/ -extern "C" HRESULT DAPI RegReadQword( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __out DWORD64* pqwValue - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD dwType = 0; - DWORD cb = sizeof(DWORD64); - - er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(pqwValue), &cb); - if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) - { - ExitFunction1(hr = E_FILENOTFOUND); - } - RegExitOnWin32Error(er, hr, "Failed to query registry key value."); - - if (REG_QWORD != dwType) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); - RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); - } - -LExit: - return hr; -} - - -/******************************************************************** - RegWriteBinary - writes a registry key value as a binary. - -*********************************************************************/ -HRESULT DAPI RegWriteBinary( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_bcount(cbBuffer) const BYTE *pbBuffer, - __in DWORD cbBuffer - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegSetValueExW(hk, wzName, 0, REG_BINARY, pbBuffer, cbBuffer); - RegExitOnWin32Error(er, hr, "Failed to write binary registry value with name: %ls", wzName); - -LExit: - return hr; -} - - -/******************************************************************** -RegWriteExpandString - writes a registry key value as an expand string. - -Note: if wzValue is NULL the value will be removed. -*********************************************************************/ -extern "C" HRESULT DAPI RegWriteExpandString( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_z_opt LPCWSTR wzValue -) -{ - return WriteStringToRegistry(hk, wzName, wzValue, REG_EXPAND_SZ); -} - - -/******************************************************************** - RegWriteString - writes a registry key value as a string. - - Note: if wzValue is NULL the value will be removed. -*********************************************************************/ -extern "C" HRESULT DAPI RegWriteString( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_z_opt LPCWSTR wzValue - ) -{ - return WriteStringToRegistry(hk, wzName, wzValue, REG_SZ); -} - - -/******************************************************************** - RegWriteStringFormatted - writes a registry key value as a formatted string. - -*********************************************************************/ -extern "C" HRESULT DAPI RegWriteStringFormatted( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in __format_string LPCWSTR szFormat, - ... - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - va_list args; - - va_start(args, szFormat); - hr = StrAllocFormattedArgs(&sczValue, szFormat, args); - va_end(args); - RegExitOnFailure(hr, "Failed to allocate %ls value.", wzName); - - hr = WriteStringToRegistry(hk, wzName, sczValue, REG_SZ); - -LExit: - ReleaseStr(sczValue); - - return hr; -} - - -/******************************************************************** - RegWriteStringArray - writes an array of strings as a REG_MULTI_SZ value - -*********************************************************************/ -HRESULT DAPI RegWriteStringArray( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_ecount(cValues) LPWSTR *rgwzValues, - __in DWORD cValues - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - LPWSTR wzCopyDestination = NULL; - LPCWSTR wzWriteValue = NULL; - LPWSTR sczWriteValue = NULL; - DWORD dwTotalStringSize = 0; - DWORD cbTotalStringSize = 0; - DWORD dwTemp = 0; - - if (0 == cValues) - { - wzWriteValue = L"\0"; - } - else - { - // Add space for the null terminator - dwTotalStringSize = 1; - - for (DWORD i = 0; i < cValues; ++i) - { - dwTemp = dwTotalStringSize; - hr = ::DWordAdd(dwTemp, 1 + lstrlenW(rgwzValues[i]), &dwTotalStringSize); - RegExitOnFailure(hr, "DWORD Overflow while adding length of string to write REG_MULTI_SZ"); - } - - hr = StrAlloc(&sczWriteValue, dwTotalStringSize); - RegExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ"); - - wzCopyDestination = sczWriteValue; - dwTemp = dwTotalStringSize; - for (DWORD i = 0; i < cValues; ++i) - { - hr = ::StringCchCopyW(wzCopyDestination, dwTotalStringSize, rgwzValues[i]); - RegExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]); - - dwTemp -= lstrlenW(rgwzValues[i]) + 1; - wzCopyDestination += lstrlenW(rgwzValues[i]) + 1; - } - - wzWriteValue = sczWriteValue; - } - - hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize); - RegExitOnFailure(hr, "Failed to get total string size in bytes"); - - er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast(wzWriteValue), cbTotalStringSize); - RegExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue); - -LExit: - ReleaseStr(sczWriteValue); - - return hr; -} - -/******************************************************************** - RegWriteNumber - writes a registry key value as a number. - -*********************************************************************/ -extern "C" HRESULT DAPI RegWriteNumber( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in DWORD dwValue - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegSetValueExW(hk, wzName, 0, REG_DWORD, reinterpret_cast(&dwValue), sizeof(dwValue)); - RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); - -LExit: - return hr; -} - -/******************************************************************** - RegWriteQword - writes a registry key value as a Qword. - -*********************************************************************/ -extern "C" HRESULT DAPI RegWriteQword( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in DWORD64 qwValue - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegSetValueExW(hk, wzName, 0, REG_QWORD, reinterpret_cast(&qwValue), sizeof(qwValue)); - RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); - -LExit: - return hr; -} - -/******************************************************************** - RegQueryKey - queries the key for the number of subkeys and values. - -*********************************************************************/ -extern "C" HRESULT DAPI RegQueryKey( - __in HKEY hk, - __out_opt DWORD* pcSubKeys, - __out_opt DWORD* pcValues - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, pcSubKeys, NULL, NULL, pcValues, NULL, NULL, NULL, NULL); - RegExitOnWin32Error(er, hr, "Failed to get the number of subkeys and values under registry key."); - -LExit: - return hr; -} - -/******************************************************************** -RegKeyReadNumber - reads a DWORD registry key value as a number from -a specified subkey. - -*********************************************************************/ -extern "C" HRESULT DAPI RegKeyReadNumber( - __in HKEY hk, - __in_z LPCWSTR wzSubKey, - __in_z_opt LPCWSTR wzName, - __in BOOL f64Bit, - __out DWORD* pdwValue - ) -{ - HRESULT hr = S_OK; - HKEY hkKey = NULL; - - hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey); - RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey); - - hr = RegReadNumber(hkKey, wzName, pdwValue); - RegExitOnFailure(hr, "Failed to read value: %ls/@%ls", wzSubKey, wzName); - -LExit: - ReleaseRegKey(hkKey); - - return hr; -} - -/******************************************************************** -RegValueExists - determines whether a named value exists in a -specified subkey. - -*********************************************************************/ -extern "C" BOOL DAPI RegValueExists( - __in HKEY hk, - __in_z LPCWSTR wzSubKey, - __in_z_opt LPCWSTR wzName, - __in BOOL f64Bit - ) -{ - HRESULT hr = S_OK; - HKEY hkKey = NULL; - DWORD dwType = 0; - - hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey); - RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey); - - hr = RegGetType(hkKey, wzName, &dwType); - RegExitOnFailure(hr, "Failed to read value type: %ls/@%ls", wzSubKey, wzName); - -LExit: - ReleaseRegKey(hkKey); - - return SUCCEEDED(hr); -} - -static HRESULT WriteStringToRegistry( - __in HKEY hk, - __in_z_opt LPCWSTR wzName, - __in_z_opt LPCWSTR wzValue, - __in DWORD dwType - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - size_t cbValue = 0; - - if (wzValue) - { - hr = ::StringCbLengthW(wzValue, STRSAFE_MAX_CCH * sizeof(TCHAR), &cbValue); - RegExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); - - er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast(wzValue), static_cast(cbValue)); - RegExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); - } - else - { - er = vpfnRegDeleteValueW(hk, wzName); - if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) - { - er = ERROR_SUCCESS; - } - RegExitOnWin32Error(er, hr, "Failed to delete registry value: %ls", wzName); - } - -LExit: - return hr; -} diff --git a/src/dutil/resrutil.cpp b/src/dutil/resrutil.cpp deleted file mode 100644 index a6a7ee23..00000000 --- a/src/dutil/resrutil.cpp +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ResrExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) -#define ResrExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) -#define ResrExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) -#define ResrExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) -#define ResrExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) -#define ResrExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) -#define ResrExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RESRUTIL, p, x, e, s, __VA_ARGS__) -#define ResrExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, p, x, s, __VA_ARGS__) -#define ResrExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RESRUTIL, p, x, e, s, __VA_ARGS__) -#define ResrExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, p, x, s, __VA_ARGS__) -#define ResrExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RESRUTIL, e, x, s, __VA_ARGS__) -#define ResrExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RESRUTIL, g, x, s, __VA_ARGS__) - -#define RES_STRINGS_PER_BLOCK 16 - - -BOOL CALLBACK EnumLangIdProc( - __in_opt HMODULE hModule, - __in_z LPCSTR lpType, - __in_z LPCSTR lpName, - __in WORD wLanguage, - __in LONG_PTR lParam - ); - -/******************************************************************** -ResGetStringLangId - get the language id for a string in the string table. - -********************************************************************/ -extern "C" HRESULT DAPI ResGetStringLangId( - __in_opt LPCWSTR wzPath, - __in UINT uID, - __out WORD *pwLangId - ) -{ - Assert(pwLangId); - - HRESULT hr = S_OK; - HINSTANCE hModule = NULL; - DWORD dwBlockId = (uID / RES_STRINGS_PER_BLOCK) + 1; - WORD wFoundLangId = 0; - - if (wzPath && *wzPath) - { - hModule = LoadLibraryExW(wzPath, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); - ResrExitOnNullWithLastError(hModule, hr, "Failed to open resource file: %ls", wzPath); - } - -#pragma prefast(push) -#pragma prefast(disable:25068) - if (!::EnumResourceLanguagesA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), static_cast(EnumLangIdProc), reinterpret_cast(&wFoundLangId))) -#pragma prefast(pop) - { - ResrExitWithLastError(hr, "Failed to find string language identifier."); - } - - *pwLangId = wFoundLangId; - -LExit: - if (hModule) - { - ::FreeLibrary(hModule); - } - - return hr; -} - - -/******************************************************************** -ResReadString - -NOTE: ppwzString should be freed with StrFree() -********************************************************************/ -extern "C" HRESULT DAPI ResReadString( - __in HINSTANCE hinst, - __in UINT uID, - __deref_out_z LPWSTR* ppwzString - ) -{ - Assert(hinst && ppwzString); - - HRESULT hr = S_OK; - DWORD cch = 64; // first guess - DWORD cchReturned = 0; - - do - { - hr = StrAlloc(ppwzString, cch); - ResrExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); - - cchReturned = ::LoadStringW(hinst, uID, *ppwzString, cch); - if (0 == cchReturned) - { - ResrExitWithLastError(hr, "Failed to load string resource id: %d", uID); - } - - // if the returned string count is one character too small, it's likely we have - // more data to read - if (cchReturned + 1 == cch) - { - cch *= 2; - hr = S_FALSE; - } - } while (S_FALSE == hr); - ResrExitOnFailure(hr, "Failed to load string resource id: %d", uID); - -LExit: - return hr; -} - - -/******************************************************************** - ResReadStringAnsi - - NOTE: ppszString should be freed with StrFree() -********************************************************************/ -extern "C" HRESULT DAPI ResReadStringAnsi( - __in HINSTANCE hinst, - __in UINT uID, - __deref_out_z LPSTR* ppszString - ) -{ - Assert(hinst && ppszString); - - HRESULT hr = S_OK; - DWORD cch = 64; // first guess - DWORD cchReturned = 0; - - do - { - hr = StrAnsiAlloc(ppszString, cch); - ResrExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); - -#pragma prefast(push) -#pragma prefast(disable:25068) - cchReturned = ::LoadStringA(hinst, uID, *ppszString, cch); -#pragma prefast(pop) - if (0 == cchReturned) - { - ResrExitWithLastError(hr, "Failed to load string resource id: %d", uID); - } - - // if the returned string count is one character too small, it's likely we have - // more data to read - if (cchReturned + 1 == cch) - { - cch *= 2; - hr = S_FALSE; - } - } while (S_FALSE == hr); - ResrExitOnFailure(hr, "failed to load string resource id: %d", uID); - -LExit: - return hr; -} - - -/******************************************************************** -ResReadData - returns a pointer to the specified resource data - -NOTE: there is no "free" function for this call -********************************************************************/ -extern "C" HRESULT DAPI ResReadData( - __in_opt HINSTANCE hinst, - __in_z LPCSTR szDataName, - __deref_out_bcount(*pcb) PVOID *ppv, - __out DWORD *pcb - ) -{ - Assert(szDataName); - Assert(ppv); - - HRESULT hr = S_OK; - HRSRC hRsrc = NULL; - HGLOBAL hData = NULL; - DWORD cbData = 0; - -#pragma prefast(push) -#pragma prefast(disable:25068) - hRsrc = ::FindResourceExA(hinst, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); -#pragma prefast(pop) - ResrExitOnNullWithLastError(hRsrc, hr, "Failed to find resource."); - - hData = ::LoadResource(hinst, hRsrc); - ResrExitOnNullWithLastError(hData, hr, "Failed to load resource."); - - cbData = ::SizeofResource(hinst, hRsrc); - if (!cbData) - { - ResrExitWithLastError(hr, "Failed to get size of resource."); - } - - *ppv = ::LockResource(hData); - ResrExitOnNullWithLastError(*ppv, hr, "Failed to lock data resource."); - *pcb = cbData; - -LExit: - return hr; -} - - -/******************************************************************** -ResExportDataToFile - extracts the resource data to the specified target file - -********************************************************************/ -extern "C" HRESULT DAPI ResExportDataToFile( - __in_z LPCSTR szDataName, - __in_z LPCWSTR wzTargetFile, - __in DWORD dwCreationDisposition - ) -{ - HRESULT hr = S_OK; - PVOID pData = NULL; - DWORD cbData = 0; - DWORD cbWritten = 0; - HANDLE hFile = INVALID_HANDLE_VALUE; - BOOL bCreatedFile = FALSE; - - hr = ResReadData(NULL, szDataName, &pData, &cbData); - ResrExitOnFailure(hr, "Failed to GetData from %s.", szDataName); - - hFile = ::CreateFileW(wzTargetFile, GENERIC_WRITE, 0, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - ResrExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzTargetFile); - } - bCreatedFile = TRUE; - - if (!::WriteFile(hFile, pData, cbData, &cbWritten, NULL)) - { - ResrExitWithLastError(hr, "Failed to ::WriteFile for %ls.", wzTargetFile); - } - -LExit: - ReleaseFile(hFile); - - if (FAILED(hr)) - { - if (bCreatedFile) - { - ::DeleteFileW(wzTargetFile); - } - } - - return hr; -} - - -BOOL CALLBACK EnumLangIdProc( - __in_opt HMODULE /* hModule */, - __in_z LPCSTR /* lpType */, - __in_z LPCSTR /* lpName */, - __in WORD wLanguage, - __in LONG_PTR lParam - ) -{ - WORD *pwLangId = reinterpret_cast(lParam); - - *pwLangId = wLanguage; - return TRUE; -} diff --git a/src/dutil/reswutil.cpp b/src/dutil/reswutil.cpp deleted file mode 100644 index e78de84a..00000000 --- a/src/dutil/reswutil.cpp +++ /dev/null @@ -1,386 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ReswExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) -#define ReswExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) -#define ReswExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) -#define ReswExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) -#define ReswExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) -#define ReswExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) -#define ReswExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__) -#define ReswExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__) -#define ReswExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__) -#define ReswExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__) -#define ReswExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RESWUTIL, e, x, s, __VA_ARGS__) -#define ReswExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RESWUTIL, g, x, s, __VA_ARGS__) - -#define RES_STRINGS_PER_BLOCK 16 - -// Internal data structure format for a string block in a resource table. -// Note: Strings are always stored as UNICODED. -typedef struct _RES_STRING_BLOCK -{ - DWORD dwBlockId; - WORD wLangId; - LPWSTR rgwz[RES_STRINGS_PER_BLOCK]; -} RES_STRING_BLOCK; - - -// private functions -static HRESULT StringBlockInitialize( - __in_opt HINSTANCE hModule, - __in DWORD dwBlockId, - __in WORD wLangId, - __in RES_STRING_BLOCK* pStrBlock - ); -static void StringBlockUnitialize( - __in RES_STRING_BLOCK* pStrBlock - ); -static HRESULT StringBlockChangeString( - __in RES_STRING_BLOCK* pStrBlock, - __in DWORD dwStringId, - __in_z LPCWSTR szData - ); -static HRESULT StringBlockConvertToResourceData( - __in const RES_STRING_BLOCK* pStrBlock, - __deref_out_bcount(*pcbData) LPVOID* ppvData, - __out DWORD* pcbData - ); -static HRESULT StringBlockConvertFromResourceData( - __in RES_STRING_BLOCK* pStrBlock, - __in_bcount(cbData) LPCVOID pvData, - __in SIZE_T cbData - ); - - -/******************************************************************** -ResWriteString - sets the string into to the specified file's resource name - -********************************************************************/ -extern "C" HRESULT DAPI ResWriteString( - __in_z LPCWSTR wzResourceFile, - __in DWORD dwDataId, - __in_z LPCWSTR wzData, - __in WORD wLangId - ) -{ - Assert(wzResourceFile); - Assert(wzData); - - HRESULT hr = S_OK; - HINSTANCE hModule = NULL; - HANDLE hUpdate = NULL; - RES_STRING_BLOCK StrBlock = { }; - LPVOID pvData = NULL; - DWORD cbData = 0; - - DWORD dwBlockId = (dwDataId / RES_STRINGS_PER_BLOCK) + 1; - DWORD dwStringId = (dwDataId % RES_STRINGS_PER_BLOCK); - - hModule = LoadLibraryExW(wzResourceFile, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); - ReswExitOnNullWithLastError(hModule, hr, "Failed to load library: %ls", wzResourceFile); - - hr = StringBlockInitialize(hModule, dwBlockId, wLangId, &StrBlock); - ReswExitOnFailure(hr, "Failed to get string block to update."); - - hr = StringBlockChangeString(&StrBlock, dwStringId, wzData); - ReswExitOnFailure(hr, "Failed to update string block string."); - - hr = StringBlockConvertToResourceData(&StrBlock, &pvData, &cbData); - ReswExitOnFailure(hr, "Failed to convert string block to resource data."); - - ::FreeLibrary(hModule); - hModule = NULL; - - hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); - ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); - - if (!::UpdateResourceA(hUpdate, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId, pvData, cbData)) - { - ReswExitWithLastError(hr, "Failed to ::UpdateResourceA."); - } - - if (!::EndUpdateResource(hUpdate, FALSE)) - { - ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); - } - - hUpdate = NULL; - -LExit: - ReleaseMem(pvData); - - StringBlockUnitialize(&StrBlock); - - if (hUpdate) - { - ::EndUpdateResource(hUpdate, TRUE); - } - - if (hModule) - { - ::FreeLibrary(hModule); - } - - return hr; -} - - -/******************************************************************** -ResWriteData - sets the data into to the specified file's resource name - -********************************************************************/ -extern "C" HRESULT DAPI ResWriteData( - __in_z LPCWSTR wzResourceFile, - __in_z LPCSTR szDataName, - __in PVOID pData, - __in DWORD cbData - ) -{ - Assert(wzResourceFile); - Assert(szDataName); - Assert(pData); - Assert(cbData); - - HRESULT hr = S_OK; - HANDLE hUpdate = NULL; - - hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); - ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); - - if (!::UpdateResourceA(hUpdate, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), pData, cbData)) - { - ReswExitWithLastError(hr, "Failed to ::UpdateResourceA."); - } - - if (!::EndUpdateResource(hUpdate, FALSE)) - { - ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); - } - - hUpdate = NULL; - -LExit: - if (hUpdate) - { - ::EndUpdateResource(hUpdate, TRUE); - } - - return hr; -} - - -/******************************************************************** -ResImportDataFromFile - reads a file and sets the data into to the specified file's resource name - -********************************************************************/ -extern "C" HRESULT DAPI ResImportDataFromFile( - __in_z LPCWSTR wzTargetFile, - __in_z LPCWSTR wzSourceFile, - __in_z LPCSTR szDataName - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - DWORD cbFile = 0; - HANDLE hMap = NULL; - PVOID pv = NULL; - - hFile = ::CreateFileW(wzSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - ReswExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzSourceFile); - } - - cbFile = ::GetFileSize(hFile, NULL); - if (!cbFile) - { - ReswExitWithLastError(hr, "Failed to GetFileSize for %ls.", wzSourceFile); - } - - hMap = ::CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); - ReswExitOnNullWithLastError(hMap, hr, "Failed to CreateFileMapping for %ls.", wzSourceFile); - - pv = ::MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, cbFile); - ReswExitOnNullWithLastError(pv, hr, "Failed to MapViewOfFile for %ls.", wzSourceFile); - - hr = ResWriteData(wzTargetFile, szDataName, pv, cbFile); - ReswExitOnFailure(hr, "Failed to ResSetData %s on file %ls.", szDataName, wzTargetFile); - -LExit: - if (pv) - { - ::UnmapViewOfFile(pv); - } - - if (hMap) - { - ::CloseHandle(hMap); - } - - ReleaseFile(hFile); - - return hr; -} - - -static HRESULT StringBlockInitialize( - __in_opt HINSTANCE hModule, - __in DWORD dwBlockId, - __in WORD wLangId, - __in RES_STRING_BLOCK* pStrBlock - ) -{ - HRESULT hr = S_OK; - HRSRC hRsrc = NULL; - HGLOBAL hData = NULL; - LPCVOID pvData = NULL; // does not need to be freed - DWORD cbData = 0; - - hRsrc = ::FindResourceExA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId); - ReswExitOnNullWithLastError(hRsrc, hr, "Failed to ::FindResourceExW."); - - hData = ::LoadResource(hModule, hRsrc); - ReswExitOnNullWithLastError(hData, hr, "Failed to ::LoadResource."); - - cbData = ::SizeofResource(hModule, hRsrc); - if (!cbData) - { - ReswExitWithLastError(hr, "Failed to ::SizeofResource."); - } - - pvData = ::LockResource(hData); - ReswExitOnNullWithLastError(pvData, hr, "Failed to lock data resource."); - - pStrBlock->dwBlockId = dwBlockId; - pStrBlock->wLangId = wLangId; - - hr = StringBlockConvertFromResourceData(pStrBlock, pvData, cbData); - ReswExitOnFailure(hr, "Failed to convert string block from resource data."); - -LExit: - return hr; -} - - -static void StringBlockUnitialize( - __in RES_STRING_BLOCK* pStrBlock - ) -{ - if (pStrBlock) - { - for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) - { - ReleaseNullMem(pStrBlock->rgwz[i]); - } - } -} - - -static HRESULT StringBlockChangeString( - __in RES_STRING_BLOCK* pStrBlock, - __in DWORD dwStringId, - __in_z LPCWSTR szData - ) -{ - HRESULT hr = S_OK; - LPWSTR pwzData = NULL; - size_t cchData = 0; - - hr = ::StringCchLengthW(szData, STRSAFE_MAX_LENGTH, &cchData); - ReswExitOnRootFailure(hr, "Failed to get block string length."); - - pwzData = static_cast(MemAlloc((cchData + 1) * sizeof(WCHAR), TRUE)); - ReswExitOnNull(pwzData, hr, E_OUTOFMEMORY, "Failed to allocate new block string."); - - hr = ::StringCchCopyW(pwzData, cchData + 1, szData); - ReswExitOnRootFailure(hr, "Failed to copy new block string."); - - ReleaseNullMem(pStrBlock->rgwz[dwStringId]); - - pStrBlock->rgwz[dwStringId] = pwzData; - pwzData = NULL; - -LExit: - ReleaseMem(pwzData); - - return hr; -} - - -static HRESULT StringBlockConvertToResourceData( - __in const RES_STRING_BLOCK* pStrBlock, - __deref_out_bcount(*pcbData) LPVOID* ppvData, - __out DWORD* pcbData - ) -{ - HRESULT hr = S_OK; - DWORD cbData = 0; - LPVOID pvData = NULL; - WCHAR* pwz = NULL; - - for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) - { - cbData += (lstrlenW(pStrBlock->rgwz[i]) + 1); - } - cbData *= sizeof(WCHAR); - - pvData = MemAlloc(cbData, TRUE); - ReswExitOnNull(pvData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to convert string block."); - - pwz = static_cast(pvData); - for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) - { - DWORD cch = lstrlenW(pStrBlock->rgwz[i]); - - *pwz = static_cast(cch); - ++pwz; - - for (DWORD j = 0; j < cch; ++j) - { - *pwz = pStrBlock->rgwz[i][j]; - ++pwz; - } - } - - *pcbData = cbData; - *ppvData = pvData; - pvData = NULL; - -LExit: - ReleaseMem(pvData); - - return hr; -} - - -static HRESULT StringBlockConvertFromResourceData( - __in RES_STRING_BLOCK* pStrBlock, - __in_bcount(cbData) LPCVOID pvData, - __in SIZE_T cbData - ) -{ - UNREFERENCED_PARAMETER(cbData); - HRESULT hr = S_OK; - LPCWSTR pwzParse = static_cast(pvData); - - for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) - { - DWORD cchParse = static_cast(*pwzParse); - ++pwzParse; - - pStrBlock->rgwz[i] = static_cast(MemAlloc((cchParse + 1) * sizeof(WCHAR), TRUE)); - ReswExitOnNull(pStrBlock->rgwz[i], hr, E_OUTOFMEMORY, "Failed to populate pStrBlock."); - - hr = ::StringCchCopyNExW(pStrBlock->rgwz[i], cchParse + 1, pwzParse, cchParse, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - ReswExitOnFailure(hr, "Failed to copy parsed resource data into string block."); - - pwzParse += cchParse; - } - -LExit: - return hr; -} diff --git a/src/dutil/rexutil.cpp b/src/dutil/rexutil.cpp deleted file mode 100644 index 155ca714..00000000 --- a/src/dutil/rexutil.cpp +++ /dev/null @@ -1,601 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" -#include "rexutil.h" - - -// Exit macros -#define RexExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) -#define RexExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) -#define RexExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) -#define RexExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) -#define RexExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) -#define RexExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) -#define RexExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__) -#define RexExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__) -#define RexExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__) -#define RexExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__) -#define RexExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REXUTIL, e, x, s, __VA_ARGS__) -#define RexExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REXUTIL, g, x, s, __VA_ARGS__) - -// -// static globals -// -static HMODULE vhCabinetDll = NULL; -static HFDI vhfdi = NULL; -static ERF verf; - -static FAKE_FILE vrgffFileTable[FILETABLESIZE]; -static DWORD vcbRes; -static LPCBYTE vpbRes; -static CHAR vszResource[MAX_PATH]; -static REX_CALLBACK_WRITE vpfnWrite = NULL; - -static HRESULT vhrLastError = S_OK; - -// -// structs -// -struct REX_CALLBACK_STRUCT -{ - BOOL fStopExtracting; // flag set when no more files are needed - LPCWSTR pwzExtract; // file to extract ("*" means extract all) - LPCWSTR pwzExtractDir; // directory to extract files to - LPCWSTR pwzExtractName; // name of file (pwzExtract can't be "*") - - // possible user data - REX_CALLBACK_PROGRESS pfnProgress; - LPVOID pvContext; -}; - -// -// prototypes -// -static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize); -static __callback void DIAMONDAPI RexFree(LPVOID pvData); -static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode); -static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb); -static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb); -static __callback int FAR DIAMONDAPI RexClose(INT_PTR hf); -static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype); -static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify); - - -/******************************************************************** - RexInitialize - initializes internal static variables - -*******************************************************************/ -extern "C" HRESULT RexInitialize() -{ - Assert(!vhfdi); - - HRESULT hr = S_OK; - - vhfdi = ::FDICreate(RexAlloc, RexFree, RexOpen, RexRead, RexWrite, RexClose, RexSeek, cpuUNKNOWN, &verf); - if (!vhfdi) - { - hr = E_FAIL; - RexExitOnFailure(hr, "failed to initialize cabinet.dll"); // TODO: put verf info in trace message here - } - - ::ZeroMemory(vrgffFileTable, sizeof(vrgffFileTable)); - -LExit: - if (FAILED(hr)) - { - ::FDIDestroy(vhfdi); - vhfdi = NULL; - } - - return hr; -} - - -/******************************************************************** - RexUninitialize - initializes internal static variables - -*******************************************************************/ -extern "C" void RexUninitialize() -{ - if (vhfdi) - { - ::FDIDestroy(vhfdi); - vhfdi = NULL; - } -} - - -/******************************************************************** - RexExtract - extracts one or all files from a resource cabinet - - NOTE: wzExtractId can be a single file id or "*" to extract all files - wzExttractDir must be normalized (end in a "\") - wzExtractName is ignored if wzExtractId is "*" -*******************************************************************/ -extern "C" HRESULT RexExtract( - __in_z LPCSTR szResource, - __in_z LPCWSTR wzExtractId, - __in_z LPCWSTR wzExtractDir, - __in_z LPCWSTR wzExtractName, - __in REX_CALLBACK_PROGRESS pfnProgress, - __in REX_CALLBACK_WRITE pfnWrite, - __in LPVOID pvContext - ) -{ - Assert(vhfdi); - HRESULT hr = S_OK; - BOOL fResult; - - HRSRC hResInfo = NULL; - HANDLE hRes = NULL; - - REX_CALLBACK_STRUCT rcs; - - // remember the write callback - vpfnWrite = pfnWrite; - - // - // load the cabinet resource - // - hResInfo = ::FindResourceExA(NULL, RT_RCDATA, szResource, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); - RexExitOnNullWithLastError(hResInfo, hr, "Failed to find resource."); - //hResInfo = ::FindResourceW(NULL, wzResource, /*RT_RCDATA*/MAKEINTRESOURCEW(10)); - //ExitOnNullWithLastError(hResInfo, hr, "failed to load resource info"); - - hRes = ::LoadResource(NULL, hResInfo); - RexExitOnNullWithLastError(hRes, hr, "failed to load resource"); - - vcbRes = ::SizeofResource(NULL, hResInfo); - vpbRes = (const BYTE*)::LockResource(hRes); - - // TODO: Call FDIIsCabinet to confirm resource is a cabinet before trying to extract from it - - // - // convert the resource name to multi-byte - // - //if (!::WideCharToMultiByte(CP_ACP, 0, wzResource, -1, vszResource, countof(vszResource), NULL, NULL)) - //{ - // RexExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource); - //} - - hr = ::StringCchCopyA(vszResource, countof(vszResource), szResource); - RexExitOnFailure(hr, "Failed to copy resource name to global."); - - // - // iterate through files in cabinet extracting them to the callback function - // - rcs.fStopExtracting = FALSE; - rcs.pwzExtract = wzExtractId; - rcs.pwzExtractDir = wzExtractDir; - rcs.pwzExtractName = wzExtractName; - rcs.pfnProgress = pfnProgress; - rcs.pvContext = pvContext; - - fResult = ::FDICopy(vhfdi, vszResource, "", 0, RexCallback, NULL, static_cast(&rcs)); - if (!fResult && !rcs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure - { - hr = vhrLastError; // TODO: put verf info in trace message here - } - -LExit: - return hr; -} - - -/**************************************************************************** - default extract routines - -****************************************************************************/ -static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize) -{ - return MemAlloc(dwSize, FALSE); -} - - -static __callback void DIAMONDAPI RexFree(LPVOID pvData) -{ - MemFree(pvData); -} - - -static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - int i = 0; - - // if FDI asks for some unusual mode (__in low memory situation it could ask for a scratch file) fail - if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) - { - hr = E_OUTOFMEMORY; - RexExitOnFailure(hr, "FDI asked for to create a scratch file, which is unusual"); - } - - // find an empty spot in the fake file table - for (i = 0; i < FILETABLESIZE; ++i) - { - if (!vrgffFileTable[i].fUsed) - { - break; - } - } - - // we should never run out of space in the fake file table - if (FILETABLESIZE <= i) - { - hr = E_OUTOFMEMORY; - RexExitOnFailure(hr, "File table exceeded"); - } - - if (0 == lstrcmpA(vszResource, pszFile)) - { - vrgffFileTable[i].fUsed = TRUE; - vrgffFileTable[i].fftType = MEMORY_FILE; - vrgffFileTable[i].mfFile.vpStart = static_cast(vpbRes); - vrgffFileTable[i].mfFile.uiCurrent = 0; - vrgffFileTable[i].mfFile.uiLength = vcbRes; - } - else // it's a real file - { - hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - RexExitWithLastError(hr, "failed to open file: %s", pszFile); - } - - vrgffFileTable[i].fUsed = TRUE; - vrgffFileTable[i].fftType = NORMAL_FILE; - vrgffFileTable[i].hFile = hFile; - } - -LExit: - if (FAILED(hr)) - { - vhrLastError = hr; - } - - return FAILED(hr) ? -1 : i; -} - - -static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb) -{ - Assert(vrgffFileTable[hf].fUsed); - - HRESULT hr = S_OK; - DWORD cbRead = 0; - DWORD cbAvailable = 0; - - if (MEMORY_FILE == vrgffFileTable[hf].fftType) - { - // ensure that we don't read past the length of the resource - cbAvailable = vrgffFileTable[hf].mfFile.uiLength - vrgffFileTable[hf].mfFile.uiCurrent; - cbRead = cb < cbAvailable? cb : cbAvailable; - - memcpy(pv, static_cast(vrgffFileTable[hf].mfFile.vpStart + vrgffFileTable[hf].mfFile.uiCurrent), cbRead); - - vrgffFileTable[hf].mfFile.uiCurrent += cbRead; - } - else // NORMAL_FILE - { - Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); - - if (!::ReadFile(vrgffFileTable[hf].hFile, pv, cb, &cbRead, NULL)) - { - RexExitWithLastError(hr, "failed to read during cabinet extraction"); - } - } - -LExit: - if (FAILED(hr)) - { - vhrLastError = hr; - } - - return FAILED(hr) ? -1 : cbRead; -} - - -static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb) -{ - Assert(vrgffFileTable[hf].fUsed); - Assert(vrgffFileTable[hf].fftType == NORMAL_FILE); // we should never be writing to a memory file - - HRESULT hr = S_OK; - DWORD cbWrite = 0; - - Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); - if (!::WriteFile(reinterpret_cast(vrgffFileTable[hf].hFile), pv, cb, &cbWrite, NULL)) - { - RexExitWithLastError(hr, "failed to write during cabinet extraction"); - } - - // call the writer callback if defined - if (vpfnWrite) - { - vpfnWrite(cb); - } - -LExit: - if (FAILED(hr)) - { - vhrLastError = hr; - } - - return FAILED(hr) ? -1 : cbWrite; -} - - -static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype) -{ - Assert(vrgffFileTable[hf].fUsed); - - HRESULT hr = S_OK; - DWORD dwMoveMethod; - LONG lMove = 0; - - switch (seektype) - { - case 0: // SEEK_SET - dwMoveMethod = FILE_BEGIN; - break; - case 1: /// SEEK_CUR - dwMoveMethod = FILE_CURRENT; - break; - case 2: // SEEK_END - dwMoveMethod = FILE_END; - break; - default : - dwMoveMethod = 0; - hr = E_UNEXPECTED; - RexExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); - } - - if (MEMORY_FILE == vrgffFileTable[hf].fftType) - { - if (FILE_BEGIN == dwMoveMethod) - { - vrgffFileTable[hf].mfFile.uiCurrent = dist; - } - else if (FILE_CURRENT == dwMoveMethod) - { - vrgffFileTable[hf].mfFile.uiCurrent += dist; - } - else // FILE_END - { - vrgffFileTable[hf].mfFile.uiCurrent = vrgffFileTable[hf].mfFile.uiLength + dist; - } - - lMove = vrgffFileTable[hf].mfFile.uiCurrent; - } - else // NORMAL_FILE - { - Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); - - // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. - // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) - lMove = ::SetFilePointer(vrgffFileTable[hf].hFile, dist, NULL, dwMoveMethod); - if (0xFFFFFFFF == lMove) - { - RexExitWithLastError(hr, "failed to move file pointer %d bytes", dist); - } - } - -LExit: - if (FAILED(hr)) - { - vhrLastError = hr; - } - - return FAILED(hr) ? -1 : lMove; -} - - -__callback int FAR DIAMONDAPI RexClose(INT_PTR hf) -{ - Assert(vrgffFileTable[hf].fUsed); - - HRESULT hr = S_OK; - - if (MEMORY_FILE == vrgffFileTable[hf].fftType) - { - vrgffFileTable[hf].mfFile.vpStart = NULL; - vrgffFileTable[hf].mfFile.uiCurrent = 0; - vrgffFileTable[hf].mfFile.uiLength = 0; - } - else - { - Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); - - if (!::CloseHandle(vrgffFileTable[hf].hFile)) - { - RexExitWithLastError(hr, "failed to close file during cabinet extraction"); - } - - vrgffFileTable[hf].hFile = INVALID_HANDLE_VALUE; - } - - vrgffFileTable[hf].fUsed = FALSE; - -LExit: - if (FAILED(hr)) - { - vhrLastError = hr; - } - - return FAILED(hr) ? -1 : 0; -} - - -static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify) -{ - Assert(pFDINotify->pv); - - HRESULT hr = S_OK; - int ipResult = 0; // result to return on success - HANDLE hFile = INVALID_HANDLE_VALUE; - - REX_CALLBACK_STRUCT* prcs = static_cast(pFDINotify->pv); - LPCSTR sz; - WCHAR wz[MAX_PATH]; - FILETIME ft; - int i = 0; - - switch (iNotification) - { - case fdintCOPY_FILE: // beGIN extracting a resource from cabinet - Assert(pFDINotify->psz1); - - if (prcs->fStopExtracting) - { - ExitFunction1(hr = S_FALSE); // no more extracting - } - - // convert params to useful variables - sz = static_cast(pFDINotify->psz1); - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) - { - RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); - } - - if (prcs->pfnProgress) - { - hr = prcs->pfnProgress(TRUE, wz, prcs->pvContext); - if (S_OK != hr) - { - ExitFunction(); - } - } - - if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, wz)) - { - // get the created date for the resource in the cabinet - if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft)) - { - RexExitWithLastError(hr, "failed to get time for resource: %ls", wz); - } - - WCHAR wzPath[MAX_PATH]; - - hr = ::StringCchCopyW(wzPath, countof(wzPath), prcs->pwzExtractDir); - RexExitOnFailure(hr, "failed to copy extract directory: %ls for file: %ls", prcs->pwzExtractDir, wz); - - if (L'*' == *prcs->pwzExtract) - { - hr = ::StringCchCatW(wzPath, countof(wzPath), wz); - RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); - } - else - { - Assert(*prcs->pwzExtractName); - - hr = ::StringCchCatW(wzPath, countof(wzPath), prcs->pwzExtractName); - RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName); - } - - // Quickly chop off the file name part of the path to ensure the path exists - // then put the file name back on the path (by putting the first character - // back over the null terminator). - LPWSTR wzFile = PathFile(wzPath); - WCHAR wzFileFirstChar = *wzFile; - *wzFile = L'\0'; - - hr = DirEnsureExists(wzPath, NULL); - RexExitOnFailure(hr, "failed to ensure directory: %ls", wzPath); - - hr = S_OK; - - *wzFile = wzFileFirstChar; - - // find an empty spot in the fake file table - for (i = 0; i < FILETABLESIZE; ++i) - { - if (!vrgffFileTable[i].fUsed) - { - break; - } - } - - // we should never run out of space in the fake file table - if (FILETABLESIZE <= i) - { - hr = E_OUTOFMEMORY; - RexExitOnFailure(hr, "File table exceeded"); - } - - // open the file - hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - RexExitWithLastError(hr, "failed to open file: %ls", wzPath); - } - - vrgffFileTable[i].fUsed = TRUE; - vrgffFileTable[i].fftType = NORMAL_FILE; - vrgffFileTable[i].hFile = hFile; - - ipResult = i; - - ::SetFileTime(vrgffFileTable[i].hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails) - - if (::SetFilePointer(vrgffFileTable[i].hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) - { - if (::SetEndOfFile(vrgffFileTable[i].hFile)) - { - ::SetFilePointer(vrgffFileTable[i].hFile, 0, NULL, FILE_BEGIN); // reset the file pointer - } - } - } - else // resource wasn't requested, skip it - { - hr = S_OK; - ipResult = 0; - } - - break; - case fdintCLOSE_FILE_INFO: // resource extraction complete - Assert(pFDINotify->hf && pFDINotify->psz1); - - // convert params to useful variables - sz = static_cast(pFDINotify->psz1); - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) - { - RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); - } - - RexClose(pFDINotify->hf); - - if (prcs->pfnProgress) - { - hr = prcs->pfnProgress(FALSE, wz, prcs->pvContext); - } - - if (S_OK == hr && L'*' == *prcs->pwzExtract) // if everything is okay and we're extracting all files, keep going - { - ipResult = TRUE; - } - else // something went wrong or we only needed to extract one file - { - hr = S_OK; - ipResult = FALSE; - prcs->fStopExtracting = TRUE; - } - - break; - case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through - case fdintNEXT_CABINET: __fallthrough; - case fdintENUMERATE: __fallthrough; - case fdintCABINET_INFO: - break; - default: - AssertSz(FALSE, "RexCallback() - unknown FDI notification command"); - }; - -LExit: - if (FAILED(hr)) - { - vhrLastError = hr; - } - - return (S_OK == hr) ? ipResult : -1; -} diff --git a/src/dutil/rmutil.cpp b/src/dutil/rmutil.cpp deleted file mode 100644 index 95c8c8a4..00000000 --- a/src/dutil/rmutil.cpp +++ /dev/null @@ -1,488 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" -#include - - -// Exit macros -#define RmExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) -#define RmExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) -#define RmExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) -#define RmExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) -#define RmExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) -#define RmExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) -#define RmExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RMUTIL, p, x, e, s, __VA_ARGS__) -#define RmExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RMUTIL, p, x, s, __VA_ARGS__) -#define RmExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RMUTIL, p, x, e, s, __VA_ARGS__) -#define RmExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RMUTIL, p, x, s, __VA_ARGS__) -#define RmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RMUTIL, e, x, s, __VA_ARGS__) -#define RmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RMUTIL, g, x, s, __VA_ARGS__) - -#define ARRAY_GROWTH_SIZE 5 - -typedef DWORD (WINAPI *PFNRMJOINSESSION)( - __out DWORD *pSessionHandle, - __in_z const WCHAR strSessionKey[] - ); - -typedef DWORD (WINAPI *PFNRMENDSESSION)( - __in DWORD dwSessionHandle - ); - -typedef DWORD (WINAPI *PFNRMREGISTERRESOURCES)( - __in DWORD dwSessionHandle, - __in UINT nFiles, - __in_z_opt LPWSTR *rgsFilenames, - __in UINT nApplications, - __in_opt RM_UNIQUE_PROCESS *rgApplications, - __in UINT nServices, - __in_z_opt LPWSTR *rgsServiceNames - ); - -typedef struct _RMU_SESSION -{ - CRITICAL_SECTION cs; - DWORD dwSessionHandle; - BOOL fStartedSessionHandle; - BOOL fInitialized; - - UINT cFilenames; - LPWSTR *rgsczFilenames; - - UINT cApplications; - RM_UNIQUE_PROCESS *rgApplications; - - UINT cServiceNames; - LPWSTR *rgsczServiceNames; - -} RMU_SESSION; - -static volatile LONG vcRmuInitialized = 0; -static HMODULE vhModule = NULL; -static PFNRMJOINSESSION vpfnRmJoinSession = NULL; -static PFNRMENDSESSION vpfnRmEndSession = NULL; -static PFNRMREGISTERRESOURCES vpfnRmRegisterResources = NULL; - -static HRESULT RmuInitialize(); -static void RmuUninitialize(); - -static HRESULT RmuApplicationArrayAlloc( - __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications, - __inout LPUINT pcApplications, - __in DWORD dwProcessId, - __in FILETIME ProcessStartTime - ); - -static HRESULT RmuApplicationArrayFree( - __in RM_UNIQUE_PROCESS *rgApplications - ); - -#define ReleaseNullApplicationArray(rg, c) { if (rg) { RmuApplicationArrayFree(rg); c = 0; rg = NULL; } } - -/******************************************************************** -RmuJoinSession - Joins an existing Restart Manager session. - -********************************************************************/ -extern "C" HRESULT DAPI RmuJoinSession( - __out PRMU_SESSION *ppSession, - __in_z LPCWSTR wzSessionKey - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - PRMU_SESSION pSession = NULL; - - *ppSession = NULL; - - pSession = static_cast(MemAlloc(sizeof(RMU_SESSION), TRUE)); - RmExitOnNull(pSession, hr, E_OUTOFMEMORY, "Failed to allocate the RMU_SESSION structure."); - - hr = RmuInitialize(); - RmExitOnFailure(hr, "Failed to initialize Restart Manager."); - - er = vpfnRmJoinSession(&pSession->dwSessionHandle, wzSessionKey); - RmExitOnWin32Error(er, hr, "Failed to join Restart Manager session %ls.", wzSessionKey); - - ::InitializeCriticalSection(&pSession->cs); - pSession->fInitialized = TRUE; - - *ppSession = pSession; - -LExit: - if (FAILED(hr)) - { - ReleaseNullMem(pSession); - } - - return hr; -} - -/******************************************************************** -RmuAddFile - Adds the file path to the Restart Manager session. - -You should call this multiple times as necessary before calling -RmuRegisterResources. - -********************************************************************/ -extern "C" HRESULT DAPI RmuAddFile( - __in PRMU_SESSION pSession, - __in_z LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - - ::EnterCriticalSection(&pSession->cs); - - // Create or grow the jagged array. - hr = StrArrayAllocString(&pSession->rgsczFilenames, &pSession->cFilenames, wzPath, 0); - RmExitOnFailure(hr, "Failed to add the filename to the array."); - -LExit: - ::LeaveCriticalSection(&pSession->cs); - return hr; -} - -/******************************************************************** -RmuAddProcessById - Adds the process ID to the Restart Manager sesion. - -You should call this multiple times as necessary before calling -RmuRegisterResources. - -********************************************************************/ -extern "C" HRESULT DAPI RmuAddProcessById( - __in PRMU_SESSION pSession, - __in DWORD dwProcessId - ) -{ - HRESULT hr = S_OK; - HANDLE hProcess = NULL; - FILETIME CreationTime = {}; - FILETIME ExitTime = {}; - FILETIME KernelTime = {}; - FILETIME UserTime = {}; - BOOL fLocked = FALSE; - - HANDLE hToken = NULL; - TOKEN_PRIVILEGES priv = { 0 }; - TOKEN_PRIVILEGES* pPrevPriv = NULL; - DWORD cbPrevPriv = 0; - DWORD er = ERROR_SUCCESS; - BOOL fAdjustedPrivileges = FALSE; - BOOL fElevated = FALSE; - ProcElevated(::GetCurrentProcess(), &fElevated); - - // Must be elevated to adjust process privileges - if (fElevated) { - // Adding SeDebugPrivilege in the event that the process targeted by ::OpenProcess() is in a another user context. - if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) - { - RmExitWithLastError(hr, "Failed to get process token."); - } - - priv.PrivilegeCount = 1; - priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (!::LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &priv.Privileges[0].Luid)) - { - RmExitWithLastError(hr, "Failed to get debug privilege LUID."); - } - - cbPrevPriv = sizeof(TOKEN_PRIVILEGES); - pPrevPriv = static_cast(MemAlloc(cbPrevPriv, TRUE)); - RmExitOnNull(pPrevPriv, hr, E_OUTOFMEMORY, "Failed to allocate memory for empty previous privileges."); - - if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) - { - LPVOID pv = MemReAlloc(pPrevPriv, cbPrevPriv, TRUE); - RmExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for previous privileges."); - pPrevPriv = static_cast(pv); - - if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) - { - RmExitWithLastError(hr, "Failed to get debug privilege LUID."); - } - } - - fAdjustedPrivileges = TRUE; - } - - hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId); - if (hProcess) - { - if (!::GetProcessTimes(hProcess, &CreationTime, &ExitTime, &KernelTime, &UserTime)) - { - RmExitWithLastError(hr, "Failed to get the process times for process ID %d.", dwProcessId); - } - - ::EnterCriticalSection(&pSession->cs); - fLocked = TRUE; - hr = RmuApplicationArrayAlloc(&pSession->rgApplications, &pSession->cApplications, dwProcessId, CreationTime); - RmExitOnFailure(hr, "Failed to add the application to the array."); - } - else - { - er = ::GetLastError(); - if (ERROR_ACCESS_DENIED == er) - { - // OpenProcess will fail when not elevated and the target process is in another user context. Let the caller log and continue. - hr = E_NOTFOUND; - } - else - { - RmExitOnWin32Error(er, hr, "Failed to open the process ID %d.", dwProcessId); - } - } - -LExit: - if (hProcess) - { - ::CloseHandle(hProcess); - } - - if (fAdjustedPrivileges) - { - ::AdjustTokenPrivileges(hToken, FALSE, pPrevPriv, 0, NULL, NULL); - } - - ReleaseMem(pPrevPriv); - ReleaseHandle(hToken); - - if (fLocked) - { - ::LeaveCriticalSection(&pSession->cs); - } - - return hr; -} - -/******************************************************************** -RmuAddProcessesByName - Adds all processes by the given process name - to the Restart Manager Session. - -You should call this multiple times as necessary before calling -RmuRegisterResources. - -********************************************************************/ -extern "C" HRESULT DAPI RmuAddProcessesByName( - __in PRMU_SESSION pSession, - __in_z LPCWSTR wzProcessName - ) -{ - HRESULT hr = S_OK; - DWORD *pdwProcessIds = NULL; - DWORD cProcessIds = 0; - BOOL fNotFound = FALSE; - - hr = ProcFindAllIdsFromExeName(wzProcessName, &pdwProcessIds, &cProcessIds); - RmExitOnFailure(hr, "Failed to enumerate all the processes by name %ls.", wzProcessName); - - for (DWORD i = 0; i < cProcessIds; ++i) - { - hr = RmuAddProcessById(pSession, pdwProcessIds[i]); - if (E_NOTFOUND == hr) - { - // RmuAddProcessById returns E_NOTFOUND when this setup is not elevated and OpenProcess returned access denied (target process running under another user account). - fNotFound = TRUE; - } - else - { - RmExitOnFailure(hr, "Failed to add process %ls (%d) to the Restart Manager session.", wzProcessName, pdwProcessIds[i]); - } - } - - // If one or more calls to RmuAddProcessById returned E_NOTFOUND, then return E_NOTFOUND even if other calls succeeded, so that caller can log the issue. - if (fNotFound) - { - hr = E_NOTFOUND; - } - -LExit: - ReleaseMem(pdwProcessIds); - - return hr; -} - -/******************************************************************** -RmuAddService - Adds the service name to the Restart Manager session. - -You should call this multiple times as necessary before calling -RmuRegisterResources. - -********************************************************************/ -extern "C" HRESULT DAPI RmuAddService( - __in PRMU_SESSION pSession, - __in_z LPCWSTR wzServiceName - ) -{ - HRESULT hr = S_OK; - - ::EnterCriticalSection(&pSession->cs); - - hr = StrArrayAllocString(&pSession->rgsczServiceNames, &pSession->cServiceNames, wzServiceName, 0); - RmExitOnFailure(hr, "Failed to add the service name to the array."); - -LExit: - ::LeaveCriticalSection(&pSession->cs); - return hr; -} - -/******************************************************************** -RmuRegisterResources - Registers resources for the Restart Manager. - -This should be called rarely because it is expensive to run. Call -functions like RmuAddFile for multiple resources then commit them -as a batch of updates to RmuRegisterResources. - -Duplicate resources appear to be handled by Restart Manager. -Only one WM_QUERYENDSESSION is being sent for each top-level window. - -********************************************************************/ -extern "C" HRESULT DAPI RmuRegisterResources( - __in PRMU_SESSION pSession - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized."); - - ::EnterCriticalSection(&pSession->cs); - - er = vpfnRmRegisterResources( - pSession->dwSessionHandle, - pSession->cFilenames, - pSession->rgsczFilenames, - pSession->cApplications, - pSession->rgApplications, - pSession->cServiceNames, - pSession->rgsczServiceNames - ); - RmExitOnWin32Error(er, hr, "Failed to register the resources with the Restart Manager session."); - - // Empty the arrays if registered in case additional resources are added later. - ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); - ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications); - ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames); - -LExit: - ::LeaveCriticalSection(&pSession->cs); - return hr; -} - -/******************************************************************** -RmuEndSession - Ends the session. - -If the session was joined by RmuJoinSession, any remaining resources -are registered before the session is ended (left). - -********************************************************************/ -extern "C" HRESULT DAPI RmuEndSession( - __in PRMU_SESSION pSession - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized."); - - // Make sure all resources are registered if we joined the session. - if (!pSession->fStartedSessionHandle) - { - hr = RmuRegisterResources(pSession); - RmExitOnFailure(hr, "Failed to register remaining resources."); - } - - er = vpfnRmEndSession(pSession->dwSessionHandle); - RmExitOnWin32Error(er, hr, "Failed to end the Restart Manager session."); - -LExit: - if (pSession->fInitialized) - { - ::DeleteCriticalSection(&pSession->cs); - } - - ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); - ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications); - ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames); - ReleaseNullMem(pSession); - - RmuUninitialize(); - - return hr; -} - -static HRESULT RmuInitialize() -{ - HRESULT hr = S_OK; - HMODULE hModule = NULL; - - LONG iRef = ::InterlockedIncrement(&vcRmuInitialized); - if (1 == iRef && !vhModule) - { - hr = LoadSystemLibrary(L"rstrtmgr.dll", &hModule); - RmExitOnFailure(hr, "Failed to load the rstrtmgr.dll module."); - - vpfnRmJoinSession = reinterpret_cast(::GetProcAddress(hModule, "RmJoinSession")); - RmExitOnNullWithLastError(vpfnRmJoinSession, hr, "Failed to get the RmJoinSession procedure from rstrtmgr.dll."); - - vpfnRmRegisterResources = reinterpret_cast(::GetProcAddress(hModule, "RmRegisterResources")); - RmExitOnNullWithLastError(vpfnRmRegisterResources, hr, "Failed to get the RmRegisterResources procedure from rstrtmgr.dll."); - - vpfnRmEndSession = reinterpret_cast(::GetProcAddress(hModule, "RmEndSession")); - RmExitOnNullWithLastError(vpfnRmEndSession, hr, "Failed to get the RmEndSession procedure from rstrtmgr.dll."); - - vhModule = hModule; - } - -LExit: - return hr; -} - -static void RmuUninitialize() -{ - LONG iRef = ::InterlockedDecrement(&vcRmuInitialized); - if (0 == iRef && vhModule) - { - vpfnRmJoinSession = NULL; - vpfnRmEndSession = NULL; - vpfnRmRegisterResources = NULL; - - ::FreeLibrary(vhModule); - vhModule = NULL; - } -} - -static HRESULT RmuApplicationArrayAlloc( - __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications, - __inout LPUINT pcApplications, - __in DWORD dwProcessId, - __in FILETIME ProcessStartTime - ) -{ - HRESULT hr = S_OK; - RM_UNIQUE_PROCESS *pApplication = NULL; - - hr = MemEnsureArraySize(reinterpret_cast(prgApplications), *pcApplications + 1, sizeof(RM_UNIQUE_PROCESS), ARRAY_GROWTH_SIZE); - RmExitOnFailure(hr, "Failed to allocate memory for the application array."); - - pApplication = static_cast(&(*prgApplications)[*pcApplications]); - pApplication->dwProcessId = dwProcessId; - pApplication->ProcessStartTime = ProcessStartTime; - - ++(*pcApplications); - -LExit: - return hr; -} - -static HRESULT RmuApplicationArrayFree( - __in RM_UNIQUE_PROCESS *rgApplications - ) -{ - HRESULT hr = S_OK; - - hr = MemFree(rgApplications); - RmExitOnFailure(hr, "Failed to free memory for the application array."); - -LExit: - return hr; -} diff --git a/src/dutil/rssutil.cpp b/src/dutil/rssutil.cpp deleted file mode 100644 index 8f994dfc..00000000 --- a/src/dutil/rssutil.cpp +++ /dev/null @@ -1,647 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define RssExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) -#define RssExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) -#define RssExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) -#define RssExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) -#define RssExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) -#define RssExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) -#define RssExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__) -#define RssExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__) -#define RssExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__) -#define RssExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__) -#define RssExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RSSUTIL, e, x, s, __VA_ARGS__) -#define RssExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RSSUTIL, g, x, s, __VA_ARGS__) - -static HRESULT ParseRssDocument( - __in IXMLDOMDocument *pixd, - __out RSS_CHANNEL **ppChannel - ); -static HRESULT ParseRssChannel( - __in IXMLDOMNode *pixnChannel, - __out RSS_CHANNEL **ppChannel - ); -static HRESULT ParseRssItem( - __in IXMLDOMNode *pixnItem, - __in DWORD cItem, - __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel - ); -static HRESULT ParseRssUnknownElement( - __in IXMLDOMNode *pNode, - __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement - ); -static HRESULT ParseRssUnknownAttribute( - __in IXMLDOMNode *pNode, - __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute - ); -static void FreeRssUnknownElementList( - __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement - ); -static void FreeRssUnknownAttributeList( - __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute - ); - - -/******************************************************************** - RssInitialize - Initialize RSS utilities. - -*********************************************************************/ -extern "C" HRESULT DAPI RssInitialize() -{ - return XmlInitialize(); -} - - -/******************************************************************** - RssUninitialize - Uninitialize RSS utilities. - -*********************************************************************/ -extern "C" void DAPI RssUninitialize() -{ - XmlUninitialize(); -} - - -/******************************************************************** - RssParseFromString - parses out an RSS channel from a string. - -*********************************************************************/ -extern "C" HRESULT DAPI RssParseFromString( - __in_z LPCWSTR wzRssString, - __out RSS_CHANNEL **ppChannel - ) -{ - Assert(wzRssString); - Assert(ppChannel); - - HRESULT hr = S_OK; - RSS_CHANNEL *pNewChannel = NULL; - IXMLDOMDocument *pixdRss = NULL; - - hr = XmlLoadDocument(wzRssString, &pixdRss); - RssExitOnFailure(hr, "Failed to load RSS string as XML document."); - - hr = ParseRssDocument(pixdRss, &pNewChannel); - RssExitOnFailure(hr, "Failed to parse RSS document."); - - *ppChannel = pNewChannel; - pNewChannel = NULL; - -LExit: - ReleaseObject(pixdRss); - - ReleaseRssChannel(pNewChannel); - - return hr; -} - - -/******************************************************************** - RssParseFromFile - parses out an RSS channel from a file path. - -*********************************************************************/ -extern "C" HRESULT DAPI RssParseFromFile( - __in_z LPCWSTR wzRssFile, - __out RSS_CHANNEL **ppChannel - ) -{ - Assert(wzRssFile); - Assert(ppChannel); - - HRESULT hr = S_OK; - RSS_CHANNEL *pNewChannel = NULL; - IXMLDOMDocument *pixdRss = NULL; - - hr = XmlLoadDocumentFromFile(wzRssFile, &pixdRss); - RssExitOnFailure(hr, "Failed to load RSS string as XML document."); - - hr = ParseRssDocument(pixdRss, &pNewChannel); - RssExitOnFailure(hr, "Failed to parse RSS document."); - - *ppChannel = pNewChannel; - pNewChannel = NULL; - -LExit: - ReleaseObject(pixdRss); - - ReleaseRssChannel(pNewChannel); - - return hr; -} - - -/******************************************************************** - RssFreeChannel - parses out an RSS channel from a string. - -*********************************************************************/ -extern "C" void DAPI RssFreeChannel( - __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel - ) -{ - if (pChannel) - { - for (DWORD i = 0; i < pChannel->cItems; ++i) - { - ReleaseStr(pChannel->rgItems[i].wzTitle); - ReleaseStr(pChannel->rgItems[i].wzLink); - ReleaseStr(pChannel->rgItems[i].wzDescription); - ReleaseStr(pChannel->rgItems[i].wzGuid); - ReleaseStr(pChannel->rgItems[i].wzEnclosureUrl); - ReleaseStr(pChannel->rgItems[i].wzEnclosureType); - - FreeRssUnknownElementList(pChannel->rgItems[i].pUnknownElements); - } - - ReleaseStr(pChannel->wzTitle); - ReleaseStr(pChannel->wzLink); - ReleaseStr(pChannel->wzDescription); - FreeRssUnknownElementList(pChannel->pUnknownElements); - - MemFree(pChannel); - } -} - - -/******************************************************************** - ParseRssDocument - parses out an RSS channel from a loaded XML DOM document. - -*********************************************************************/ -static HRESULT ParseRssDocument( - __in IXMLDOMDocument *pixd, - __out RSS_CHANNEL **ppChannel - ) -{ - Assert(pixd); - Assert(ppChannel); - - HRESULT hr = S_OK; - IXMLDOMElement *pRssElement = NULL; - IXMLDOMNodeList *pChannelNodes = NULL; - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - - RSS_CHANNEL *pNewChannel = NULL; - - // - // Get the document element and start processing channels. - // - hr = pixd ->get_documentElement(&pRssElement); - RssExitOnFailure(hr, "failed get_documentElement in ParseRssDocument"); - - hr = pRssElement->get_childNodes(&pChannelNodes); - RssExitOnFailure(hr, "Failed to get child nodes of Rss Document element."); - - while (S_OK == (hr = XmlNextElement(pChannelNodes, &pNode, &bstrNodeName))) - { - if (0 == lstrcmpW(bstrNodeName, L"channel")) - { - hr = ParseRssChannel(pNode, &pNewChannel); - RssExitOnFailure(hr, "Failed to parse RSS channel."); - } - else if (0 == lstrcmpW(bstrNodeName, L"link")) - { - } - - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - - if (S_FALSE == hr) - { - hr = S_OK; - } - - *ppChannel = pNewChannel; - pNewChannel = NULL; - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pChannelNodes); - ReleaseObject(pRssElement); - - ReleaseRssChannel(pNewChannel); - - return hr; -} - - -/******************************************************************** - ParseRssChannel - parses out an RSS channel from a loaded XML DOM element. - -*********************************************************************/ -static HRESULT ParseRssChannel( - __in IXMLDOMNode *pixnChannel, - __out RSS_CHANNEL **ppChannel - ) -{ - Assert(pixnChannel); - Assert(ppChannel); - - HRESULT hr = S_OK; - IXMLDOMNodeList *pNodeList = NULL; - - RSS_CHANNEL *pNewChannel = NULL; - long cItems = 0; - - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - BSTR bstrNodeValue = NULL; - - // - // First, calculate how many RSS items we're going to have and allocate - // the RSS_CHANNEL structure - // - hr = XmlSelectNodes(pixnChannel, L"item", &pNodeList); - RssExitOnFailure(hr, "Failed to select all RSS items in an RSS channel."); - - hr = pNodeList->get_length(&cItems); - RssExitOnFailure(hr, "Failed to count the number of RSS items in RSS channel."); - - pNewChannel = static_cast(MemAlloc(sizeof(RSS_CHANNEL) + sizeof(RSS_ITEM) * cItems, TRUE)); - RssExitOnNull(pNewChannel, hr, E_OUTOFMEMORY, "Failed to allocate RSS channel structure."); - - pNewChannel->cItems = cItems; - - // - // Process the elements under a channel now. - // - hr = pixnChannel->get_childNodes(&pNodeList); - RssExitOnFailure(hr, "Failed to get child nodes of RSS channel element."); - - cItems = 0; // reset the counter and use this to walk through the channel items - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - if (0 == lstrcmpW(bstrNodeName, L"title")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS channel title."); - - hr = StrAllocString(&pNewChannel->wzTitle, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS channel title."); - } - else if (0 == lstrcmpW(bstrNodeName, L"link")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS channel link."); - - hr = StrAllocString(&pNewChannel->wzLink, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS channel link."); - } - else if (0 == lstrcmpW(bstrNodeName, L"description")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS channel description."); - - hr = StrAllocString(&pNewChannel->wzDescription, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS channel description."); - } - else if (0 == lstrcmpW(bstrNodeName, L"ttl")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS channel description."); - - pNewChannel->dwTimeToLive = (DWORD)wcstoul(bstrNodeValue, NULL, 10); - } - else if (0 == lstrcmpW(bstrNodeName, L"item")) - { - hr = ParseRssItem(pNode, cItems, pNewChannel); - RssExitOnFailure(hr, "Failed to parse RSS item."); - - ++cItems; - } - else - { - hr = ParseRssUnknownElement(pNode, &pNewChannel->pUnknownElements); - RssExitOnFailure(hr, "Failed to parse unknown RSS channel element: %ls", bstrNodeName); - } - - ReleaseNullBSTR(bstrNodeValue); - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - - *ppChannel = pNewChannel; - pNewChannel = NULL; - -LExit: - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - - ReleaseRssChannel(pNewChannel); - - return hr; -} - - -/******************************************************************** - ParseRssItem - parses out an RSS item from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseRssItem( - __in IXMLDOMNode *pixnItem, - __in DWORD cItem, - __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel - ) -{ - HRESULT hr = S_OK; - - RSS_ITEM *pItem = NULL; - IXMLDOMNodeList *pNodeList = NULL; - - IXMLDOMNode *pNode = NULL; - BSTR bstrNodeName = NULL; - BSTR bstrNodeValue = NULL; - - // - // First make sure we're dealing with a valid item. - // - if (pChannel->cItems <= cItem) - { - hr = E_UNEXPECTED; - RssExitOnFailure(hr, "Unexpected number of items parsed."); - } - - pItem = pChannel->rgItems + cItem; - - // - // Process the elements under an item now. - // - hr = pixnItem->get_childNodes(&pNodeList); - RssExitOnFailure(hr, "Failed to get child nodes of RSS item element."); - while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) - { - if (0 == lstrcmpW(bstrNodeName, L"title")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS channel title."); - - hr = StrAllocString(&pItem->wzTitle, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS item title."); - } - else if (0 == lstrcmpW(bstrNodeName, L"link")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS channel link."); - - hr = StrAllocString(&pItem->wzLink, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS item link."); - } - else if (0 == lstrcmpW(bstrNodeName, L"description")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS item description."); - - hr = StrAllocString(&pItem->wzDescription, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS item description."); - } - else if (0 == lstrcmpW(bstrNodeName, L"guid")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS item guid."); - - hr = StrAllocString(&pItem->wzGuid, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS item guid."); - } - else if (0 == lstrcmpW(bstrNodeName, L"pubDate")) - { - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS item guid."); - - hr = TimeFromString(bstrNodeValue, &pItem->ftPublished); - RssExitOnFailure(hr, "Failed to convert RSS item time."); - } - else if (0 == lstrcmpW(bstrNodeName, L"enclosure")) - { - hr = XmlGetAttribute(pNode, L"url", &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS item enclosure url."); - - hr = StrAllocString(&pItem->wzEnclosureUrl, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS item enclosure url."); - ReleaseNullBSTR(bstrNodeValue); - - hr = XmlGetAttributeNumber(pNode, L"length", &pItem->dwEnclosureSize); - RssExitOnFailure(hr, "Failed to get RSS item enclosure length."); - - hr = XmlGetAttribute(pNode, L"type", &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get RSS item enclosure type."); - - hr = StrAllocString(&pItem->wzEnclosureType, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS item enclosure type."); - } - else - { - hr = ParseRssUnknownElement(pNode, &pItem->pUnknownElements); - RssExitOnFailure(hr, "Failed to parse unknown RSS item element: %ls", bstrNodeName); - } - - ReleaseNullBSTR(bstrNodeValue); - ReleaseNullBSTR(bstrNodeName); - ReleaseNullObject(pNode); - } - -LExit: - ReleaseBSTR(bstrNodeValue); - ReleaseBSTR(bstrNodeName); - ReleaseObject(pNode); - ReleaseObject(pNodeList); - - return hr; -} - - -/******************************************************************** - ParseRssUnknownElement - parses out an unknown item from the RSS feed from a loaded XML DOM node. - -*********************************************************************/ -static HRESULT ParseRssUnknownElement( - __in IXMLDOMNode *pNode, - __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement - ) -{ - Assert(ppUnknownElement); - - HRESULT hr = S_OK; - BSTR bstrNodeNamespace = NULL; - BSTR bstrNodeName = NULL; - BSTR bstrNodeValue = NULL; - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNode* pixnAttribute = NULL; - RSS_UNKNOWN_ELEMENT* pNewUnknownElement; - - pNewUnknownElement = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ELEMENT), TRUE)); - RssExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); - - hr = pNode->get_namespaceURI(&bstrNodeNamespace); - if (S_OK == hr) - { - hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); - RssExitOnFailure(hr, "Failed to allocate RSS unknown element namespace."); - } - else if (S_FALSE == hr) - { - hr = S_OK; - } - RssExitOnFailure(hr, "Failed to get unknown element namespace."); - - hr = pNode->get_baseName(&bstrNodeName); - RssExitOnFailure(hr, "Failed to get unknown element name."); - - hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); - RssExitOnFailure(hr, "Failed to allocate RSS unknown element name."); - - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get unknown element value."); - - hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS unknown element value."); - - hr = pNode->get_attributes(&pixnnmAttributes); - RssExitOnFailure(hr, "Failed get attributes on RSS unknown element."); - - while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) - { - hr = ParseRssUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); - RssExitOnFailure(hr, "Failed to parse attribute on RSS unknown element."); - - ReleaseNullObject(pixnAttribute); - } - - if (S_FALSE == hr) - { - hr = S_OK; - } - RssExitOnFailure(hr, "Failed to enumerate all attributes on RSS unknown element."); - - RSS_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; - while (*ppTail) - { - ppTail = &(*ppTail)->pNext; - } - - *ppTail = pNewUnknownElement; - pNewUnknownElement = NULL; - -LExit: - FreeRssUnknownElementList(pNewUnknownElement); - - ReleaseBSTR(bstrNodeNamespace); - ReleaseBSTR(bstrNodeName); - ReleaseBSTR(bstrNodeValue); - ReleaseObject(pixnnmAttributes); - ReleaseObject(pixnAttribute); - - return hr; -} - - -/******************************************************************** - ParseRssUnknownAttribute - parses out attribute from an unknown element - -*********************************************************************/ -static HRESULT ParseRssUnknownAttribute( - __in IXMLDOMNode *pNode, - __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute - ) -{ - Assert(ppUnknownAttribute); - - HRESULT hr = S_OK; - BSTR bstrNodeNamespace = NULL; - BSTR bstrNodeName = NULL; - BSTR bstrNodeValue = NULL; - RSS_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; - - pNewUnknownAttribute = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ATTRIBUTE), TRUE)); - RssExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); - - hr = pNode->get_namespaceURI(&bstrNodeNamespace); - if (S_OK == hr) - { - hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); - RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute namespace."); - } - else if (S_FALSE == hr) - { - hr = S_OK; - } - RssExitOnFailure(hr, "Failed to get unknown attribute namespace."); - - hr = pNode->get_baseName(&bstrNodeName); - RssExitOnFailure(hr, "Failed to get unknown attribute name."); - - hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); - RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute name."); - - hr = XmlGetText(pNode, &bstrNodeValue); - RssExitOnFailure(hr, "Failed to get unknown attribute value."); - - hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); - RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute value."); - - RSS_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; - while (*ppTail) - { - ppTail = &(*ppTail)->pNext; - } - - *ppTail = pNewUnknownAttribute; - pNewUnknownAttribute = NULL; - -LExit: - FreeRssUnknownAttributeList(pNewUnknownAttribute); - - ReleaseBSTR(bstrNodeNamespace); - ReleaseBSTR(bstrNodeName); - ReleaseBSTR(bstrNodeValue); - - return hr; -} - - -/******************************************************************** - FreeRssUnknownElement - releases all of the memory used by a list of unknown elements - -*********************************************************************/ -static void FreeRssUnknownElementList( - __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement - ) -{ - while (pUnknownElement) - { - RSS_UNKNOWN_ELEMENT* pFree = pUnknownElement; - pUnknownElement = pUnknownElement->pNext; - - FreeRssUnknownAttributeList(pFree->pAttributes); - ReleaseStr(pFree->wzNamespace); - ReleaseStr(pFree->wzElement); - ReleaseStr(pFree->wzValue); - MemFree(pFree); - } -} - - -/******************************************************************** - FreeRssUnknownAttribute - releases all of the memory used by a list of unknown attributes - -*********************************************************************/ -static void FreeRssUnknownAttributeList( - __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute - ) -{ - while (pUnknownAttribute) - { - RSS_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute; - pUnknownAttribute = pUnknownAttribute->pNext; - - ReleaseStr(pFree->wzNamespace); - ReleaseStr(pFree->wzAttribute); - ReleaseStr(pFree->wzValue); - MemFree(pFree); - } -} diff --git a/src/dutil/sceutil.cpp b/src/dutil/sceutil.cpp deleted file mode 100644 index cdb1623b..00000000 --- a/src/dutil/sceutil.cpp +++ /dev/null @@ -1,2489 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -#include "sceutil.h" - -// Limit is documented as 4 GB, but for some reason the API's don't let us specify anything above 4091 MB. -#define MAX_SQLCE_DATABASE_SIZE 4091 - -// In case of some older versions of sqlce_oledb.h don't have these definitions, define some types. -#ifndef DBTYPEFOR_DBLENGTH -#ifdef _WIN64 -#define SKIP_SCE_COMPILE -#else -#define SCE_32BIT_ONLY -typedef DWORD DBLENGTH; -typedef LONG DBROWOFFSET; -typedef LONG DBROWCOUNT; -typedef DWORD DBCOUNTITEM; -typedef DWORD DBORDINAL; -typedef LONG DB_LORDINAL; -typedef DWORD DBBKMARK; -typedef DWORD DBBYTEOFFSET; -typedef DWORD DBREFCOUNT; -typedef DWORD DB_UPARAMS; -typedef LONG DB_LPARAMS; -typedef DWORD DBHASHVALUE; -typedef DWORD DB_DWRESERVE; -typedef LONG DB_LRESERVE; -typedef DWORD DB_URESERVE; -#endif - -#endif - -#ifndef SKIP_SCE_COMPILE // If the sce headers don't support 64-bit, don't build for 64-bit - -// structs -struct SCE_DATABASE_INTERNAL -{ - // In case we call DllGetClassObject on a specific file - HMODULE hSqlCeDll; - - volatile LONG dwTransactionRefcount; - IDBInitialize *pIDBInitialize; - IDBCreateSession *pIDBCreateSession; - ITransactionLocal *pITransactionLocal; - IDBProperties *pIDBProperties; - IOpenRowset *pIOpenRowset; - ISessionProperties *pISessionProperties; - - BOOL fChanges; // This database has changed - BOOL fPendingChanges; // Some changes are pending, upon transaction commit - BOOL fRollbackTransaction; // If this flag is true, the current transaction was requested to be rolled back - BOOL fTransactionBadState; // If this flag is true, we were unable to get out of a transaction, so starting a new transaction should fail - - // If the database was opened as read-only, we copied it here - so delete it on close - LPWSTR sczTempDbFile; -}; - -struct SCE_ROW -{ - SCE_DATABASE_INTERNAL *pDatabaseInternal; - - SCE_TABLE_SCHEMA *pTableSchema; - IRowset *pIRowset; - HROW hRow; - BOOL fInserting; - - DWORD dwBindingIndex; - DBBINDING *rgBinding; - SIZE_T cbOffset; - BYTE *pbData; -}; - -struct SCE_QUERY -{ - SCE_TABLE_SCHEMA *pTableSchema; - SCE_INDEX_SCHEMA *pIndexSchema; - SCE_DATABASE_INTERNAL *pDatabaseInternal; - - // Accessor build-up members - DWORD dwBindingIndex; - DBBINDING *rgBinding; - SIZE_T cbOffset; - BYTE *pbData; -}; - -struct SCE_QUERY_RESULTS -{ - SCE_DATABASE_INTERNAL *pDatabaseInternal; - IRowset *pIRowset; - SCE_TABLE_SCHEMA *pTableSchema; -}; - -extern const int SCE_ROW_HANDLE_BYTES = sizeof(SCE_ROW); -extern const int SCE_QUERY_HANDLE_BYTES = sizeof(SCE_QUERY); -extern const int SCE_QUERY_RESULTS_HANDLE_BYTES = sizeof(SCE_QUERY_RESULTS); - -// The following is the internal Sce-maintained table to tell the identifier and version of the schema -const SCE_COLUMN_SCHEMA SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA[] = -{ - { - L"AppIdentifier", - DBTYPE_WSTR, - 0, - FALSE, - TRUE, - FALSE, - NULL, - 0, - 0 - }, - { - L"Version", - DBTYPE_I4, - 0, - FALSE, - FALSE, - FALSE, - NULL, - 0, - 0 - } -}; - -const SCE_TABLE_SCHEMA SCE_INTERNAL_VERSION_TABLE_SCHEMA[] = -{ - L"SceSchemaTablev1", - _countof(SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA), - (SCE_COLUMN_SCHEMA *)SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA, - 0, - NULL, - NULL, - NULL -}; - -// internal function declarations - -// Creates an instance of SQL Server CE object, returning IDBInitialize object -// If a file path is provided in wzSqlCeDllPath parameter, it calls DllGetClassObject -// on that file specifically. Otherwise it calls CoCreateInstance -static HRESULT CreateSqlCe( - __in_z_opt LPCWSTR wzSqlCeDllPath, - __out IDBInitialize **ppIDBInitialize, - __out_opt HMODULE *phSqlCeDll - ); -static HRESULT RunQuery( - __in BOOL fRange, - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle, - __out SCE_QUERY_RESULTS **ppsqrhHandle - ); -static HRESULT FillOutColumnDescFromSchema( - __in const SCE_COLUMN_SCHEMA *pSchema, - __out DBCOLUMNDESC pColumnDesc - ); -static HRESULT EnsureSchema( - __in SCE_DATABASE *pDatabase, - __in SCE_DATABASE_SCHEMA *pDatabaseSchema - ); -static HRESULT OpenSchema( - __in SCE_DATABASE *pDatabase, - __in SCE_DATABASE_SCHEMA *pdsSchema - ); -static HRESULT SetColumnValue( - __in const SCE_TABLE_SCHEMA *pTableSchema, - __in DWORD dwColumnIndex, - __in_bcount_opt(cbSize) const BYTE *pbData, - __in SIZE_T cbSize, - __inout DBBINDING *pBinding, - __inout SIZE_T *pcbOffset, - __inout BYTE **ppbBuffer - ); -static HRESULT GetColumnValue( - __in SCE_ROW *pRow, - __in DWORD dwColumnIndex, - __out_opt BYTE **ppbData, - __out SIZE_T *pcbSize - ); -static HRESULT GetColumnValueFixed( - __in SCE_ROW *pRow, - __in DWORD dwColumnIndex, - __in DWORD cbSize, - __out BYTE *pbData - ); -static HRESULT EnsureLocalColumnConstraints( - __in ITableDefinition *pTableDefinition, - __in DBID *pTableID, - __in SCE_TABLE_SCHEMA *pTableSchema - ); -static HRESULT EnsureForeignColumnConstraints( - __in ITableDefinition *pTableDefinition, - __in DBID *pTableID, - __in SCE_TABLE_SCHEMA *pTableSchema, - __in SCE_DATABASE_SCHEMA *pDatabaseSchema - ); -static HRESULT SetSessionProperties( - __in ISessionProperties *pISessionProperties - ); -static HRESULT GetDatabaseSchemaInfo( - __in SCE_DATABASE *pDatabase, - __out LPWSTR *psczSchemaType, - __out DWORD *pdwVersion - ); -static HRESULT SetDatabaseSchemaInfo( - __in SCE_DATABASE *pDatabase, - __in LPCWSTR wzSchemaType, - __in DWORD dwVersion - ); -static void ReleaseDatabase( - SCE_DATABASE *pDatabase - ); -static void ReleaseDatabaseInternal( - SCE_DATABASE_INTERNAL *pDatabaseInternal - ); - -// function definitions -extern "C" HRESULT DAPI SceCreateDatabase( - __in_z LPCWSTR sczFile, - __in_z_opt LPCWSTR wzSqlCeDllPath, - __out SCE_DATABASE **ppDatabase - ) -{ - HRESULT hr = S_OK; - LPWSTR sczDirectory = NULL; - SCE_DATABASE *pNewSceDatabase = NULL; - SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL; - IDBDataSourceAdmin *pIDBDataSourceAdmin = NULL; - DBPROPSET rgdbpDataSourcePropSet[2] = { }; - DBPROP rgdbpDataSourceProp[2] = { }; - DBPROP rgdbpDataSourceSsceProp[1] = { }; - - pNewSceDatabase = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE), TRUE)); - ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct"); - - pNewSceDatabaseInternal = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE)); - ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct"); - - pNewSceDatabase->sdbHandle = reinterpret_cast(pNewSceDatabaseInternal); - - hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll); - ExitOnFailure(hr, "Failed to get IDBInitialize interface"); - - hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBDataSourceAdmin, reinterpret_cast(&pIDBDataSourceAdmin)); - ExitOnFailure(hr, "Failed to get IDBDataSourceAdmin interface"); - - hr = PathGetDirectory(sczFile, &sczDirectory); - ExitOnFailure(hr, "Failed to get directory portion of path: %ls", sczFile); - - hr = DirEnsureExists(sczDirectory, NULL); - ExitOnFailure(hr, "Failed to ensure directory exists: %ls", sczDirectory); - - rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE; - rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceProp[0].vValue.vt = VT_BSTR; - rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(sczFile); - - rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE; - rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceProp[1].vValue.vt = VT_I4; - rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE; - - // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT - rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT; - rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; - rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp); - - rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE; - rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4; - rgdbpDataSourceSsceProp[0].vValue.intVal = MAX_SQLCE_DATABASE_SIZE; - - rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT; - rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp; - rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp); - - hr = pIDBDataSourceAdmin->CreateDataSource(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet, NULL, IID_IUnknown, NULL); - ExitOnFailure(hr, "Failed to create data source"); - - hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast(&pNewSceDatabaseInternal->pIDBProperties)); - ExitOnFailure(hr, "Failed to get IDBProperties interface"); - - hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast(&pNewSceDatabaseInternal->pIDBCreateSession)); - ExitOnFailure(hr, "Failed to get IDBCreateSession interface"); - - hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast(&pNewSceDatabaseInternal->pISessionProperties)); - ExitOnFailure(hr, "Failed to get ISessionProperties interface"); - - hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties); - ExitOnFailure(hr, "Failed to set session properties"); - - hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast(&pNewSceDatabaseInternal->pIOpenRowset)); - ExitOnFailure(hr, "Failed to get IOpenRowset interface"); - - hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast(&pNewSceDatabaseInternal->pITransactionLocal)); - ExitOnFailure(hr, "Failed to get ITransactionLocal interface"); - - *ppDatabase = pNewSceDatabase; - pNewSceDatabase = NULL; - -LExit: - ReleaseStr(sczDirectory); - ReleaseObject(pIDBDataSourceAdmin); - ReleaseDatabase(pNewSceDatabase); - ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal); - - return hr; -} - -extern "C" HRESULT DAPI SceOpenDatabase( - __in_z LPCWSTR sczFile, - __in_z_opt LPCWSTR wzSqlCeDllPath, - __in LPCWSTR wzExpectedSchemaType, - __in DWORD dwExpectedVersion, - __out SCE_DATABASE **ppDatabase, - __in BOOL fReadOnly - ) -{ - HRESULT hr = S_OK; - DWORD dwVersionFound = 0; - WCHAR wzTempDbFile[MAX_PATH]; - LPCWSTR wzPathToOpen = NULL; - LPWSTR sczSchemaType = NULL; - SCE_DATABASE *pNewSceDatabase = NULL; - SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL; - DBPROPSET rgdbpDataSourcePropSet[2] = { }; - DBPROP rgdbpDataSourceProp[2] = { }; - DBPROP rgdbpDataSourceSsceProp[1] = { }; - - pNewSceDatabase = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE), TRUE)); - ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct"); - - pNewSceDatabaseInternal = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE)); - ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct"); - - pNewSceDatabase->sdbHandle = reinterpret_cast(pNewSceDatabaseInternal); - - hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll); - ExitOnFailure(hr, "Failed to get IDBInitialize interface"); - - hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast(&pNewSceDatabaseInternal->pIDBProperties)); - ExitOnFailure(hr, "Failed to get IDBProperties interface"); - - // TODO: had trouble getting SQL CE to read a file read-only, so we're copying it to a temp path for now. - if (fReadOnly) - { - hr = DirCreateTempPath(PathFile(sczFile), (LPWSTR)wzTempDbFile, _countof(wzTempDbFile)); - ExitOnFailure(hr, "Failed to get temp path"); - - hr = FileEnsureCopy(sczFile, (LPCWSTR)wzTempDbFile, TRUE); - ExitOnFailure(hr, "Failed to copy file to temp path"); - - hr = StrAllocString(&pNewSceDatabaseInternal->sczTempDbFile, (LPCWSTR)wzTempDbFile, 0); - ExitOnFailure(hr, "Failed to copy temp db file path"); - - wzPathToOpen = (LPCWSTR)wzTempDbFile; - } - else - { - wzPathToOpen = sczFile; - } - - rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE; - rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceProp[0].vValue.vt = VT_BSTR; - rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(wzPathToOpen); - - rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE; - rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceProp[1].vValue.vt = VT_I4; - rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE; - - // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT - rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT; - rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; - rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp); - - rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE; - rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4; - rgdbpDataSourceSsceProp[0].vValue.lVal = MAX_SQLCE_DATABASE_SIZE; - - rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT; - rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp; - rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp); - - hr = pNewSceDatabaseInternal->pIDBProperties->SetProperties(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet); - ExitOnFailure(hr, "Failed to set initial properties to open database"); - - hr = pNewSceDatabaseInternal->pIDBInitialize->Initialize(); - ExitOnFailure(hr, "Failed to open database: %ls", sczFile); - - hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast(&pNewSceDatabaseInternal->pIDBCreateSession)); - ExitOnFailure(hr, "Failed to get IDBCreateSession interface"); - - hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast(&pNewSceDatabaseInternal->pISessionProperties)); - ExitOnFailure(hr, "Failed to get ISessionProperties interface"); - - hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties); - ExitOnFailure(hr, "Failed to set session properties"); - - hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast(&pNewSceDatabaseInternal->pIOpenRowset)); - ExitOnFailure(hr, "Failed to get IOpenRowset interface"); - - hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast(&pNewSceDatabaseInternal->pITransactionLocal)); - ExitOnFailure(hr, "Failed to get ITransactionLocal interface"); - - hr = GetDatabaseSchemaInfo(pNewSceDatabase, &sczSchemaType, &dwVersionFound); - ExitOnFailure(hr, "Failed to find schema version of database"); - - if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczSchemaType, -1, wzExpectedSchemaType, -1)) - { - hr = HRESULT_FROM_WIN32(ERROR_BAD_FILE_TYPE); - ExitOnRootFailure(hr, "Tried to open wrong database type - expected type %ls, found type %ls", wzExpectedSchemaType, sczSchemaType); - } - else if (dwVersionFound != dwExpectedVersion) - { - hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION); - ExitOnRootFailure(hr, "Tried to open wrong database schema version - expected version %u, found version %u", dwExpectedVersion, dwVersionFound); - } - - *ppDatabase = pNewSceDatabase; - pNewSceDatabase = NULL; - -LExit: - ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal); - ReleaseStr(sczSchemaType); - ReleaseDatabase(pNewSceDatabase); - - return hr; -} - -extern "C" HRESULT DAPI SceEnsureDatabase( - __in_z LPCWSTR sczFile, - __in_z_opt LPCWSTR wzSqlCeDllPath, - __in LPCWSTR wzSchemaType, - __in DWORD dwExpectedVersion, - __in SCE_DATABASE_SCHEMA *pdsSchema, - __out SCE_DATABASE **ppDatabase - ) -{ - HRESULT hr = S_OK; - SCE_DATABASE *pDatabase = NULL; - - if (FileExistsEx(sczFile, NULL)) - { - hr = SceOpenDatabase(sczFile, wzSqlCeDllPath, wzSchemaType, dwExpectedVersion, &pDatabase, FALSE); - ExitOnFailure(hr, "Failed to open database while ensuring database exists: %ls", sczFile); - } - else - { - hr = SceCreateDatabase(sczFile, wzSqlCeDllPath, &pDatabase); - ExitOnFailure(hr, "Failed to create database while ensuring database exists: %ls", sczFile); - - hr = SetDatabaseSchemaInfo(pDatabase, wzSchemaType, dwExpectedVersion); - ExitOnFailure(hr, "Failed to set schema version of database"); - } - - hr = EnsureSchema(pDatabase, pdsSchema); - ExitOnFailure(hr, "Failed to ensure schema is correct in database: %ls", sczFile); - - // Keep a pointer to the schema in the SCE_DATABASE object for future reference - pDatabase->pdsSchema = pdsSchema; - - *ppDatabase = pDatabase; - pDatabase = NULL; - -LExit: - ReleaseDatabase(pDatabase); - - return hr; -} - -extern "C" HRESULT DAPI SceIsTableEmpty( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __out BOOL *pfEmpty - ) -{ - HRESULT hr = S_OK; - SCE_ROW_HANDLE row = NULL; - - hr = SceGetFirstRow(pDatabase, dwTableIndex, &row); - if (E_NOTFOUND == hr) - { - *pfEmpty = TRUE; - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed to get first row while checking if table is empty"); - - *pfEmpty = FALSE; - -LExit: - ReleaseSceRow(row); - - return hr; -} - -extern "C" HRESULT DAPI SceGetFirstRow( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ) -{ - HRESULT hr = S_OK; - DBCOUNTITEM cRowsObtained = 0; - HROW hRow = DB_NULL_HROW; - HROW *phRow = &hRow; - SCE_ROW *pRow = NULL; - SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); - - hr = pTable->pIRowset->RestartPosition(DB_NULL_HCHAPTER); - ExitOnFailure(hr, "Failed to reset IRowset position to beginning"); - - hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); - if (DB_S_ENDOFROWSET == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - ExitOnFailure(hr, "Failed to get next first row"); - - pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); - ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); - - pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - pRow->hRow = hRow; - pRow->pTableSchema = pTable; - pRow->pIRowset = pTable->pIRowset; - pRow->pIRowset->AddRef(); - - *pRowHandle = reinterpret_cast(pRow); - -LExit: - return hr; -} - -HRESULT DAPI SceGetNextRow( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ) -{ - HRESULT hr = S_OK; - DBCOUNTITEM cRowsObtained = 0; - HROW hRow = DB_NULL_HROW; - HROW *phRow = &hRow; - SCE_ROW *pRow = NULL; - SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); - - hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); - if (DB_S_ENDOFROWSET == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - ExitOnFailure(hr, "Failed to get next first row"); - - pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); - ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); - - pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - pRow->hRow = hRow; - pRow->pTableSchema = pTable; - pRow->pIRowset = pTable->pIRowset; - pRow->pIRowset->AddRef(); - - *pRowHandle = reinterpret_cast(pRow); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceBeginTransaction( - __in SCE_DATABASE *pDatabase - ) -{ - HRESULT hr = S_OK; - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - - if (pDatabaseInternal->fTransactionBadState) - { - // We're in a hosed transaction state - we can't start a new one - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_RECOVERY_FAILURE)); - } - - ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount); - - if (1 == pDatabaseInternal->dwTransactionRefcount) - { - hr = pDatabaseInternal->pITransactionLocal->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL); - ExitOnFailure(hr, "Failed to start transaction"); - } - -LExit: - if (FAILED(hr)) - { - ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); - } - - return hr; -} - -extern "C" HRESULT DAPI SceCommitTransaction( - __in SCE_DATABASE *pDatabase - ) -{ - HRESULT hr = S_OK; - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - Assert(0 < pDatabaseInternal->dwTransactionRefcount); - - ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); - - if (0 == pDatabaseInternal->dwTransactionRefcount) - { - if (pDatabaseInternal->fRollbackTransaction) - { - hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE); - ExitOnFailure(hr, "Failed to abort transaction"); - } - else - { - hr = pDatabaseInternal->pITransactionLocal->Commit(FALSE, XACTTC_SYNC, 0); - ExitOnFailure(hr, "Failed to commit transaction"); - } - - if (pDatabaseInternal->fPendingChanges) - { - pDatabaseInternal->fPendingChanges = FALSE; - pDatabaseInternal->fChanges = TRUE; - } - - pDatabaseInternal->fRollbackTransaction = FALSE; - } - -LExit: - // If we tried to commit and failed, the caller should subsequently call rollback - if (FAILED(hr)) - { - ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount); - } - - return hr; -} - -extern "C" HRESULT DAPI SceRollbackTransaction( - __in SCE_DATABASE *pDatabase - ) -{ - HRESULT hr = S_OK; - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - Assert(0 < pDatabaseInternal->dwTransactionRefcount); - - ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); - - if (0 == pDatabaseInternal->dwTransactionRefcount) - { - hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE); - ExitOnFailure(hr, "Failed to abort transaction"); - pDatabaseInternal->fPendingChanges = FALSE; - - pDatabaseInternal->fRollbackTransaction = FALSE; - } - else - { - pDatabaseInternal->fRollbackTransaction = TRUE; - } - -LExit: - // We're just in a bad state now. Don't increment the transaction refcount (what is the user going to do - call us again?) - // but mark the database as bad so the user gets an error if they try to start a new transaction. - if (FAILED(hr)) - { - TraceError(hr, "Failed to rollback transaction"); - pDatabaseInternal->fTransactionBadState = TRUE; - } - - return hr; -} - -extern "C" HRESULT DAPI SceDeleteRow( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(*pRowHandle); - IRowsetChange *pIRowsetChange = NULL; - DBROWSTATUS rowStatus = DBROWSTATUS_S_OK; - - hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pIRowsetChange)); - ExitOnFailure(hr, "Failed to get IRowsetChange interface"); - - hr = pIRowsetChange->DeleteRows(DB_NULL_HCHAPTER, 1, &pRow->hRow, &rowStatus); - ExitOnFailure(hr, "Failed to delete row with status: %u", rowStatus); - - ReleaseNullSceRow(*pRowHandle); - -LExit: - ReleaseObject(pIRowsetChange); - - return hr; -} - -extern "C" HRESULT DAPI ScePrepareInsert( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = NULL; - - pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); - ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); - - pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - pRow->hRow = DB_NULL_HROW; - pRow->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); - pRow->pIRowset = pRow->pTableSchema->pIRowset; - pRow->pIRowset->AddRef(); - pRow->fInserting = TRUE; - - *pRowHandle = reinterpret_cast(pRow); - pRow = NULL; - -LExit: - ReleaseSceRow(pRow); - - return hr; -} - -extern "C" HRESULT DAPI SceFinishUpdate( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - IAccessor *pIAccessor = NULL; - IRowsetChange *pIRowsetChange = NULL; - DBBINDSTATUS *rgBindStatus = NULL; - HACCESSOR hAccessor = DB_NULL_HACCESSOR; - HROW hRow = DB_NULL_HROW; - - hr = pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); - ExitOnFailure(hr, "Failed to get IAccessor interface"); - -// This can be used when stepping through the debugger to see bind failures -#ifdef DEBUG - if (0 < pRow->dwBindingIndex) - { - hr = MemEnsureArraySize(reinterpret_cast(&rgBindStatus), pRow->dwBindingIndex, sizeof(DBBINDSTATUS), 0); - ExitOnFailure(hr, "Failed to ensure binding status array size"); - } -#endif - - hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pRow->dwBindingIndex, pRow->rgBinding, 0, &hAccessor, rgBindStatus); - ExitOnFailure(hr, "Failed to create accessor"); - - hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pIRowsetChange)); - ExitOnFailure(hr, "Failed to get IRowsetChange interface"); - - if (pRow->fInserting) - { - hr = pIRowsetChange->InsertRow(DB_NULL_HCHAPTER, hAccessor, pRow->pbData, &hRow); - ExitOnFailure(hr, "Failed to insert new row"); - - pRow->hRow = hRow; - ReleaseNullObject(pRow->pIRowset); - pRow->pIRowset = pRow->pTableSchema->pIRowset; - pRow->pIRowset->AddRef(); - } - else - { - hr = pIRowsetChange->SetData(pRow->hRow, hAccessor, pRow->pbData); - ExitOnFailure(hr, "Failed to update existing row"); - } - - if (0 < pRow->pDatabaseInternal->dwTransactionRefcount) - { - pRow->pDatabaseInternal->fPendingChanges = TRUE; - } - else - { - pRow->pDatabaseInternal->fChanges = TRUE; - } - -LExit: - if (DB_NULL_HACCESSOR != hAccessor) - { - pIAccessor->ReleaseAccessor(hAccessor, NULL); - } - ReleaseMem(rgBindStatus); - ReleaseObject(pIAccessor); - ReleaseObject(pIRowsetChange); - - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnBinary( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set binary, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, pbBuffer, cbBuffer, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as binary"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnDword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const DWORD dwValue - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set dword, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&dwValue), 4, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as binary"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnQword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const DWORD64 qwValue - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set qword, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&qwValue), 8, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as qword"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnBool( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const BOOL fValue - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - short int sValue = fValue ? 0xFFFF : 0x0000; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set bool, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&sValue), 2, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as binary"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnString( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in_z_opt LPCWSTR wzValue - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - SIZE_T cbSize = (NULL == wzValue) ? 0 : ((lstrlenW(wzValue) + 1) * sizeof(WCHAR)); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set string, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(wzValue), cbSize, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as string: %ls", wzValue); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnNull( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set empty, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, NULL, 0, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as empty value"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceSetColumnSystemTime( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, - __in DWORD dwColumnIndex, - __in const SYSTEMTIME *pst - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - DBTIMESTAMP dbTimeStamp = { }; - - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set systemtime, columns: %u", pRow->pTableSchema->cColumns); - - if (NULL == pRow->rgBinding) - { - pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); - } - - dbTimeStamp.year = pst->wYear; - dbTimeStamp.month = pst->wMonth; - dbTimeStamp.day = pst->wDay; - dbTimeStamp.hour = pst->wHour; - dbTimeStamp.minute = pst->wMinute; - dbTimeStamp.second = pst->wSecond; - // Don't use .fraction because milliseconds are not reliable in SQL CE. They are rounded to the nearest 1/300th of a second, - // and it is not supported (or at least I can't figure out how) to query for an exact timestamp if milliseconds - // are involved (even when rounded the way SQL CE returns them). - - hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&dbTimeStamp), sizeof(dbTimeStamp), &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); - ExitOnFailure(hr, "Failed to set column value as DBTIMESTAMP"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceGetColumnBinary( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out_opt BYTE **ppbBuffer, - __inout SIZE_T *pcbBuffer - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(rowReadHandle); - - hr = GetColumnValue(pRow, dwColumnIndex, ppbBuffer, pcbBuffer); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get binary data out of column"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceGetColumnDword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out DWORD *pdwValue - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(rowReadHandle); - - hr = GetColumnValueFixed(pRow, dwColumnIndex, 4, reinterpret_cast(pdwValue)); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get dword data out of column"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceGetColumnQword( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __in DWORD64 *pqwValue - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(rowReadHandle); - - hr = GetColumnValueFixed(pRow, dwColumnIndex, 8, reinterpret_cast(pqwValue)); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get qword data out of column"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceGetColumnBool( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out BOOL *pfValue - ) -{ - HRESULT hr = S_OK; - short int sValue = 0; - SCE_ROW *pRow = reinterpret_cast(rowReadHandle); - - hr = GetColumnValueFixed(pRow, dwColumnIndex, 2, reinterpret_cast(&sValue)); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get data out of column"); - - if (sValue == 0x0000) - { - *pfValue = FALSE; - } - else - { - *pfValue = TRUE; - } - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceGetColumnString( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out_z LPWSTR *psczValue - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(rowReadHandle); - SIZE_T cbSize = 0; - - hr = GetColumnValue(pRow, dwColumnIndex, reinterpret_cast(psczValue), &cbSize); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get string data out of column"); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI SceGetColumnSystemTime( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, - __in DWORD dwColumnIndex, - __out SYSTEMTIME *pst - ) -{ - HRESULT hr = S_OK; - SCE_ROW *pRow = reinterpret_cast(rowReadHandle); - DBTIMESTAMP dbTimeStamp = { }; - - hr = GetColumnValueFixed(pRow, dwColumnIndex, sizeof(dbTimeStamp), reinterpret_cast(&dbTimeStamp)); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get string data out of column"); - - pst->wYear = dbTimeStamp.year; - pst->wMonth = dbTimeStamp.month; - pst->wDay = dbTimeStamp.day; - pst->wHour = dbTimeStamp.hour; - pst->wMinute = dbTimeStamp.minute; - pst->wSecond = dbTimeStamp.second; - -LExit: - return hr; -} - -extern "C" void DAPI SceCloseTable( - __in SCE_TABLE_SCHEMA *pTable - ) -{ - ReleaseNullObject(pTable->pIRowsetChange); - ReleaseNullObject(pTable->pIRowset); -} - -extern "C" BOOL DAPI SceDatabaseChanged( - __in SCE_DATABASE *pDatabase - ) -{ - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - - return pDatabaseInternal->fChanges; -} - -void DAPI SceResetDatabaseChanged( - __in SCE_DATABASE *pDatabase - ) -{ - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - - pDatabaseInternal->fChanges = FALSE; -} - -extern "C" HRESULT DAPI SceCloseDatabase( - __in SCE_DATABASE *pDatabase - ) -{ - HRESULT hr = S_OK; - - ReleaseDatabase(pDatabase); - - return hr; -} - -extern "C" HRESULT DAPI SceBeginQuery( - __in SCE_DATABASE *pDatabase, - __in DWORD dwTableIndex, - __in DWORD dwIndex, - __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - SCE_QUERY *psq = static_cast(MemAlloc(sizeof(SCE_QUERY), TRUE)); - ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate new sce query"); - - psq->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); - psq->pIndexSchema = &(psq->pTableSchema->rgIndexes[dwIndex]); - psq->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - - hr = ::SizeTMult(sizeof(DBBINDING), psq->pTableSchema->cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to begin query, columns: %u", psq->pTableSchema->cColumns); - - psq->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for new sce query"); - - *psqhHandle = static_cast(psq); - psq = NULL; - -LExit: - ReleaseSceQuery(psq); - - return hr; -} - -HRESULT DAPI SceSetQueryColumnBinary( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in_bcount(cbBuffer) const BYTE* pbBuffer, - __in SIZE_T cbBuffer - ) -{ - HRESULT hr = S_OK; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], pbBuffer, cbBuffer, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as binary of size: %u", cbBuffer); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceSetQueryColumnDword( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const DWORD dwValue - ) -{ - HRESULT hr = S_OK; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&dwValue), 4, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as dword"); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceSetQueryColumnQword( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const DWORD64 qwValue - ) -{ - HRESULT hr = S_OK; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&qwValue), 8, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as qword"); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceSetQueryColumnBool( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const BOOL fValue - ) -{ - HRESULT hr = S_OK; - short int sValue = fValue ? 1 : 0; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&sValue), 2, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as boolean"); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceSetQueryColumnString( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in_z_opt LPCWSTR wzString - ) -{ - HRESULT hr = S_OK; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - SIZE_T cbSize = (NULL == wzString) ? 0 : ((lstrlenW(wzString) + 1) * sizeof(WCHAR)); - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(wzString), cbSize, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as string"); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceSetQueryColumnSystemTime( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, - __in const SYSTEMTIME *pst - ) -{ - HRESULT hr = S_OK; - DBTIMESTAMP dbTimeStamp = { }; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - dbTimeStamp.year = pst->wYear; - dbTimeStamp.month = pst->wMonth; - dbTimeStamp.day = pst->wDay; - dbTimeStamp.hour = pst->wHour; - dbTimeStamp.minute = pst->wMinute; - dbTimeStamp.second = pst->wSecond; - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&dbTimeStamp), sizeof(dbTimeStamp), &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as DBTIMESTAMP"); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceSetQueryColumnEmpty( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle - ) -{ - HRESULT hr = S_OK; - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], NULL, 0, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); - ExitOnFailure(hr, "Failed to set query column value as empty value"); - - ++(pQuery->dwBindingIndex); - -LExit: - return hr; -} - -HRESULT DAPI SceRunQueryExact( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE *psqhHandle, - __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ) -{ - HRESULT hr = S_OK; - SCE_QUERY_RESULTS *pQueryResults = NULL; - - hr = RunQuery(FALSE, *psqhHandle, &pQueryResults); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to run query exact"); - - hr = SceGetNextResultRow(reinterpret_cast(pQueryResults), pRowHandle); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get next row out of results"); - -LExit: - ReleaseNullSceQuery(*psqhHandle); - ReleaseSceQueryResults(pQueryResults); - - return hr; -} - -extern "C" HRESULT DAPI SceRunQueryRange( - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, - __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle - ) -{ - HRESULT hr = S_OK; - SCE_QUERY_RESULTS **ppQueryResults = reinterpret_cast(psqrhHandle); - - hr = RunQuery(TRUE, *psqhHandle, ppQueryResults); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to run query for range"); - -LExit: - ReleaseNullSceQuery(*psqhHandle); - - return hr; -} - -extern "C" HRESULT DAPI SceGetNextResultRow( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle, - __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle - ) -{ - HRESULT hr = S_OK; - HROW hRow = DB_NULL_HROW; - HROW *phRow = &hRow; - DBCOUNTITEM cRowsObtained = 0; - SCE_ROW *pRow = NULL; - SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast(sqrhHandle); - - Assert(pRowHandle && (*pRowHandle == NULL)); - - hr = pQueryResults->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); - if (DB_S_ENDOFROWSET == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - ExitOnFailure(hr, "Failed to get next first row"); - - pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); - ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); - - pRow->pDatabaseInternal = reinterpret_cast(pQueryResults->pDatabaseInternal); - pRow->hRow = hRow; - pRow->pTableSchema = pQueryResults->pTableSchema; - pRow->pIRowset = pQueryResults->pIRowset; - pRow->pIRowset->AddRef(); - - *pRowHandle = reinterpret_cast(pRow); - pRow = NULL; - hRow = DB_NULL_HROW; - -LExit: - if (DB_NULL_HROW != hRow) - { - pQueryResults->pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL); - } - ReleaseSceRow(pRow); - - return hr; -} - -extern "C" void DAPI SceFreeRow( - __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle - ) -{ - SCE_ROW *pRow = reinterpret_cast(rowHandle); - - if (DB_NULL_HROW != pRow->hRow) - { - pRow->pIRowset->ReleaseRows(1, &pRow->hRow, NULL, NULL, NULL); - } - ReleaseObject(pRow->pIRowset); - ReleaseMem(pRow->rgBinding); - ReleaseMem(pRow->pbData); - ReleaseMem(pRow); -} - -void DAPI SceFreeQuery( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle - ) -{ - SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); - - ReleaseMem(pQuery->rgBinding); - ReleaseMem(pQuery->pbData); - ReleaseMem(pQuery); -} - -void DAPI SceFreeQueryResults( - __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle - ) -{ - SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast(sqrhHandle); - - ReleaseObject(pQueryResults->pIRowset); - ReleaseMem(pQueryResults); -} - -// internal function definitions -static HRESULT CreateSqlCe( - __in_z_opt LPCWSTR wzSqlCeDllPath, - __out IDBInitialize **ppIDBInitialize, - __out_opt HMODULE *phSqlCeDll - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPath = NULL; - LPWSTR sczDirectory = NULL; - LPWSTR sczDllFullPath = NULL; - - if (NULL == wzSqlCeDllPath) - { - hr = CoCreateInstance(CLSID_SQLSERVERCE, 0, CLSCTX_INPROC_SERVER, IID_IDBInitialize, reinterpret_cast(ppIDBInitialize)); - ExitOnFailure(hr, "Failed to get IDBInitialize interface"); - } - else - { - // First try loading DLL from the path of our EXE - hr = PathForCurrentProcess(&sczPath, NULL); - ExitOnFailure(hr, "Failed to get path for current process"); - - hr = PathGetDirectory(sczPath, &sczDirectory); - ExitOnFailure(hr, "Failed to get directory of current process"); - - hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath); - ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename"); - - *phSqlCeDll = ::LoadLibraryW(sczDllFullPath); - - // If that failed, fallback to loading from current path - if (NULL == *phSqlCeDll) - { - hr = DirGetCurrent(&sczDirectory); - ExitOnFailure(hr, "Failed to get current directory"); - - hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath); - ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename"); - - *phSqlCeDll = ::LoadLibraryW(sczDllFullPath); - ExitOnNullWithLastError(*phSqlCeDll, hr, "Failed to open Sql CE DLL: %ls", sczDllFullPath); - } - - HRESULT (WINAPI *pfnGetFactory)(REFCLSID, REFIID, void**); - pfnGetFactory = (HRESULT (WINAPI *)(REFCLSID, REFIID, void**))GetProcAddress(*phSqlCeDll, "DllGetClassObject"); - - IClassFactory* pFactory = NULL; - hr = pfnGetFactory(CLSID_SQLSERVERCE, IID_IClassFactory, (void**)&pFactory); - ExitOnFailure(hr, "Failed to get factory for IID_IDBInitialize from DLL: %ls", sczDllFullPath); - ExitOnNull(pFactory, hr, E_UNEXPECTED, "GetFactory returned success, but pFactory was NULL"); - - hr = pFactory->CreateInstance(NULL, IID_IDBInitialize, (void**)ppIDBInitialize); - pFactory->Release(); - } - -LExit: - ReleaseStr(sczPath); - ReleaseStr(sczDirectory); - ReleaseStr(sczDllFullPath); - - return hr; -} - -static HRESULT RunQuery( - __in BOOL fRange, - __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle, - __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS **ppQueryResults - ) -{ - HRESULT hr = S_OK; - DBID tableID = { }; - DBID indexID = { }; - IAccessor *pIAccessor = NULL; - IRowsetIndex *pIRowsetIndex = NULL; - IRowset *pIRowset = NULL; - HACCESSOR hAccessor = DB_NULL_HACCESSOR; - SCE_QUERY *pQuery = reinterpret_cast(psqhHandle); - SCE_QUERY_RESULTS *pQueryResults = NULL; - DBPROPSET rgdbpIndexPropSet[1]; - DBPROP rgdbpIndexProp[1]; - - rgdbpIndexPropSet[0].cProperties = 1; - rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_ROWSET; - rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp; - - rgdbpIndexProp[0].dwPropertyID = DBPROP_IRowsetIndex; - rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpIndexProp[0].colid = DB_NULLID; - rgdbpIndexProp[0].vValue.vt = VT_BOOL; - rgdbpIndexProp[0].vValue.boolVal = VARIANT_TRUE; - - tableID.eKind = DBKIND_NAME; - tableID.uName.pwszName = const_cast(pQuery->pTableSchema->wzName); - - indexID.eKind = DBKIND_NAME; - indexID.uName.pwszName = const_cast(pQuery->pIndexSchema->wzName); - - hr = pQuery->pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, _countof(rgdbpIndexPropSet), rgdbpIndexPropSet, (IUnknown**) &pIRowsetIndex); - ExitOnFailure(hr, "Failed to open IRowsetIndex"); - - hr = pIRowsetIndex->QueryInterface(IID_IRowset, reinterpret_cast(&pIRowset)); - ExitOnFailure(hr, "Failed to get IRowset interface from IRowsetIndex"); - - hr = pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); - ExitOnFailure(hr, "Failed to get IAccessor interface"); - - hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pQuery->dwBindingIndex, pQuery->rgBinding, 0, &hAccessor, NULL); - ExitOnFailure(hr, "Failed to create accessor"); - - if (!fRange) - { - hr = pIRowsetIndex->Seek(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, DBSEEK_FIRSTEQ); - if (DB_E_NOTFOUND == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - ExitOnFailure(hr, "Failed to seek to record"); - } - else - { - // If ALL columns in the index were specified, do a full key match - if (pQuery->dwBindingIndex == pQuery->pIndexSchema->cColumns) - { - hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, 0, NULL, DBRANGE_MATCH); - } - else - { - // Otherwise, just match the specified keys. - // We really want to use DBRANGE_MATCH_N_SHIFT here, but SQL CE doesn't appear to support it - // So instead, we set the start and end to the same partial key, and then allow inclusive matching - // This appears to accomplish the same thing - hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, pQuery->dwBindingIndex, pQuery->pbData, 0); - } - if (DB_E_NOTFOUND == hr || E_NOTFOUND == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - ExitOnFailure(hr, "Failed to set range to find records"); - } - - pQueryResults = reinterpret_cast(MemAlloc(sizeof(SCE_QUERY_RESULTS), TRUE)); - ExitOnNull(pQueryResults, hr, E_OUTOFMEMORY, "Failed to allocate query results struct"); - - pQueryResults->pDatabaseInternal = pQuery->pDatabaseInternal; - pQueryResults->pTableSchema = pQuery->pTableSchema; - pQueryResults->pIRowset = pIRowset; - pIRowset = NULL; - - *ppQueryResults = pQueryResults; - pQueryResults = NULL; - -LExit: - if (DB_NULL_HACCESSOR != hAccessor) - { - pIAccessor->ReleaseAccessor(hAccessor, NULL); - } - ReleaseObject(pIAccessor); - ReleaseObject(pIRowset); - ReleaseObject(pIRowsetIndex); - ReleaseMem(pQueryResults); - ReleaseSceQueryResults(pQueryResults); - - return hr; -} - -static HRESULT FillOutColumnDescFromSchema( - __in const SCE_COLUMN_SCHEMA *pColumnSchema, - __out DBCOLUMNDESC *pColumnDesc - ) -{ - HRESULT hr = S_OK; - DWORD dwColumnProperties = 0; - DWORD dwColumnPropertyIndex = 0; - BOOL fFixedSize = FALSE; - - pColumnDesc->dbcid.eKind = DBKIND_NAME; - pColumnDesc->dbcid.uName.pwszName = (WCHAR *)pColumnSchema->wzName; - pColumnDesc->wType = pColumnSchema->dbtColumnType; - pColumnDesc->ulColumnSize = pColumnSchema->dwLength; - if (0 == pColumnDesc->ulColumnSize && (DBTYPE_WSTR == pColumnDesc->wType || DBTYPE_BYTES == pColumnDesc->wType)) - { - fFixedSize = FALSE; - } - else - { - fFixedSize = TRUE; - } - - dwColumnProperties = 1; - if (pColumnSchema->fAutoIncrement) - { - ++dwColumnProperties; - } - if (!pColumnSchema->fNullable) - { - ++dwColumnProperties; - } - - if (0 < dwColumnProperties) - { - pColumnDesc->cPropertySets = 1; - pColumnDesc->rgPropertySets = reinterpret_cast(MemAlloc(sizeof(DBPROPSET), TRUE)); - ExitOnNull(pColumnDesc->rgPropertySets, hr, E_OUTOFMEMORY, "Failed to allocate propset object while setting up column parameters"); - - pColumnDesc->rgPropertySets[0].cProperties = dwColumnProperties; - pColumnDesc->rgPropertySets[0].guidPropertySet = DBPROPSET_COLUMN; - pColumnDesc->rgPropertySets[0].rgProperties = reinterpret_cast(MemAlloc(sizeof(DBPROP) * dwColumnProperties, TRUE)); - - dwColumnPropertyIndex = 0; - if (pColumnSchema->fAutoIncrement) - { - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_AUTOINCREMENT; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_TRUE; - ++dwColumnPropertyIndex; - } - if (!pColumnSchema->fNullable) - { - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_NULLABLE; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_FALSE; - ++dwColumnPropertyIndex; - } - - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_FIXEDLENGTH; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; - pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = fFixedSize ? VARIANT_TRUE : VARIANT_FALSE; - ++dwColumnPropertyIndex; - } - -LExit: - return hr; -} - -static HRESULT EnsureSchema( - __in SCE_DATABASE *pDatabase, - __in SCE_DATABASE_SCHEMA *pdsSchema - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - BOOL fInTransaction = FALSE; - BOOL fSchemaNeedsSetup = TRUE; - DBID tableID = { }; - DBID indexID = { }; - DBPROPSET rgdbpIndexPropSet[1]; - DBPROPSET rgdbpRowSetPropSet[1]; - DBPROP rgdbpIndexProp[1]; - DBPROP rgdbpRowSetProp[1]; - DBCOLUMNDESC *rgColumnDescriptions = NULL; - DBINDEXCOLUMNDESC *rgIndexColumnDescriptions = NULL; - DWORD cIndexColumnDescriptions = 0; - DWORD dwTableColumnIndex = 0; - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - ITableDefinition *pTableDefinition = NULL; - IIndexDefinition *pIndexDefinition = NULL; - IRowsetIndex *pIRowsetIndex = NULL; - - rgdbpRowSetPropSet[0].cProperties = 1; - rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET; - rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp; - - rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange; - rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpRowSetProp[0].colid = DB_NULLID; - rgdbpRowSetProp[0].vValue.vt = VT_BOOL; - rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE; - - rgdbpIndexPropSet[0].cProperties = 1; - rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_INDEX; - rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp; - - rgdbpIndexProp[0].dwPropertyID = DBPROP_INDEX_NULLS; - rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpIndexProp[0].colid = DB_NULLID; - rgdbpIndexProp[0].vValue.vt = VT_I4; - rgdbpIndexProp[0].vValue.lVal = DBPROPVAL_IN_DISALLOWNULL; - - hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_ITableDefinition, reinterpret_cast(&pTableDefinition)); - ExitOnFailure(hr, "Failed to get ITableDefinition for table creation"); - - hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_IIndexDefinition, reinterpret_cast(&pIndexDefinition)); - ExitOnFailure(hr, "Failed to get IIndexDefinition for index creation"); - - hr = SceBeginTransaction(pDatabase); - ExitOnFailure(hr, "Failed to start transaction for ensuring schema"); - fInTransaction = TRUE; - - for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) - { - tableID.eKind = DBKIND_NAME; - tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); - - // Fill out each column description struct as appropriate, to be used for creating the table, or confirming the table's columns all exist - rgColumnDescriptions = static_cast(MemAlloc(sizeof(DBCOLUMNDESC) * pdsSchema->rgTables[dwTable].cColumns, TRUE)); - ExitOnNull(rgColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate column description array while creating table"); - - for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i) - { - hr = FillOutColumnDescFromSchema(pdsSchema->rgTables[dwTable].rgColumns + i, rgColumnDescriptions + i); - ExitOnFailure(hr, "Failed to fill out column description from schema"); - } - - // First try to open the table - or if it doesn't exist, create it - hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowset)); - if (DB_E_NOTABLE == hr) - { - // The table doesn't exist, so let's create it - hr = pTableDefinition->CreateTable(NULL, &tableID, pdsSchema->rgTables[dwTable].cColumns, rgColumnDescriptions, IID_IUnknown, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, NULL, NULL); - ExitOnFailure(hr, "Failed to create table: %ls", pdsSchema->rgTables[dwTable].wzName); - } - else - { - ExitOnFailure(hr, "Failed to open table %ls while ensuring schema", tableID.uName.pwszName); - - // Close any rowset we opened - ReleaseNullObject(pdsSchema->rgTables[dwTable].pIRowset); - - // If it does exist, make sure all columns are in the table - // Only nullable columns can be added to an existing table - for (DWORD i = 1; i < pdsSchema->rgTables[dwTable].cColumns; ++i) - { - if (pdsSchema->rgTables[dwTable].rgColumns[i].fNullable) - hr = pTableDefinition->AddColumn(&tableID, rgColumnDescriptions + i, NULL); - if (DB_E_DUPLICATECOLUMNID == hr) - { - hr = S_OK; - } - ExitOnFailure(hr, "Failed to add column %ls", pdsSchema->rgTables[dwTable].rgColumns[i].wzName); - } - } - -#pragma prefast(push) -#pragma prefast(disable:26010) - hr = EnsureLocalColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable); -#pragma prefast(pop) - ExitOnFailure(hr, "Failed to ensure local column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName); - - for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i) - { - if (NULL != rgColumnDescriptions[i].rgPropertySets) - { - ReleaseMem(rgColumnDescriptions[i].rgPropertySets[0].rgProperties); - ReleaseMem(rgColumnDescriptions[i].rgPropertySets); - } - } - - ReleaseNullMem(rgColumnDescriptions); - if (0 < pdsSchema->rgTables[dwTable].cIndexes) - { - // Now create indexes for the table - for (DWORD dwIndex = 0; dwIndex < pdsSchema->rgTables[dwTable].cIndexes; ++dwIndex) - { - indexID.eKind = DBKIND_NAME; - indexID.uName.pwszName = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName; - - // Check if the index exists - hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, 0, NULL, (IUnknown**) &pIRowsetIndex); - if (SUCCEEDED(hr)) - { - // TODO: If one with the same name exists, check if the schema actually matches - ReleaseNullObject(pIRowsetIndex); - continue; - } - hr = S_OK; - - hr = ::SizeTMult(sizeof(DBINDEXCOLUMNDESC), pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns, &cbAllocSize); - ExitOnFailure(hr, "Overflow while calculating allocation size for DBINDEXCOLUMNDESC, columns: %u", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns); - - rgIndexColumnDescriptions = reinterpret_cast(MemAlloc(cbAllocSize, TRUE)); - ExitOnNull(rgIndexColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate structure to hold index column descriptions"); - cIndexColumnDescriptions = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns; - - for (DWORD dwColumnIndex = 0; dwColumnIndex < cIndexColumnDescriptions; ++dwColumnIndex) - { - dwTableColumnIndex = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].rgColumns[dwColumnIndex]; - - rgIndexColumnDescriptions[dwColumnIndex].pColumnID = reinterpret_cast(MemAlloc(sizeof(DBID), TRUE)); - rgIndexColumnDescriptions[dwColumnIndex].pColumnID->eKind = DBKIND_NAME; - rgIndexColumnDescriptions[dwColumnIndex].pColumnID->uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].wzName); - rgIndexColumnDescriptions[dwColumnIndex].eIndexColOrder = pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].fDescending ? DBINDEX_COL_ORDER_DESC : DBINDEX_COL_ORDER_ASC; - } - - hr = pIndexDefinition->CreateIndex(&tableID, &indexID, static_cast(pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns), rgIndexColumnDescriptions, 1, rgdbpIndexPropSet, NULL); - if (DB_E_DUPLICATEINDEXID == hr) - { - // If the index already exists, no worries - hr = S_OK; - } - ExitOnFailure(hr, "Failed to create index named %ls into table named %ls", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName, pdsSchema->rgTables[dwTable].wzName); - - for (DWORD i = 0; i < cIndexColumnDescriptions; ++i) - { - ReleaseMem(rgIndexColumnDescriptions[i].pColumnID); - } - - cIndexColumnDescriptions = 0; - ReleaseNullMem(rgIndexColumnDescriptions); - } - } - } - - // Now once all tables have been created, create foreign key relationships - if (fSchemaNeedsSetup) - { - for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) - { - tableID.eKind = DBKIND_NAME; - tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); - - // Setup any constraints for the table's columns - hr = EnsureForeignColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable, pdsSchema); - ExitOnFailure(hr, "Failed to ensure foreign column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName); - } - } - - hr = SceCommitTransaction(pDatabase); - ExitOnFailure(hr, "Failed to commit transaction for ensuring schema"); - fInTransaction = FALSE; - - hr = OpenSchema(pDatabase, pdsSchema); - ExitOnFailure(hr, "Failed to open schema"); - -LExit: - ReleaseObject(pTableDefinition); - ReleaseObject(pIndexDefinition); - ReleaseObject(pIRowsetIndex); - - if (fInTransaction) - { - SceRollbackTransaction(pDatabase); - } - - for (DWORD i = 0; i < cIndexColumnDescriptions; ++i) - { - ReleaseMem(rgIndexColumnDescriptions[i].pColumnID); - } - - ReleaseMem(rgIndexColumnDescriptions); - ReleaseMem(rgColumnDescriptions); - - return hr; -} - -static HRESULT OpenSchema( - __in SCE_DATABASE *pDatabase, - __in SCE_DATABASE_SCHEMA *pdsSchema - ) -{ - HRESULT hr = S_OK; - SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); - DBID tableID = { }; - DBPROPSET rgdbpRowSetPropSet[1]; - DBPROP rgdbpRowSetProp[1]; - - rgdbpRowSetPropSet[0].cProperties = 1; - rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET; - rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp; - - rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange; - rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpRowSetProp[0].colid = DB_NULLID; - rgdbpRowSetProp[0].vValue.vt = VT_BOOL; - rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE; - - // Finally, open all tables - for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) - { - tableID.eKind = DBKIND_NAME; - tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); - - // And finally, open the table's standard interfaces - hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowset)); - ExitOnFailure(hr, "Failed to open table %u named %ls after ensuring all indexes and constraints are created", dwTable, pdsSchema->rgTables[dwTable].wzName); - - hr = pdsSchema->rgTables[dwTable].pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowsetChange)); - ExitOnFailure(hr, "Failed to get IRowsetChange object for table: %ls", pdsSchema->rgTables[dwTable].wzName); - } - -LExit: - return hr; -} - -static HRESULT SetColumnValue( - __in const SCE_TABLE_SCHEMA *pTableSchema, - __in DWORD dwColumnIndex, - __in_bcount_opt(cbSize) const BYTE *pbData, - __in SIZE_T cbSize, - __inout DBBINDING *pBinding, - __inout SIZE_T *pcbOffset, - __inout BYTE **ppbBuffer - ) -{ - HRESULT hr = S_OK; - size_t cbNewOffset = *pcbOffset; - - pBinding->iOrdinal = dwColumnIndex + 1; // Skip bookmark column - pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED; - pBinding->dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS; - - pBinding->obLength = cbNewOffset; - - hr = ::SizeTAdd(cbNewOffset, sizeof(DBBYTEOFFSET), &cbNewOffset); - ExitOnFailure(hr, "Failed to add sizeof(DBBYTEOFFSET) to alloc size while setting column value"); - - pBinding->obValue = cbNewOffset; - - hr = ::SizeTAdd(cbNewOffset, cbSize, &cbNewOffset); - ExitOnFailure(hr, "Failed to add %u to alloc size while setting column value", cbSize); - - pBinding->obStatus = cbNewOffset; - pBinding->eParamIO = DBPARAMIO_INPUT; - - hr = ::SizeTAdd(cbNewOffset, sizeof(DBSTATUS), &cbNewOffset); - ExitOnFailure(hr, "Failed to add sizeof(DBSTATUS) to alloc size while setting column value"); - - pBinding->wType = pTableSchema->rgColumns[dwColumnIndex].dbtColumnType; - pBinding->cbMaxLen = static_cast(cbSize); - - if (NULL == *ppbBuffer) - { - *ppbBuffer = reinterpret_cast(MemAlloc(cbNewOffset, TRUE)); - ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer while setting row string"); - } - else - { - *ppbBuffer = reinterpret_cast(MemReAlloc(*ppbBuffer, cbNewOffset, TRUE)); - ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate buffer while setting row string"); - } - - *(reinterpret_cast(*ppbBuffer + *pcbOffset)) = static_cast(cbSize); - *pcbOffset += sizeof(DBBYTEOFFSET); - memcpy(*ppbBuffer + *pcbOffset, pbData, cbSize); - *pcbOffset += cbSize; - if (NULL == pbData) - { - *(reinterpret_cast(*ppbBuffer + *pcbOffset)) = DBSTATUS_S_ISNULL; - } - *pcbOffset += sizeof(DBSTATUS); - -LExit: - return hr; -} - -static HRESULT GetColumnValue( - __in SCE_ROW *pRow, - __in DWORD dwColumnIndex, - __out_opt BYTE **ppbData, - __out SIZE_T *pcbSize - ) -{ - HRESULT hr = S_OK; - const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema; - IAccessor *pIAccessor = NULL; - HACCESSOR hAccessorLength = DB_NULL_HACCESSOR; - HACCESSOR hAccessorValue = DB_NULL_HACCESSOR; - DWORD dwDataSize = 0; - void *pvRawData = NULL; - DBBINDING dbBinding = { }; - DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK; - - dbBinding.iOrdinal = dwColumnIndex + 1; - dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; - dbBinding.dwPart = DBPART_LENGTH; - dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType; - - pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); - ExitOnFailure(hr, "Failed to get IAccessor interface"); - - hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus); - ExitOnFailure(hr, "Failed to create accessor"); - - hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast(&dwDataSize)); - ExitOnFailure(hr, "Failed to get size of data"); - - // For variable-length columns, zero data returned means NULL - if (0 == dwDataSize) - { - ExitFunction1(hr = E_NOTFOUND); - } - - if (NULL != ppbData) - { - dbBinding.dwPart = DBPART_VALUE; - dbBinding.cbMaxLen = dwDataSize; - - hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus); - ExitOnFailure(hr, "Failed to create accessor"); - - if (DBBINDSTATUS_OK != dbBindStatus) - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Bad bind status while creating accessor to get value"); - } - - if (DBTYPE_WSTR == dbBinding.wType) - { - hr = StrAlloc(reinterpret_cast(&pvRawData), dwDataSize / sizeof(WCHAR)); - ExitOnFailure(hr, "Failed to allocate space for string data while reading column %u", dwColumnIndex); - } - else - { - pvRawData = MemAlloc(dwDataSize, TRUE); - ExitOnNull(pvRawData, hr, E_OUTOFMEMORY, "Failed to allocate space for data while reading column %u", dwColumnIndex); - } - - hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, pvRawData); - ExitOnFailure(hr, "Failed to read data value"); - - ReleaseMem(*ppbData); - *ppbData = reinterpret_cast(pvRawData); - pvRawData = NULL; - } - - *pcbSize = dwDataSize; - -LExit: - ReleaseMem(pvRawData); - - if (DB_NULL_HACCESSOR != hAccessorLength) - { - pIAccessor->ReleaseAccessor(hAccessorLength, NULL); - } - if (DB_NULL_HACCESSOR != hAccessorValue) - { - pIAccessor->ReleaseAccessor(hAccessorValue, NULL); - } - ReleaseObject(pIAccessor); - - return hr; -} - -static HRESULT GetColumnValueFixed( - __in SCE_ROW *pRow, - __in DWORD dwColumnIndex, - __in DWORD cbSize, - __out BYTE *pbData - ) -{ - HRESULT hr = S_OK; - const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema; - IAccessor *pIAccessor = NULL; - HACCESSOR hAccessorLength = DB_NULL_HACCESSOR; - HACCESSOR hAccessorValue = DB_NULL_HACCESSOR; - DWORD dwDataSize = 0; - DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK; - DBBINDING dbBinding = { }; - - dbBinding.iOrdinal = dwColumnIndex + 1; - dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; - dbBinding.dwPart = DBPART_LENGTH; - dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType; - - pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); - ExitOnFailure(hr, "Failed to get IAccessor interface"); - - hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus); - ExitOnFailure(hr, "Failed to create accessor"); - - if (DBBINDSTATUS_OK != dbBindStatus) - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Bad bind status while creating accessor to get length of value"); - } - - hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast(&dwDataSize)); - ExitOnFailure(hr, "Failed to get size of data"); - - if (0 == dwDataSize) - { - ExitFunction1(hr = E_NOTFOUND); - } - - dbBinding.dwPart = DBPART_VALUE; - dbBinding.cbMaxLen = cbSize; - - hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus); - ExitOnFailure(hr, "Failed to create accessor"); - - if (DBBINDSTATUS_OK != dbBindStatus) - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Bad bind status while creating accessor to get value"); - } - - hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, reinterpret_cast(pbData)); - ExitOnFailure(hr, "Failed to read data value"); - -LExit: - if (DB_NULL_HACCESSOR != hAccessorLength) - { - pIAccessor->ReleaseAccessor(hAccessorLength, NULL); - } - if (DB_NULL_HACCESSOR != hAccessorValue) - { - pIAccessor->ReleaseAccessor(hAccessorValue, NULL); - } - ReleaseObject(pIAccessor); - - return hr; -} - -static HRESULT EnsureLocalColumnConstraints( - __in ITableDefinition *pTableDefinition, - __in DBID *pTableID, - __in SCE_TABLE_SCHEMA *pTableSchema - ) -{ - HRESULT hr = S_OK; - SCE_COLUMN_SCHEMA *pCurrentColumn = NULL; - DBCONSTRAINTDESC dbcdConstraint = { }; - DBID dbConstraintID = { }; - DBID dbLocalColumnID = { }; - ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL; - - hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast(&pTableDefinitionWithConstraints)); - ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints"); - - for (DWORD i = 0; i < pTableSchema->cColumns; ++i) - { - pCurrentColumn = pTableSchema->rgColumns + i; - - // Add a primary key constraint for this column, if one exists - if (pCurrentColumn->fPrimaryKey) - { - // Setup DBID for new constraint - dbConstraintID.eKind = DBKIND_NAME; - dbConstraintID.uName.pwszName = const_cast(L"PrimaryKey"); - dbcdConstraint.pConstraintID = &dbConstraintID; - - dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_PRIMARYKEY; - - dbLocalColumnID.eKind = DBKIND_NAME; - dbLocalColumnID.uName.pwszName = const_cast(pCurrentColumn->wzName); - dbcdConstraint.cColumns = 1; - dbcdConstraint.rgColumnList = &dbLocalColumnID; - - dbcdConstraint.pReferencedTableID = NULL; - dbcdConstraint.cForeignKeyColumns = 0; - dbcdConstraint.rgForeignKeyColumnList = NULL; - dbcdConstraint.pwszConstraintText = NULL; - dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION; - dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION; - dbcdConstraint.MatchType = DBMATCHTYPE_NONE; - dbcdConstraint.Deferrability = 0; - dbcdConstraint.cReserved = 0; - dbcdConstraint.rgReserved = NULL; - - hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint); - if (DB_E_DUPLICATECONSTRAINTID == hr) - { - hr = S_OK; - } - ExitOnFailure(hr, "Failed to add primary key constraint for column %ls, table %ls", pCurrentColumn->wzName, pTableSchema->wzName); - } - } - -LExit: - ReleaseObject(pTableDefinitionWithConstraints); - - return hr; -} - -static HRESULT EnsureForeignColumnConstraints( - __in ITableDefinition *pTableDefinition, - __in DBID *pTableID, - __in SCE_TABLE_SCHEMA *pTableSchema, - __in SCE_DATABASE_SCHEMA *pDatabaseSchema - ) -{ - HRESULT hr = S_OK; - SCE_COLUMN_SCHEMA *pCurrentColumn = NULL; - DBCONSTRAINTDESC dbcdConstraint = { }; - DBID dbConstraintID = { }; - DBID dbLocalColumnID = { }; - DBID dbForeignTableID = { }; - DBID dbForeignColumnID = { }; - ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL; - - hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast(&pTableDefinitionWithConstraints)); - ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints"); - - for (DWORD i = 0; i < pTableSchema->cColumns; ++i) - { - pCurrentColumn = pTableSchema->rgColumns + i; - - // Add a foreign key constraint for this column, if one exists - if (NULL != pCurrentColumn->wzRelationName) - { - // Setup DBID for new constraint - dbConstraintID.eKind = DBKIND_NAME; - dbConstraintID.uName.pwszName = const_cast(pCurrentColumn->wzRelationName); - dbcdConstraint.pConstraintID = &dbConstraintID; - - dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_FOREIGNKEY; - - dbForeignColumnID.eKind = DBKIND_NAME; - dbForeignColumnID.uName.pwszName = const_cast(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].rgColumns[pCurrentColumn->dwForeignKeyColumn].wzName); - dbcdConstraint.cColumns = 1; - dbcdConstraint.rgColumnList = &dbForeignColumnID; - - dbForeignTableID.eKind = DBKIND_NAME; - dbForeignTableID.uName.pwszName = const_cast(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].wzName); - dbcdConstraint.pReferencedTableID = &dbForeignTableID; - - dbLocalColumnID.eKind = DBKIND_NAME; - dbLocalColumnID.uName.pwszName = const_cast(pCurrentColumn->wzName); - dbcdConstraint.cForeignKeyColumns = 1; - dbcdConstraint.rgForeignKeyColumnList = &dbLocalColumnID; - - dbcdConstraint.pwszConstraintText = NULL; - dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION; - dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION; - dbcdConstraint.MatchType = DBMATCHTYPE_FULL; - dbcdConstraint.Deferrability = 0; - dbcdConstraint.cReserved = 0; - dbcdConstraint.rgReserved = NULL; - - hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint); - if (DB_E_DUPLICATECONSTRAINTID == hr) - { - hr = S_OK; - } - ExitOnFailure(hr, "Failed to add constraint named: %ls to table: %ls", pCurrentColumn->wzRelationName, pTableSchema->wzName); - } - } - -LExit: - ReleaseObject(pTableDefinitionWithConstraints); - - return hr; -} - -static HRESULT SetSessionProperties( - __in ISessionProperties *pISessionProperties - ) -{ - HRESULT hr = S_OK; - DBPROP rgdbpDataSourceProp[1]; - DBPROPSET rgdbpDataSourcePropSet[1]; - - rgdbpDataSourceProp[0].dwPropertyID = DBPROP_SSCE_TRANSACTION_COMMIT_MODE; - rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpDataSourceProp[0].vValue.vt = VT_I4; - rgdbpDataSourceProp[0].vValue.lVal = DBPROPVAL_SSCE_TCM_FLUSH; - - rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_SSCE_SESSION; - rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; - rgdbpDataSourcePropSet[0].cProperties = 1; - - hr = pISessionProperties->SetProperties(1, rgdbpDataSourcePropSet); - ExitOnFailure(hr, "Failed to set session properties"); - -LExit: - return hr; -} - -static HRESULT GetDatabaseSchemaInfo( - __in SCE_DATABASE *pDatabase, - __out LPWSTR *psczSchemaType, - __out DWORD *pdwVersion - ) -{ - HRESULT hr = S_OK; - LPWSTR sczSchemaType = NULL; - DWORD dwVersionFound = 0; - SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0]; - SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable}; - // Database object with our alternate schema - SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema }; - SCE_ROW_HANDLE sceRow = NULL; - - hr = OpenSchema(pDatabase, &fullSchema); - ExitOnFailure(hr, "Failed to ensure internal version schema"); - - hr = SceGetFirstRow(&database, 0, &sceRow); - ExitOnFailure(hr, "Failed to get first row in internal version schema table"); - - hr = SceGetColumnString(sceRow, 0, &sczSchemaType); - ExitOnFailure(hr, "Failed to get internal schematype"); - - hr = SceGetColumnDword(sceRow, 1, &dwVersionFound); - ExitOnFailure(hr, "Failed to get internal version"); - - *psczSchemaType = sczSchemaType; - sczSchemaType = NULL; - *pdwVersion = dwVersionFound; - -LExit: - SceCloseTable(&schemaTable); // ignore failure - ReleaseStr(sczSchemaType); - ReleaseSceRow(sceRow); - - return hr; -} - -static HRESULT SetDatabaseSchemaInfo( - __in SCE_DATABASE *pDatabase, - __in LPCWSTR wzSchemaType, - __in DWORD dwVersion - ) -{ - HRESULT hr = S_OK; - BOOL fInSceTransaction = FALSE; - SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0]; - SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable}; - // Database object with our alternate schema - SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema }; - SCE_ROW_HANDLE sceRow = NULL; - - hr = EnsureSchema(pDatabase, &fullSchema); - ExitOnFailure(hr, "Failed to ensure internal version schema"); - - hr = SceBeginTransaction(&database); - ExitOnFailure(hr, "Failed to begin transaction"); - fInSceTransaction = TRUE; - - hr = SceGetFirstRow(&database, 0, &sceRow); - if (E_NOTFOUND == hr) - { - hr = ScePrepareInsert(&database, 0, &sceRow); - ExitOnFailure(hr, "Failed to insert only row into internal version schema table"); - } - else - { - ExitOnFailure(hr, "Failed to get first row in internal version schema table"); - } - - hr = SceSetColumnString(sceRow, 0, wzSchemaType); - ExitOnFailure(hr, "Failed to set internal schematype to: %ls", wzSchemaType); - - hr = SceSetColumnDword(sceRow, 1, dwVersion); - ExitOnFailure(hr, "Failed to set internal version to: %u", dwVersion); - - hr = SceFinishUpdate(sceRow); - ExitOnFailure(hr, "Failed to insert first row in internal version schema table"); - - hr = SceCommitTransaction(&database); - ExitOnFailure(hr, "Failed to commit transaction"); - fInSceTransaction = FALSE; - -LExit: - SceCloseTable(&schemaTable); // ignore failure - ReleaseSceRow(sceRow); - if (fInSceTransaction) - { - SceRollbackTransaction(&database); - } - - return hr; -} - -static void ReleaseDatabase( - SCE_DATABASE *pDatabase - ) -{ - if (NULL != pDatabase) - { - if (NULL != pDatabase->pdsSchema) - { - for (DWORD i = 0; i < pDatabase->pdsSchema->cTables; ++i) - { - SceCloseTable(pDatabase->pdsSchema->rgTables + i); - } - } - - if (NULL != pDatabase->sdbHandle) - { - ReleaseDatabaseInternal(reinterpret_cast(pDatabase->sdbHandle)); - } - } - ReleaseMem(pDatabase); -} - -static void ReleaseDatabaseInternal( - SCE_DATABASE_INTERNAL *pDatabaseInternal - ) -{ - HRESULT hr = S_OK; - - if (NULL != pDatabaseInternal) - { - ReleaseObject(pDatabaseInternal->pITransactionLocal); - ReleaseObject(pDatabaseInternal->pIOpenRowset); - ReleaseObject(pDatabaseInternal->pISessionProperties); - ReleaseObject(pDatabaseInternal->pIDBCreateSession); - ReleaseObject(pDatabaseInternal->pIDBProperties); - - if (NULL != pDatabaseInternal->pIDBInitialize) - { - hr = pDatabaseInternal->pIDBInitialize->Uninitialize(); - if (FAILED(hr)) - { - TraceError(hr, "Failed to call uninitialize on IDBInitialize"); - } - ReleaseObject(pDatabaseInternal->pIDBInitialize); - } - - if (NULL != pDatabaseInternal->hSqlCeDll) - { - if (!::FreeLibrary(pDatabaseInternal->hSqlCeDll)) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - TraceError(hr, "Failed to free sql ce dll"); - } - } - } - - // If there was a temp file we copied to (for read-only databases), delete it after close - if (NULL != pDatabaseInternal->sczTempDbFile) - { - hr = FileEnsureDelete(pDatabaseInternal->sczTempDbFile); - if (FAILED(hr)) - { - TraceError(hr, "Failed to delete temporary database file (copied here because the database was opened as read-only): %ls", pDatabaseInternal->sczTempDbFile); - } - ReleaseStr(pDatabaseInternal->sczTempDbFile); - } - - ReleaseMem(pDatabaseInternal); -} - -#endif // end SKIP_SCE_COMPILE diff --git a/src/dutil/shelutil.cpp b/src/dutil/shelutil.cpp deleted file mode 100644 index 2eb9a52a..00000000 --- a/src/dutil/shelutil.cpp +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ShelExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) -#define ShelExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) -#define ShelExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) -#define ShelExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) -#define ShelExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) -#define ShelExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) -#define ShelExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) -#define ShelExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) -#define ShelExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) -#define ShelExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) -#define ShelExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SHELUTIL, e, x, s, __VA_ARGS__) -#define ShelExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SHELUTIL, g, x, s, __VA_ARGS__) - -static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW; - -static HRESULT GetDesktopShellView( - __in REFIID riid, - __out void **ppv - ); -static HRESULT GetShellDispatchFromView( - __in IShellView *psv, - __in REFIID riid, - __out void **ppv - ); - -/******************************************************************** - ShelFunctionOverride - overrides the shell functions. Typically used - for unit testing. - -*********************************************************************/ -extern "C" void DAPI ShelFunctionOverride( - __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW - ) -{ - vpfnShellExecuteExW = pfnShellExecuteExW ? pfnShellExecuteExW : ::ShellExecuteExW; -} - - -/******************************************************************** - ShelExec() - executes a target. - -*******************************************************************/ -extern "C" HRESULT DAPI ShelExec( - __in_z LPCWSTR wzTargetPath, - __in_z_opt LPCWSTR wzParameters, - __in_z_opt LPCWSTR wzVerb, - __in_z_opt LPCWSTR wzWorkingDirectory, - __in int nShowCmd, - __in_opt HWND hwndParent, - __out_opt HANDLE* phProcess - ) -{ - HRESULT hr = S_OK; - SHELLEXECUTEINFOW shExecInfo = {}; - - shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); - shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; - shExecInfo.hwnd = hwndParent; - shExecInfo.lpVerb = wzVerb; - shExecInfo.lpFile = wzTargetPath; - shExecInfo.lpParameters = wzParameters; - shExecInfo.lpDirectory = wzWorkingDirectory; - shExecInfo.nShow = nShowCmd; - - if (!vpfnShellExecuteExW(&shExecInfo)) - { - ShelExitWithLastError(hr, "ShellExecEx failed with return code: %d", Dutil_er); - } - - if (phProcess) - { - *phProcess = shExecInfo.hProcess; - shExecInfo.hProcess = NULL; - } - -LExit: - ReleaseHandle(shExecInfo.hProcess); - - return hr; -} - - -/******************************************************************** - ShelExecUnelevated() - executes a target unelevated. - -*******************************************************************/ -extern "C" HRESULT DAPI ShelExecUnelevated( - __in_z LPCWSTR wzTargetPath, - __in_z_opt LPCWSTR wzParameters, - __in_z_opt LPCWSTR wzVerb, - __in_z_opt LPCWSTR wzWorkingDirectory, - __in int nShowCmd - ) -{ - HRESULT hr = S_OK; - BSTR bstrTargetPath = NULL; - VARIANT vtParameters = { }; - VARIANT vtVerb = { }; - VARIANT vtWorkingDirectory = { }; - VARIANT vtShow = { }; - IShellView* psv = NULL; - IShellDispatch2* psd = NULL; - - bstrTargetPath = ::SysAllocString(wzTargetPath); - ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate target path BSTR."); - - if (wzParameters && *wzParameters) - { - vtParameters.vt = VT_BSTR; - vtParameters.bstrVal = ::SysAllocString(wzParameters); - ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate parameters BSTR."); - } - - if (wzVerb && *wzVerb) - { - vtVerb.vt = VT_BSTR; - vtVerb.bstrVal = ::SysAllocString(wzVerb); - ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate verb BSTR."); - } - - if (wzWorkingDirectory && *wzWorkingDirectory) - { - vtWorkingDirectory.vt = VT_BSTR; - vtWorkingDirectory.bstrVal = ::SysAllocString(wzWorkingDirectory); - ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate working directory BSTR."); - } - - vtShow.vt = VT_INT; - vtShow.intVal = nShowCmd; - - hr = GetDesktopShellView(IID_PPV_ARGS(&psv)); - ShelExitOnFailure(hr, "Failed to get desktop shell view."); - - hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd)); - ShelExitOnFailure(hr, "Failed to get shell dispatch from view."); - - hr = psd->ShellExecute(bstrTargetPath, vtParameters, vtWorkingDirectory, vtVerb, vtShow); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); - } - ShelExitOnRootFailure(hr, "Failed to launch unelevate executable: %ls", bstrTargetPath); - -LExit: - ReleaseObject(psd); - ReleaseObject(psv); - ReleaseBSTR(vtWorkingDirectory.bstrVal); - ReleaseBSTR(vtVerb.bstrVal); - ReleaseBSTR(vtParameters.bstrVal); - ReleaseBSTR(bstrTargetPath); - - return hr; -} - - -/******************************************************************** - ShelGetFolder() - gets a folder by CSIDL. - -*******************************************************************/ -extern "C" HRESULT DAPI ShelGetFolder( - __out_z LPWSTR* psczFolderPath, - __in int csidlFolder - ) -{ - HRESULT hr = S_OK; - WCHAR wzPath[MAX_PATH]; - - hr = ::SHGetFolderPathW(NULL, csidlFolder | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, wzPath); - ShelExitOnFailure(hr, "Failed to get folder path for CSIDL: %d", csidlFolder); - - hr = StrAllocString(psczFolderPath, wzPath, 0); - ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", wzPath); - - hr = PathBackslashTerminate(psczFolderPath); - ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); - -LExit: - return hr; -} - - -/******************************************************************** - ShelGetKnownFolder() - gets a folder by KNOWNFOLDERID. - - Note: return E_NOTIMPL if called on pre-Vista operating systems. -*******************************************************************/ -#ifndef REFKNOWNFOLDERID -#define REFKNOWNFOLDERID REFGUID -#endif - -#ifndef KF_FLAG_CREATE -#define KF_FLAG_CREATE 0x00008000 // Make sure that the folder already exists or create it and apply security specified in folder definition -#endif - -EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)( - REFKNOWNFOLDERID rfid, - DWORD dwFlags, - HANDLE hToken, - PWSTR *ppszPath - ); - -extern "C" HRESULT DAPI ShelGetKnownFolder( - __out_z LPWSTR* psczFolderPath, - __in REFKNOWNFOLDERID rfidFolder - ) -{ - HRESULT hr = S_OK; - HMODULE hShell32Dll = NULL; - PFN_SHGetKnownFolderPath pfn = NULL; - LPWSTR pwzPath = NULL; - - hr = LoadSystemLibrary(L"shell32.dll", &hShell32Dll); - if (E_MODNOTFOUND == hr) - { - TraceError(hr, "Failed to load shell32.dll"); - ExitFunction1(hr = E_NOTIMPL); - } - ShelExitOnFailure(hr, "Failed to load shell32.dll."); - - pfn = reinterpret_cast(::GetProcAddress(hShell32Dll, "SHGetKnownFolderPath")); - ShelExitOnNull(pfn, hr, E_NOTIMPL, "Failed to find SHGetKnownFolderPath entry point."); - - hr = pfn(rfidFolder, KF_FLAG_CREATE, NULL, &pwzPath); - ShelExitOnFailure(hr, "Failed to get known folder path."); - - hr = StrAllocString(psczFolderPath, pwzPath, 0); - ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", pwzPath); - - hr = PathBackslashTerminate(psczFolderPath); - ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); - -LExit: - if (pwzPath) - { - ::CoTaskMemFree(pwzPath); - } - - if (hShell32Dll) - { - ::FreeLibrary(hShell32Dll); - } - - return hr; -} - - -// Internal functions. - -static HRESULT GetDesktopShellView( - __in REFIID riid, - __out void **ppv - ) -{ - HRESULT hr = S_OK; - IShellWindows* psw = NULL; - HWND hwnd = NULL; - IDispatch* pdisp = NULL; - VARIANT vEmpty = {}; // VT_EMPTY - IShellBrowser* psb = NULL; - IShellFolder* psf = NULL; - IShellView* psv = NULL; - - // use the shell view for the desktop using the shell windows automation to find the - // desktop web browser and then grabs its view - // returns IShellView, IFolderView and related interfaces - hr = ::CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw)); - ShelExitOnFailure(hr, "Failed to get shell view."); - - hr = psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp); - if (S_OK == hr) - { - hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb)); - ShelExitOnFailure(hr, "Failed to get desktop window."); - - hr = psb->QueryActiveShellView(&psv); - ShelExitOnFailure(hr, "Failed to get active shell view."); - - hr = psv->QueryInterface(riid, ppv); - ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); - } - else if (S_FALSE == hr) - { - //Windows XP - hr = SHGetDesktopFolder(&psf); - ShelExitOnFailure(hr, "Failed to get desktop folder."); - - hr = psf->CreateViewObject(NULL, IID_IShellView, ppv); - ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); - } - else - { - ShelExitOnFailure(hr, "Failed to get desktop window."); - } - -LExit: - ReleaseObject(psv); - ReleaseObject(psb); - ReleaseObject(psf); - ReleaseObject(pdisp); - ReleaseObject(psw); - - return hr; -} - -static HRESULT GetShellDispatchFromView( - __in IShellView *psv, - __in REFIID riid, - __out void **ppv - ) -{ - HRESULT hr = S_OK; - IDispatch *pdispBackground = NULL; - IShellFolderViewDual *psfvd = NULL; - IDispatch *pdisp = NULL; - - // From a shell view object, gets its automation interface and from that get the shell - // application object that implements IShellDispatch2 and related interfaces. - hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground)); - ShelExitOnFailure(hr, "Failed to get the automation interface for shell."); - - hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd)); - ShelExitOnFailure(hr, "Failed to get shell folder view dual."); - - hr = psfvd->get_Application(&pdisp); - ShelExitOnFailure(hr, "Failed to application object."); - - hr = pdisp->QueryInterface(riid, ppv); - ShelExitOnFailure(hr, "Failed to get IShellDispatch2."); - -LExit: - ReleaseObject(pdisp); - ReleaseObject(psfvd); - ReleaseObject(pdispBackground); - - return hr; -} diff --git a/src/dutil/sqlutil.cpp b/src/dutil/sqlutil.cpp deleted file mode 100644 index 782c7088..00000000 --- a/src/dutil/sqlutil.cpp +++ /dev/null @@ -1,1002 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// okay, this may look a little weird, but sqlutil.h cannot be in the -// pre-compiled header because we need to #define these things so the -// correct GUID's get pulled into this object file -#include -#define DBINITCONSTANTS -#include "sqlutil.h" - - -//Please note that only SQL native client 11 has TLS1.2 support -#define _SQLNCLI_OLEDB_DEPRECATE_WARNING - -#if !defined(SQLNCLI_VER) -#define SQLNCLI_VER 1100 -#endif - -#if SQLNCLI_VER >= 1100 -#if defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_) -#define SQLNCLI_CLSID CLSID_SQLNCLI11 -#endif // defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_) -extern const GUID OLEDBDECLSPEC _SQLNCLI_OLEDB_DEPRECATE_WARNING CLSID_SQLNCLI11 = { 0x397C2819L,0x8272,0x4532,{ 0xAD,0x3A,0xFB,0x5E,0x43,0xBE,0xAA,0x39 } }; -#endif // SQLNCLI_VER >= 1100 - -// Exit macros -#define SqlExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) -#define SqlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__) -#define SqlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__) -#define SqlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__) -#define SqlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__) -#define SqlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SQLUTIL, e, x, s, __VA_ARGS__) -#define SqlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SQLUTIL, g, x, s, __VA_ARGS__) - -// private prototypes -static HRESULT InitializeDatabaseConnection( - __in REFCLSID rclsid, - __in_z LPCSTR szFriendlyClsidName, - __in DBPROPSET rgdbpsetInit[], - __in_ecount(rgdbpsetInit) DWORD cdbpsetInit, - __out IDBCreateSession** ppidbSession - ); -HRESULT DumpErrorRecords(); -static HRESULT FileSpecToString( - __in const SQL_FILESPEC* psf, - __out LPWSTR* ppwz - ); -static HRESULT EscapeSqlIdentifier( - __in_z LPCWSTR wzDatabase, - __deref_out_z LPWSTR* ppwz - ); - - -/******************************************************************** - SqlConnectDatabase - establishes a connection to a database - - NOTE: wzInstance is optional - if fIntegratedAuth is set then wzUser and wzPassword are ignored -********************************************************************/ -extern "C" HRESULT DAPI SqlConnectDatabase( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __out IDBCreateSession** ppidbSession - ) -{ - Assert(wzServer && wzDatabase && *wzDatabase && ppidbSession); - - HRESULT hr = S_OK; - LPWSTR pwzServerInstance = NULL; - DBPROP rgdbpInit[4] = { }; - DBPROPSET rgdbpsetInit[1] = { }; - ULONG cProperties = 0; - - // if there is an instance - if (wzInstance && *wzInstance) - { - hr = StrAllocFormatted(&pwzServerInstance, L"%s\\%s", wzServer, wzInstance); - } - else - { - hr = StrAllocString(&pwzServerInstance, wzServer, 0); - } - SqlExitOnFailure(hr, "failed to allocate memory for the server instance"); - - // server[\instance] - rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_DATASOURCE; - rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpInit[cProperties].colid = DB_NULLID; - ::VariantInit(&rgdbpInit[cProperties].vValue); - rgdbpInit[cProperties].vValue.vt = VT_BSTR; - rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(pwzServerInstance); - ++cProperties; - - // database - rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_CATALOG; - rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpInit[cProperties].colid = DB_NULLID; - ::VariantInit(&rgdbpInit[cProperties].vValue); - rgdbpInit[cProperties].vValue.vt = VT_BSTR; - rgdbpInit[cProperties].vValue.bstrVal= ::SysAllocString(wzDatabase); - ++cProperties; - - if (fIntegratedAuth) - { - // username - rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_INTEGRATED; - rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpInit[cProperties].colid = DB_NULLID; - ::VariantInit(&rgdbpInit[cProperties].vValue); - rgdbpInit[cProperties].vValue.vt = VT_BSTR; - rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(L"SSPI"); // default windows authentication - ++cProperties; - } - else - { - // username - rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_USERID; - rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpInit[cProperties].colid = DB_NULLID; - ::VariantInit(&rgdbpInit[cProperties].vValue); - rgdbpInit[cProperties].vValue.vt = VT_BSTR; - rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzUser); - ++cProperties; - - // password - rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_PASSWORD; - rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; - rgdbpInit[cProperties].colid = DB_NULLID; - ::VariantInit(&rgdbpInit[cProperties].vValue); - rgdbpInit[cProperties].vValue.vt = VT_BSTR; - rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzPassword); - ++cProperties; - } - - // put the properties into a set - rgdbpsetInit[0].guidPropertySet = DBPROPSET_DBINIT; - rgdbpsetInit[0].rgProperties = rgdbpInit; - rgdbpsetInit[0].cProperties = cProperties; - - // obtain access to the SQL Native Client provider - hr = InitializeDatabaseConnection(SQLNCLI_CLSID, "SQL Native Client", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession); - if (FAILED(hr)) - { - SqlExitTrace(hr, "Could not initialize SQL Native Client, falling back to SQL OLE DB..."); - - // try OLE DB but if that fails return original error failure - HRESULT hr2 = InitializeDatabaseConnection(CLSID_SQLOLEDB, "SQL OLE DB", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession); - if (FAILED(hr2)) - { - SqlExitTrace(hr2, "Could not initialize SQL OLE DB either, giving up."); - } - else - { - hr = S_OK; - } - } - -LExit: - for (; 0 < cProperties; cProperties--) - { - ::VariantClear(&rgdbpInit[cProperties - 1].vValue); - } - - ReleaseStr(pwzServerInstance); - - return hr; -} - - -/******************************************************************** - SqlStartTransaction - Starts a new transaction that must be ended - -*********************************************************************/ -extern "C" HRESULT DAPI SqlStartTransaction( - __in IDBCreateSession* pidbSession, - __out IDBCreateCommand** ppidbCommand, - __out ITransaction** ppit - ) -{ - Assert(pidbSession && ppit); - - HRESULT hr = S_OK; - - hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)ppidbCommand); - SqlExitOnFailure(hr, "unable to create command from session"); - - hr = (*ppidbCommand)->QueryInterface(IID_ITransactionLocal, (LPVOID*)ppit); - SqlExitOnFailure(hr, "Unable to QueryInterface session to get ITransactionLocal"); - - hr = ((ITransactionLocal*)*ppit)->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL); - -LExit: - - return hr; -} - -/******************************************************************** - SqlEndTransaction - Ends the transaction - - NOTE: if fCommit, will commit the transaction, otherwise rolls back -*********************************************************************/ -extern "C" HRESULT DAPI SqlEndTransaction( - __in ITransaction* pit, - __in BOOL fCommit - ) -{ - Assert(pit); - - HRESULT hr = S_OK; - - if (fCommit) - { - hr = pit->Commit(FALSE, XACTTC_SYNC, 0); - SqlExitOnFailure(hr, "commit of transaction failed"); - } - else - { - hr = pit->Abort(NULL, FALSE, FALSE); - SqlExitOnFailure(hr, "abort of transaction failed"); - } - -LExit: - - return hr; -} - - -/******************************************************************** - SqlDatabaseExists - determines if database exists - - NOTE: wzInstance is optional - if fIntegratedAuth is set then wzUser and wzPassword are ignored - returns S_OK if database exist - returns S_FALSE if database does not exist - returns E_* on error -********************************************************************/ -extern "C" HRESULT DAPI SqlDatabaseExists( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(wzServer && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - IDBCreateSession* pidbSession = NULL; - - hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); - SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); - - hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); - -LExit: - ReleaseObject(pidbSession); - - return hr; -} - - -/******************************************************************** - SqlSessionDatabaseExists - determines if database exists - - NOTE: pidbSession must be connected to master database - returns S_OK if database exist - returns S_FALSE if database does not exist - returns E_* on error -********************************************************************/ -extern "C" HRESULT DAPI SqlSessionDatabaseExists( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(pidbSession && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - - LPWSTR pwzQuery = NULL; - IRowset* pirs = NULL; - - DBCOUNTITEM cRows = 0; - HROW rghRows[1]; - HROW* prow = rghRows; - - // - // query to see if the database exists - // - hr = StrAllocFormatted(&pwzQuery, L"SELECT name FROM sysdatabases WHERE name='%s'", wzDatabase); - SqlExitOnFailure(hr, "failed to allocate query string to ensure database exists"); - - hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, &pirs, NULL, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to get database list from 'master' database"); - Assert(pirs); - - // - // check to see if the database was returned - // - hr = pirs->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRows, &prow); - SqlExitOnFailure(hr, "failed to get row with database name"); - - // succeeded but no database - if ((DB_S_ENDOFROWSET == hr) || (0 == cRows)) - { - hr = S_FALSE; - } - -LExit: - ReleaseObject(pirs); - ReleaseStr(pwzQuery); - - return hr; -} - - -/******************************************************************** - SqlDatabaseEnsureExists - creates a database if it does not exist - - NOTE: wzInstance is optional - if fIntegratedAuth is set then wzUser and wzPassword are ignored -********************************************************************/ -extern "C" HRESULT DAPI SqlDatabaseEnsureExists( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(wzServer && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - IDBCreateSession* pidbSession = NULL; - - // - // connect to the master database to create the new database - // - hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); - SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); - - hr = SqlSessionDatabaseEnsureExists(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); - - Assert(S_OK == hr); -LExit: - ReleaseObject(pidbSession); - - return hr; -} - - -/******************************************************************** - SqlSessionDatabaseEnsureExists - creates a database if it does not exist - - NOTE: pidbSession must be connected to the master database -********************************************************************/ -extern "C" HRESULT DAPI SqlSessionDatabaseEnsureExists( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(pidbSession && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - - hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); - - if (S_FALSE == hr) - { - hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); - } - // else database already exists, return S_FALSE - - Assert(S_OK == hr); -LExit: - - return hr; -} - - -/******************************************************************** - SqlCreateDatabase - creates a database on the server - - NOTE: wzInstance is optional - if fIntegratedAuth is set then wzUser and wzPassword are ignored -********************************************************************/ -extern "C" HRESULT DAPI SqlCreateDatabase( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(wzServer && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - IDBCreateSession* pidbSession = NULL; - - // - // connect to the master database to create the new database - // - hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); - SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); - - hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); - - Assert(S_OK == hr); -LExit: - ReleaseObject(pidbSession); - - return hr; -} - - -/******************************************************************** - SqlSessionCreateDatabase - creates a database on the server - - NOTE: pidbSession must be connected to the master database -********************************************************************/ -extern "C" HRESULT DAPI SqlSessionCreateDatabase( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __in_opt const SQL_FILESPEC* psfDatabase, - __in_opt const SQL_FILESPEC* psfLog, - __out_opt BSTR* pbstrErrorDescription - ) -{ - HRESULT hr = S_OK; - LPWSTR pwzDbFile = NULL; - LPWSTR pwzLogFile = NULL; - LPWSTR pwzQuery = NULL; - LPWSTR pwzDatabaseEscaped = NULL; - - if (psfDatabase) - { - hr = FileSpecToString(psfDatabase, &pwzDbFile); - SqlExitOnFailure(hr, "failed to convert db filespec to string"); - } - - if (psfLog) - { - hr = FileSpecToString(psfLog, &pwzLogFile); - SqlExitOnFailure(hr, "failed to convert log filespec to string"); - } - - hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped); - SqlExitOnFailure(hr, "failed to escape database string"); - - hr = StrAllocFormatted(&pwzQuery, L"CREATE DATABASE %s %s%s %s%s", pwzDatabaseEscaped, pwzDbFile ? L"ON " : L"", pwzDbFile ? pwzDbFile : L"", pwzLogFile ? L"LOG ON " : L"", pwzLogFile ? pwzLogFile : L""); - SqlExitOnFailure(hr, "failed to allocate query to create database: %ls", pwzDatabaseEscaped); - - hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to create database: %ls, Query: %ls", pwzDatabaseEscaped, pwzQuery); - -LExit: - ReleaseStr(pwzQuery); - ReleaseStr(pwzLogFile); - ReleaseStr(pwzDbFile); - ReleaseStr(pwzDatabaseEscaped); - - return hr; -} - - -/******************************************************************** - SqlDropDatabase - removes a database from a server if it exists - - NOTE: wzInstance is optional - if fIntegratedAuth is set then wzUser and wzPassword are ignored -********************************************************************/ -extern "C" HRESULT DAPI SqlDropDatabase( - __in_z LPCWSTR wzServer, - __in_z LPCWSTR wzInstance, - __in_z LPCWSTR wzDatabase, - __in BOOL fIntegratedAuth, - __in_z LPCWSTR wzUser, - __in_z LPCWSTR wzPassword, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(wzServer && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - IDBCreateSession* pidbSession = NULL; - - // - // connect to the master database to search for wzDatabase - // - hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); - SqlExitOnFailure(hr, "Failed to connect to 'master' database"); - - hr = SqlSessionDropDatabase(pidbSession, wzDatabase, pbstrErrorDescription); - -LExit: - ReleaseObject(pidbSession); - - return hr; -} - - -/******************************************************************** - SqlSessionDropDatabase - removes a database from a server if it exists - - NOTE: pidbSession must be connected to the master database -********************************************************************/ -extern "C" HRESULT DAPI SqlSessionDropDatabase( - __in IDBCreateSession* pidbSession, - __in_z LPCWSTR wzDatabase, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(pidbSession && wzDatabase && *wzDatabase); - - HRESULT hr = S_OK; - LPWSTR pwzQuery = NULL; - LPWSTR pwzDatabaseEscaped = NULL; - - hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); - SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); - - hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped); - SqlExitOnFailure(hr, "failed to escape database string"); - - if (S_OK == hr) - { - hr = StrAllocFormatted(&pwzQuery, L"DROP DATABASE %s", pwzDatabaseEscaped); - SqlExitOnFailure(hr, "failed to allocate query to drop database: %ls", pwzDatabaseEscaped); - - hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription); - SqlExitOnFailure(hr, "Failed to drop database"); - } - -LExit: - ReleaseStr(pwzQuery); - ReleaseStr(pwzDatabaseEscaped); - - return hr; -} - - -/******************************************************************** - SqlSessionExecuteQuery - executes a query and returns the results if desired - - NOTE: ppirs and pcRoes and pbstrErrorDescription are optional -********************************************************************/ -extern "C" HRESULT DAPI SqlSessionExecuteQuery( - __in IDBCreateSession* pidbSession, - __in __sql_command LPCWSTR wzSql, - __out_opt IRowset** ppirs, - __out_opt DBROWCOUNT* pcRows, - __out_opt BSTR* pbstrErrorDescription - ) -{ - Assert(pidbSession); - - HRESULT hr = S_OK; - IDBCreateCommand* pidbCommand = NULL; - ICommandText* picmdText = NULL; - ICommand* picmd = NULL; - DBROWCOUNT cRows = 0; - - if (pcRows) - { - *pcRows = NULL; - } - - // - // create the command - // - hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)&pidbCommand); - SqlExitOnFailure(hr, "failed to create database session"); - hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd); - SqlExitOnFailure(hr, "failed to create command to execute session"); - - // - // set the sql text into the command - // - hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText); - SqlExitOnFailure(hr, "failed to get command text object for command"); - hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql); - SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); - - // - // execute the command - // - hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast(ppirs)); - SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); - - if (DB_S_ERRORSOCCURRED == hr) - { - hr = E_FAIL; - } - - if (pcRows) - { - *pcRows = cRows; - } - -LExit: - - if (FAILED(hr) && picmd && pbstrErrorDescription) - { - HRESULT hrGetErrors = SqlGetErrorInfo(picmd, IID_ICommandText, 0x409, NULL, pbstrErrorDescription); // TODO: use current locale instead of always American-English - if (FAILED(hrGetErrors)) - { - ReleaseBSTR(*pbstrErrorDescription); - } - } - - ReleaseObject(picmd); - ReleaseObject(picmdText); - ReleaseObject(pidbCommand); - - return hr; -} - - -/******************************************************************** - SqlCommandExecuteQuery - executes a SQL command and returns the results if desired - - NOTE: ppirs and pcRoes are optional -********************************************************************/ -extern "C" HRESULT DAPI SqlCommandExecuteQuery( - __in IDBCreateCommand* pidbCommand, - __in __sql_command LPCWSTR wzSql, - __out IRowset** ppirs, - __out DBROWCOUNT* pcRows - ) -{ - Assert(pidbCommand); - - HRESULT hr = S_OK; - ICommandText* picmdText = NULL; - ICommand* picmd = NULL; - DBROWCOUNT cRows = 0; - - if (pcRows) - { - *pcRows = NULL; - } - - // - // create the command - // - hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd); - SqlExitOnFailure(hr, "failed to create command to execute session"); - - // - // set the sql text into the command - // - hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText); - SqlExitOnFailure(hr, "failed to get command text object for command"); - hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql); - SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); - - // - // execute the command - // - hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast(ppirs)); - SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); - - if (DB_S_ERRORSOCCURRED == hr) - { - hr = E_FAIL; - } - - if (pcRows) - { - *pcRows = cRows; - } - -LExit: - ReleaseObject(picmd); - ReleaseObject(picmdText); - - return hr; -} - - -/******************************************************************** - SqlGetErrorInfo - gets error information from the last SQL function call - - NOTE: pbstrErrorSource and pbstrErrorDescription are optional -********************************************************************/ -extern "C" HRESULT DAPI SqlGetErrorInfo( - __in IUnknown* pObjectWithError, - __in REFIID IID_InterfaceWithError, - __in DWORD dwLocaleId, - __out_opt BSTR* pbstrErrorSource, - __out_opt BSTR* pbstrErrorDescription - ) -{ - HRESULT hr = S_OK; - Assert(pObjectWithError); - - // interfaces needed to extract error information out - ISupportErrorInfo* pISupportErrorInfo = NULL; - IErrorInfo* pIErrorInfoAll = NULL; - IErrorRecords* pIErrorRecords = NULL; - IErrorInfo* pIErrorInfoRecord = NULL; - - // only ask for error information if the interface supports it. - hr = pObjectWithError->QueryInterface(IID_ISupportErrorInfo,(void**)&pISupportErrorInfo); - SqlExitOnFailure(hr, "No error information was found for object."); - - hr = pISupportErrorInfo->InterfaceSupportsErrorInfo(IID_InterfaceWithError); - SqlExitOnFailure(hr, "InterfaceWithError is not supported for object with error"); - - // ignore the return of GetErrorInfo it can succeed and return a NULL pointer in pIErrorInfoAll anyway - hr = ::GetErrorInfo(0, &pIErrorInfoAll); - SqlExitOnFailure(hr, "failed to get error info"); - - if (S_OK == hr && pIErrorInfoAll) - { - // see if it's a valid OLE DB IErrorInfo interface that exposes a list of records - hr = pIErrorInfoAll->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords); - if (SUCCEEDED(hr)) - { - ULONG cErrors = 0; - pIErrorRecords->GetRecordCount(&cErrors); - - // get the error information for each record - for (ULONG i = 0; i < cErrors; ++i) - { - hr = pIErrorRecords->GetErrorInfo(i, dwLocaleId, &pIErrorInfoRecord); - if (SUCCEEDED(hr)) - { - if (pbstrErrorSource) - { - pIErrorInfoRecord->GetSource(pbstrErrorSource); - } - if (pbstrErrorDescription) - { - pIErrorInfoRecord->GetDescription(pbstrErrorDescription); - } - - ReleaseNullObject(pIErrorInfoRecord); - - break; // TODO: return more than one error in the future! - } - } - - ReleaseNullObject(pIErrorRecords); - } - else // we have a simple error record - { - if (pbstrErrorSource) - { - pIErrorInfoAll->GetSource(pbstrErrorSource); - } - if (pbstrErrorDescription) - { - pIErrorInfoAll->GetDescription(pbstrErrorDescription); - } - } - } - else - { - hr = E_NOMOREITEMS; - } - -LExit: - ReleaseObject(pIErrorInfoRecord); - ReleaseObject(pIErrorRecords); - ReleaseObject(pIErrorInfoAll); - ReleaseObject(pISupportErrorInfo); - - return hr; -} - - -// -// private -// - -static HRESULT InitializeDatabaseConnection( - __in REFCLSID rclsid, - __in_z LPCSTR szFriendlyClsidName, - __in DBPROPSET rgdbpsetInit[], - __in_ecount(rgdbpsetInit) DWORD cdbpsetInit, - __out IDBCreateSession** ppidbSession -) -{ - Unused(szFriendlyClsidName); // only used in DEBUG builds - - HRESULT hr = S_OK; - IDBInitialize* pidbInitialize = NULL; - IDBProperties* pidbProperties = NULL; - - hr = ::CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize, (LPVOID*)&pidbInitialize); - SqlExitOnFailure(hr, "failed to initialize %s", szFriendlyClsidName); - - // create and set the property set - hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties); - SqlExitOnFailure(hr, "failed to get IID_IDBProperties for %s", szFriendlyClsidName); - - hr = pidbProperties->SetProperties(cdbpsetInit, rgdbpsetInit); - SqlExitOnFailure(hr, "failed to set properties for %s", szFriendlyClsidName); - - // initialize connection to datasource - hr = pidbInitialize->Initialize(); - if (FAILED(hr)) - { - DumpErrorRecords(); - } - SqlExitOnFailure(hr, "failed to initialize connection for %s", szFriendlyClsidName); - - hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession); - SqlExitOnFailure(hr, "failed to query for connection session for %s", szFriendlyClsidName); - -LExit: - ReleaseObject(pidbProperties); - ReleaseObject(pidbInitialize); - - return hr; -} - -HRESULT DumpErrorRecords() -{ - HRESULT hr = S_OK; - IErrorInfo* pIErrorInfo = NULL; - IErrorRecords* pIErrorRecords = NULL; - IErrorInfo* pIErrorInfoRecord = NULL; - BSTR bstrDescription = NULL; - ULONG i = 0; - ULONG cRecords = 0; - ERRORINFO ErrorInfo = { }; - - // Get IErrorInfo pointer from OLE. - hr = ::GetErrorInfo(0, &pIErrorInfo); - if (FAILED(hr)) - { - ExitFunction(); - } - - // QI for IID_IErrorRecords. - hr = pIErrorInfo->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords); - if (FAILED(hr)) - { - ExitFunction(); - } - - // Get error record count. - hr = pIErrorRecords->GetRecordCount(&cRecords); - if (FAILED(hr)) - { - ExitFunction(); - } - - // Loop through the error records. - for (i = 0; i < cRecords; i++) - { - // Get pIErrorInfo from pIErrorRecords. - hr = pIErrorRecords->GetErrorInfo(i, 1033, &pIErrorInfoRecord); - - if (SUCCEEDED(hr)) - { - // Get error description and source. - hr = pIErrorInfoRecord->GetDescription(&bstrDescription); - - // Retrieve the ErrorInfo structures. - hr = pIErrorRecords->GetBasicErrorInfo(i, &ErrorInfo); - - SqlExitTrace(ErrorInfo.hrError, "SQL error %lu/%lu: %ls", i + 1, cRecords, bstrDescription); - - ReleaseNullObject(pIErrorInfoRecord); - ReleaseNullBSTR(bstrDescription); - } - } - -LExit: - ReleaseNullBSTR(bstrDescription); - ReleaseObject(pIErrorInfoRecord); - ReleaseObject(pIErrorRecords); - ReleaseObject(pIErrorInfo); - - return hr; -} - -/******************************************************************** - FileSpecToString - -*********************************************************************/ -static HRESULT FileSpecToString( - __in const SQL_FILESPEC* psf, - __out LPWSTR* ppwz - ) -{ - Assert(psf && ppwz); - - HRESULT hr = S_OK; - LPWSTR pwz = NULL; - - hr = StrAllocString(&pwz, L"(", 1024); - SqlExitOnFailure(hr, "failed to allocate string for database file info"); - - SqlExitOnNull(*psf->wzName, hr, E_INVALIDARG, "logical name not specified in database file info"); - SqlExitOnNull(*psf->wzFilename, hr, E_INVALIDARG, "filename not specified in database file info"); - - hr = StrAllocFormatted(&pwz, L"%sNAME=%s", pwz, psf->wzName); - SqlExitOnFailure(hr, "failed to format database file info name: %ls", psf->wzName); - - hr = StrAllocFormatted(&pwz, L"%s, FILENAME='%s'", pwz, psf->wzFilename); - SqlExitOnFailure(hr, "failed to format database file info filename: %ls", psf->wzFilename); - - if (0 != psf->wzSize[0]) - { - hr = StrAllocFormatted(&pwz, L"%s, SIZE=%s", pwz, psf->wzSize); - SqlExitOnFailure(hr, "failed to format database file info size: %ls", psf->wzSize); - } - - if (0 != psf->wzMaxSize[0]) - { - hr = StrAllocFormatted(&pwz, L"%s, MAXSIZE=%s", pwz, psf->wzMaxSize); - SqlExitOnFailure(hr, "failed to format database file info maxsize: %ls", psf->wzMaxSize); - } - - if (0 != psf->wzGrow[0]) - { - hr = StrAllocFormatted(&pwz, L"%s, FILEGROWTH=%s", pwz, psf->wzGrow); - SqlExitOnFailure(hr, "failed to format database file info growth: %ls", psf->wzGrow); - } - - hr = StrAllocFormatted(&pwz, L"%s)", pwz); - SqlExitOnFailure(hr, "failed to allocate string for file spec"); - - *ppwz = pwz; - pwz = NULL; // null here so it doesn't get freed below - -LExit: - ReleaseStr(pwz); - return hr; -} - -static HRESULT EscapeSqlIdentifier( - __in_z LPCWSTR wzIdentifier, - __deref_out_z LPWSTR* ppwz - ) -{ - Assert(ppwz); - - HRESULT hr = S_OK; - LPWSTR pwz = NULL; - - if (wzIdentifier == NULL) - { - //Just ignore a NULL identifier and clear out the result - ReleaseNullStr(*ppwz); - ExitFunction(); - } - - int cchIdentifier = lstrlenW(wzIdentifier); - - //If an empty string or already escaped just copy - if (cchIdentifier == 0 || (wzIdentifier[0] == '[' && wzIdentifier[cchIdentifier-1] == ']')) - { - hr = StrAllocString(&pwz, wzIdentifier, 0); - SqlExitOnFailure(hr, "failed to format database name: %ls", wzIdentifier); - } - else - { - //escape it - hr = StrAllocFormatted(&pwz, L"[%s]", wzIdentifier); - SqlExitOnFailure(hr, "failed to format escaped database name: %ls", wzIdentifier); - } - - *ppwz = pwz; - pwz = NULL; // null here so it doesn't get freed below - -LExit: - ReleaseStr(pwz); - return hr; -} diff --git a/src/dutil/srputil.cpp b/src/dutil/srputil.cpp deleted file mode 100644 index e44536cc..00000000 --- a/src/dutil/srputil.cpp +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define SrpExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) -#define SrpExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) -#define SrpExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) -#define SrpExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) -#define SrpExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) -#define SrpExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) -#define SrpExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SRPUTIL, p, x, e, s, __VA_ARGS__) -#define SrpExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, p, x, s, __VA_ARGS__) -#define SrpExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SRPUTIL, p, x, e, s, __VA_ARGS__) -#define SrpExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, p, x, s, __VA_ARGS__) -#define SrpExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SRPUTIL, e, x, s, __VA_ARGS__) -#define SrpExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SRPUTIL, g, x, s, __VA_ARGS__) - - -typedef BOOL (WINAPI *PFN_SETRESTOREPTW)( - __in PRESTOREPOINTINFOW pRestorePtSpec, - __out PSTATEMGRSTATUS pSMgrStatus - ); - -static PFN_SETRESTOREPTW vpfnSRSetRestorePointW = NULL; -static HMODULE vhSrClientDll = NULL; - - -static HRESULT InitializeComSecurity(); - - -DAPI_(HRESULT) SrpInitialize( - __in BOOL fInitializeComSecurity - ) -{ - HRESULT hr = S_OK; - - hr = LoadSystemLibrary(L"srclient.dll", &vhSrClientDll); - if (FAILED(hr)) - { - ExitFunction1(hr = E_NOTIMPL); - } - - vpfnSRSetRestorePointW = reinterpret_cast(::GetProcAddress(vhSrClientDll, "SRSetRestorePointW")); - SrpExitOnNullWithLastError(vpfnSRSetRestorePointW, hr, "Failed to find set restore point proc address."); - - // If allowed, initialize COM security to enable NetworkService, - // LocalService and System to make callbacks to the process - // calling System Restore. This is required for any process - // that calls SRSetRestorePoint. - if (fInitializeComSecurity) - { - hr = InitializeComSecurity(); - SrpExitOnFailure(hr, "Failed to initialize security for COM to talk to system restore."); - } - -LExit: - if (FAILED(hr) && vhSrClientDll) - { - SrpUninitialize(); - } - - return hr; -} - -DAPI_(void) SrpUninitialize() -{ - if (vhSrClientDll) - { - ::FreeLibrary(vhSrClientDll); - vhSrClientDll = NULL; - vpfnSRSetRestorePointW = NULL; - } -} - -DAPI_(HRESULT) SrpCreateRestorePoint( - __in_z LPCWSTR wzApplicationName, - __in SRP_ACTION action - ) -{ - HRESULT hr = S_OK; - RESTOREPOINTINFOW restorePoint = { }; - STATEMGRSTATUS status = { }; - - if (!vpfnSRSetRestorePointW) - { - ExitFunction1(hr = E_NOTIMPL); - } - - restorePoint.dwEventType = BEGIN_SYSTEM_CHANGE; - restorePoint.dwRestorePtType = (SRP_ACTION_INSTALL == action) ? APPLICATION_INSTALL : (SRP_ACTION_UNINSTALL == action) ? APPLICATION_UNINSTALL : MODIFY_SETTINGS; - ::StringCbCopyW(restorePoint.szDescription, sizeof(restorePoint.szDescription), wzApplicationName); - - if (!vpfnSRSetRestorePointW(&restorePoint, &status)) - { - SrpExitOnWin32Error(status.nStatus, hr, "Failed to create system restore point."); - } - -LExit: - return hr; -} - - -// internal functions. - -static HRESULT InitializeComSecurity() -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - SECURITY_DESCRIPTOR sd = {0}; - EXPLICIT_ACCESS ea[5] = {0}; - ACL* pAcl = NULL; - ULONGLONG rgSidBA[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; - ULONGLONG rgSidLS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; - ULONGLONG rgSidNS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; - ULONGLONG rgSidPS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; - ULONGLONG rgSidSY[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; - DWORD cbSid = 0; - - // Create the security descriptor explicitly as follows because - // CoInitializeSecurity() will not accept the relative security descriptors - // returned by ConvertStringSecurityDescriptorToSecurityDescriptor(). - // - // The result is a security descriptor that is equivalent to the following - // security descriptor definition language (SDDL) string: - // - // O:BAG:BAD:(A;;0x1;;;LS)(A;;0x1;;;NS)(A;;0x1;;;PS)(A;;0x1;;;SY)(A;;0x1;;;BA) - // - - // Initialize the security descriptor. - if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) - { - SrpExitWithLastError(hr, "Failed to initialize security descriptor for system restore."); - } - - // Create an administrator group security identifier (SID). - cbSid = sizeof(rgSidBA); - if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, rgSidBA, &cbSid)) - { - SrpExitWithLastError(hr, "Failed to create administrator SID for system restore."); - } - - // Create a local service security identifier (SID). - cbSid = sizeof(rgSidLS); - if (!::CreateWellKnownSid(WinLocalServiceSid, NULL, rgSidLS, &cbSid)) - { - SrpExitWithLastError(hr, "Failed to create local service SID for system restore."); - } - - // Create a network service security identifier (SID). - cbSid = sizeof(rgSidNS); - if (!::CreateWellKnownSid(WinNetworkServiceSid, NULL, rgSidNS, &cbSid)) - { - SrpExitWithLastError(hr, "Failed to create network service SID for system restore."); - } - - // Create a personal account security identifier (SID). - cbSid = sizeof(rgSidPS); - if (!::CreateWellKnownSid(WinSelfSid, NULL, rgSidPS, &cbSid)) - { - SrpExitWithLastError(hr, "Failed to create self SID for system restore."); - } - - // Create a local service security identifier (SID). - cbSid = sizeof(rgSidSY); - if (!::CreateWellKnownSid(WinLocalSystemSid, NULL, rgSidSY, &cbSid)) - { - SrpExitWithLastError(hr, "Failed to create local system SID for system restore."); - } - - // Setup the access control entries (ACE) for COM. COM_RIGHTS_EXECUTE and - // COM_RIGHTS_EXECUTE_LOCAL are the minimum access rights required. - ea[0].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; - ea[0].grfAccessMode = SET_ACCESS; - ea[0].grfInheritance = NO_INHERITANCE; - ea[0].Trustee.pMultipleTrustee = NULL; - ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; - ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP; - ea[0].Trustee.ptstrName = (LPTSTR)rgSidBA; - - ea[1].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; - ea[1].grfAccessMode = SET_ACCESS; - ea[1].grfInheritance = NO_INHERITANCE; - ea[1].Trustee.pMultipleTrustee = NULL; - ea[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; - ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; - ea[1].Trustee.ptstrName = (LPTSTR)rgSidLS; - - ea[2].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; - ea[2].grfAccessMode = SET_ACCESS; - ea[2].grfInheritance = NO_INHERITANCE; - ea[2].Trustee.pMultipleTrustee = NULL; - ea[2].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; - ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[2].Trustee.TrusteeType = TRUSTEE_IS_GROUP; - ea[2].Trustee.ptstrName = (LPTSTR)rgSidNS; - - ea[3].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; - ea[3].grfAccessMode = SET_ACCESS; - ea[3].grfInheritance = NO_INHERITANCE; - ea[3].Trustee.pMultipleTrustee = NULL; - ea[3].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; - ea[3].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[3].Trustee.TrusteeType = TRUSTEE_IS_GROUP; - ea[3].Trustee.ptstrName = (LPTSTR)rgSidPS; - - ea[4].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; - ea[4].grfAccessMode = SET_ACCESS; - ea[4].grfInheritance = NO_INHERITANCE; - ea[4].Trustee.pMultipleTrustee = NULL; - ea[4].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; - ea[4].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[4].Trustee.TrusteeType = TRUSTEE_IS_GROUP; - ea[4].Trustee.ptstrName = (LPTSTR)rgSidSY; - - // Create an access control list (ACL) using this ACE list. - er = ::SetEntriesInAcl(countof(ea), ea, NULL, &pAcl); - SrpExitOnWin32Error(er, hr, "Failed to create ACL for system restore."); - - // Set the security descriptor owner to Administrators. - if (!::SetSecurityDescriptorOwner(&sd, rgSidBA, FALSE)) - { - SrpExitWithLastError(hr, "Failed to set administrators owner for system restore."); - } - - // Set the security descriptor group to Administrators. - if (!::SetSecurityDescriptorGroup(&sd, rgSidBA, FALSE)) - { - SrpExitWithLastError(hr, "Failed to set administrators group access for system restore."); - } - - // Set the discretionary access control list (DACL) to the ACL. - if (!::SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE)) - { - SrpExitWithLastError(hr, "Failed to set DACL for system restore."); - } - - // Note that an explicit security descriptor is being passed in. - hr= ::CoInitializeSecurity(&sd, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_DISABLE_AAA | EOAC_NO_CUSTOM_MARSHAL, NULL); - SrpExitOnFailure(hr, "Failed to initialize COM security for system restore."); - -LExit: - if (pAcl) - { - ::LocalFree(pAcl); - } - - return hr; -} diff --git a/src/dutil/strutil.cpp b/src/dutil/strutil.cpp deleted file mode 100644 index 550d6169..00000000 --- a/src/dutil/strutil.cpp +++ /dev/null @@ -1,2824 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define StrExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) -#define StrExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) -#define StrExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) -#define StrExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) -#define StrExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) -#define StrExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) -#define StrExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__) -#define StrExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__) -#define StrExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__) -#define StrExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__) -#define StrExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_STRUTIL, e, x, s, __VA_ARGS__) -#define StrExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_STRUTIL, g, x, s, __VA_ARGS__) - -#define ARRAY_GROWTH_SIZE 5 - -// Forward declarations. -static HRESULT AllocHelper( - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in SIZE_T cch, - __in BOOL fZeroOnRealloc - ); -static HRESULT AllocStringHelper( - __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in BOOL fZeroOnRealloc - ); -static HRESULT AllocConcatHelper( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in BOOL fZeroOnRealloc - ); -static HRESULT AllocFormattedArgsHelper( - __deref_out_z LPWSTR* ppwz, - __in BOOL fZeroOnRealloc, - __in __format_string LPCWSTR wzFormat, - __in va_list args - ); -static HRESULT StrAllocStringMapInvariant( - __deref_out_z LPWSTR* pscz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in DWORD dwMapFlags - ); - -/******************************************************************** -StrAlloc - allocates or reuses dynamic string memory - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAlloc( - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in SIZE_T cch - ) -{ - return AllocHelper(ppwz, cch, FALSE); -} - -/******************************************************************** -StrAllocSecure - allocates or reuses dynamic string memory -If the memory needs to reallocated, calls SecureZeroMemory on the -original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAllocSecure( - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in SIZE_T cch - ) -{ - return AllocHelper(ppwz, cch, TRUE); -} - -/******************************************************************** -AllocHelper - allocates or reuses dynamic string memory -If fZeroOnRealloc is true and the memory needs to reallocated, -calls SecureZeroMemory on original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -static HRESULT AllocHelper( - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in SIZE_T cch, - __in BOOL fZeroOnRealloc - ) -{ - Assert(ppwz && cch); - - HRESULT hr = S_OK; - LPWSTR pwz = NULL; - - if (cch >= MAXDWORD / sizeof(WCHAR)) - { - hr = E_OUTOFMEMORY; - StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); - } - - if (*ppwz) - { - if (fZeroOnRealloc) - { - LPVOID pvNew = NULL; - hr = MemReAllocSecure(*ppwz, sizeof(WCHAR)* cch, FALSE, &pvNew); - StrExitOnFailure(hr, "Failed to reallocate string"); - pwz = static_cast(pvNew); - } - else - { - pwz = static_cast(MemReAlloc(*ppwz, sizeof(WCHAR)* cch, FALSE)); - } - } - else - { - pwz = static_cast(MemAlloc(sizeof(WCHAR) * cch, TRUE)); - } - - StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); - - *ppwz = pwz; -LExit: - return hr; -} - - -/******************************************************************** -StrTrimCapacity - Frees any unnecessary memory associated with a string. - Purely used for optimization, generally only when a string - has been changing size, and will no longer grow. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -HRESULT DAPI StrTrimCapacity( - __deref_out_z LPWSTR* ppwz - ) -{ - Assert(ppwz); - - HRESULT hr = S_OK; - SIZE_T cchLen = 0; - - hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); - StrExitOnRootFailure(hr, "Failed to calculate length of string"); - - ++cchLen; // Add 1 for null-terminator - - hr = StrAlloc(ppwz, cchLen); - StrExitOnFailure(hr, "Failed to reallocate string"); - -LExit: - return hr; -} - - -/******************************************************************** -StrTrimWhitespace - allocates or reuses dynamic string memory and copies - in an existing string, excluding whitespace - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -HRESULT DAPI StrTrimWhitespace( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource - ) -{ - HRESULT hr = S_OK; - size_t i = 0; - LPWSTR sczResult = NULL; - - // Ignore beginning whitespace - while (L' ' == *wzSource || L'\t' == *wzSource) - { - wzSource++; - } - - hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &i); - StrExitOnRootFailure(hr, "Failed to get length of string"); - - // Overwrite ending whitespace with null characters - if (0 < i) - { - // start from the last non-null-terminator character in the array - for (i = i - 1; i > 0; --i) - { - if (L' ' != wzSource[i] && L'\t' != wzSource[i]) - { - break; - } - } - - ++i; - } - - hr = StrAllocString(&sczResult, wzSource, i); - StrExitOnFailure(hr, "Failed to copy result string"); - - // Output result - *ppwz = sczResult; - sczResult = NULL; - -LExit: - ReleaseStr(sczResult); - - return hr; -} - - -/******************************************************************** -StrAnsiAlloc - allocates or reuses dynamic ANSI string memory - -NOTE: caller is responsible for freeing ppsz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAnsiAlloc( - __deref_out_ecount_part(cch, 0) LPSTR* ppsz, - __in SIZE_T cch - ) -{ - Assert(ppsz && cch); - - HRESULT hr = S_OK; - LPSTR psz = NULL; - - if (cch >= MAXDWORD / sizeof(WCHAR)) - { - hr = E_OUTOFMEMORY; - StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); - } - - if (*ppsz) - { - psz = static_cast(MemReAlloc(*ppsz, sizeof(CHAR) * cch, FALSE)); - } - else - { - psz = static_cast(MemAlloc(sizeof(CHAR) * cch, TRUE)); - } - - StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); - - *ppsz = psz; -LExit: - return hr; -} - - -/******************************************************************** -StrAnsiTrimCapacity - Frees any unnecessary memory associated with a string. - Purely used for optimization, generally only when a string - has been changing size, and will no longer grow. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -HRESULT DAPI StrAnsiTrimCapacity( - __deref_out_z LPSTR* ppz - ) -{ - Assert(ppz); - - HRESULT hr = S_OK; - SIZE_T cchLen = 0; - -#pragma prefast(push) -#pragma prefast(disable:25068) - hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); -#pragma prefast(pop) - StrExitOnFailure(hr, "Failed to calculate length of string"); - - ++cchLen; // Add 1 for null-terminator - - hr = StrAnsiAlloc(ppz, cchLen); - StrExitOnFailure(hr, "Failed to reallocate string"); - -LExit: - return hr; -} - - -/******************************************************************** -StrAnsiTrimWhitespace - allocates or reuses dynamic string memory and copies - in an existing string, excluding whitespace - -NOTE: caller is responsible for freeing ppz even if function fails -********************************************************************/ -HRESULT DAPI StrAnsiTrimWhitespace( - __deref_out_z LPSTR* ppz, - __in_z LPCSTR szSource - ) -{ - HRESULT hr = S_OK; - size_t i = 0; - LPSTR sczResult = NULL; - - // Ignore beginning whitespace - while (' ' == *szSource || '\t' == *szSource) - { - szSource++; - } - - hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, &i); - StrExitOnRootFailure(hr, "Failed to get length of string"); - - // Overwrite ending whitespace with null characters - if (0 < i) - { - // start from the last non-null-terminator character in the array - for (i = i - 1; i > 0; --i) - { - if (L' ' != szSource[i] && L'\t' != szSource[i]) - { - break; - } - } - - ++i; - } - - hr = StrAnsiAllocStringAnsi(&sczResult, szSource, i); - StrExitOnFailure(hr, "Failed to copy result string"); - - // Output result - *ppz = sczResult; - sczResult = NULL; - -LExit: - ReleaseStr(sczResult); - - return hr; -} - -/******************************************************************** -StrAllocString - allocates or reuses dynamic string memory and copies in an existing string - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAllocString( - __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - return AllocStringHelper(ppwz, wzSource, cchSource, FALSE); -} - -/******************************************************************** -StrAllocStringSecure - allocates or reuses dynamic string memory and -copies in an existing string. If the memory needs to reallocated, -calls SecureZeroMemory on original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAllocStringSecure( - __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - return AllocStringHelper(ppwz, wzSource, cchSource, TRUE); -} - -/******************************************************************** -AllocStringHelper - allocates or reuses dynamic string memory and copies in an existing string -If fZeroOnRealloc is true and the memory needs to reallocated, -calls SecureZeroMemory on original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -static HRESULT AllocStringHelper( - __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in BOOL fZeroOnRealloc - ) -{ - Assert(ppwz && wzSource); // && *wzSource); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - - if (*ppwz) - { - cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(WCHAR); //convert the count in bytes to count in characters - } - - if (0 == cchSource && wzSource) - { - hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); - StrExitOnRootFailure(hr, "failed to get length of source string"); - } - - SIZE_T cchNeeded; - hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator - StrExitOnRootFailure(hr, "source string is too long"); - - if (cch < cchNeeded) - { - cch = cchNeeded; - hr = AllocHelper(ppwz, cch, fZeroOnRealloc); - StrExitOnFailure(hr, "failed to allocate string from string."); - } - - // copy everything (the NULL terminator will be included) - hr = ::StringCchCopyNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - -LExit: - return hr; -} - - -/******************************************************************** -StrAnsiAllocString - allocates or reuses dynamic ANSI string memory and copies in an existing string - -NOTE: caller is responsible for freeing ppsz even if function fails -NOTE: cchSource must equal the length of wzSource (not including the NULL terminator) -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAnsiAllocString( - __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in UINT uiCodepage - ) -{ - Assert(ppsz && wzSource); - - HRESULT hr = S_OK; - LPSTR psz = NULL; - SIZE_T cch = 0; - SIZE_T cchDest = cchSource; // at least enough - - if (*ppsz) - { - cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(CHAR); //convert the count in bytes to count in characters - } - - if (0 == cchSource) - { - cchDest = ::WideCharToMultiByte(uiCodepage, 0, wzSource, -1, NULL, 0, NULL, NULL); - if (0 == cchDest) - { - StrExitWithLastError(hr, "failed to get required size for conversion to ANSI: %ls", wzSource); - } - - --cchDest; // subtract one because WideChageToMultiByte includes space for the NULL terminator that we track below - } - else if (L'\0' == wzSource[cchSource - 1]) // if the source already had a null terminator, don't count that in the character count because we track it below - { - cchDest = cchSource - 1; - } - - if (cch < cchDest + 1) - { - cch = cchDest + 1; // add one for the NULL terminator - if (cch >= MAXDWORD / sizeof(WCHAR)) - { - hr = E_OUTOFMEMORY; - StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); - } - - if (*ppsz) - { - psz = static_cast(MemReAlloc(*ppsz, sizeof(CHAR) * cch, TRUE)); - } - else - { - psz = static_cast(MemAlloc(sizeof(CHAR) * cch, TRUE)); - } - StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); - - *ppsz = psz; - } - - if (0 == ::WideCharToMultiByte(uiCodepage, 0, wzSource, 0 == cchSource ? -1 : (int)cchSource, *ppsz, (int)cch, NULL, NULL)) - { - StrExitWithLastError(hr, "failed to convert to ansi: %ls", wzSource); - } - (*ppsz)[cchDest] = L'\0'; - -LExit: - return hr; -} - - -/******************************************************************** -StrAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing ANSI string - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource must equal the length of wzSource (not including the NULL terminator) -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAllocStringAnsi( - __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, - __in_z LPCSTR szSource, - __in SIZE_T cchSource, - __in UINT uiCodepage - ) -{ - Assert(ppwz && szSource); - - HRESULT hr = S_OK; - LPWSTR pwz = NULL; - SIZE_T cch = 0; - SIZE_T cchDest = cchSource; // at least enough - - if (*ppwz) - { - cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(WCHAR); //convert the count in bytes to count in characters - } - - if (0 == cchSource) - { - cchDest = ::MultiByteToWideChar(uiCodepage, 0, szSource, -1, NULL, 0); - if (0 == cchDest) - { - StrExitWithLastError(hr, "failed to get required size for conversion to unicode: %s", szSource); - } - - --cchDest; //subtract one because MultiByteToWideChar includes space for the NULL terminator that we track below - } - else if (L'\0' == szSource[cchSource - 1]) // if the source already had a null terminator, don't count that in the character count because we track it below - { - cchDest = cchSource - 1; - } - - if (cch < cchDest + 1) - { - cch = cchDest + 1; - if (cch >= MAXDWORD / sizeof(WCHAR)) - { - hr = E_OUTOFMEMORY; - StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); - } - - if (*ppwz) - { - pwz = static_cast(MemReAlloc(*ppwz, sizeof(WCHAR) * cch, TRUE)); - } - else - { - pwz = static_cast(MemAlloc(sizeof(WCHAR) * cch, TRUE)); - } - - StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); - - *ppwz = pwz; - } - - if (0 == ::MultiByteToWideChar(uiCodepage, 0, szSource, 0 == cchSource ? -1 : (int)cchSource, *ppwz, (int)cch)) - { - StrExitWithLastError(hr, "failed to convert to unicode: %s", szSource); - } - (*ppwz)[cchDest] = L'\0'; - -LExit: - return hr; -} - - -/******************************************************************** -StrAnsiAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing string - -NOTE: caller is responsible for freeing ppsz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -HRESULT DAPI StrAnsiAllocStringAnsi( - __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, - __in_z LPCSTR szSource, - __in SIZE_T cchSource - ) -{ - Assert(ppsz && szSource); // && *szSource); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - - if (*ppsz) - { - cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnRootFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(CHAR); //convert the count in bytes to count in characters - } - - if (0 == cchSource && szSource) - { - hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); - StrExitOnRootFailure(hr, "failed to get length of source string"); - } - - SIZE_T cchNeeded; - hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator - StrExitOnRootFailure(hr, "source string is too long"); - - if (cch < cchNeeded) - { - cch = cchNeeded; - hr = StrAnsiAlloc(ppsz, cch); - StrExitOnFailure(hr, "failed to allocate string from string."); - } - - // copy everything (the NULL terminator will be included) -#pragma prefast(push) -#pragma prefast(disable:25068) - hr = ::StringCchCopyNExA(*ppsz, cch, szSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); -#pragma prefast(pop) - -LExit: - return hr; -} - - -/******************************************************************** -StrAllocPrefix - allocates or reuses dynamic string memory and - prefixes a string - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchPrefix does not have to equal the length of wzPrefix -NOTE: if cchPrefix == 0, length of wzPrefix is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAllocPrefix( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzPrefix, - __in SIZE_T cchPrefix - ) -{ - Assert(ppwz && wzPrefix); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cchLen = 0; - - if (*ppwz) - { - cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(WCHAR); //convert the count in bytes to count in characters - - hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); - StrExitOnFailure(hr, "Failed to calculate length of string"); - } - - Assert(cchLen <= cch); - - if (0 == cchPrefix) - { - hr = ::StringCchLengthW(wzPrefix, STRSAFE_MAX_CCH, reinterpret_cast(&cchPrefix)); - StrExitOnFailure(hr, "Failed to calculate length of string"); - } - - if (cch - cchLen < cchPrefix + 1) - { - cch = cchPrefix + cchLen + 1; - hr = StrAlloc(ppwz, cch); - StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzPrefix); - } - - if (*ppwz) - { - SIZE_T cb = cch * sizeof(WCHAR); - SIZE_T cbPrefix = cchPrefix * sizeof(WCHAR); - - memmove(*ppwz + cchPrefix, *ppwz, cb - cbPrefix); - memcpy(*ppwz, wzPrefix, cbPrefix); - } - else - { - hr = E_UNEXPECTED; - StrExitOnFailure(hr, "for some reason our buffer is still null"); - } - -LExit: - return hr; -} - - -/******************************************************************** -StrAllocConcat - allocates or reuses dynamic string memory and adds an existing string - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAllocConcat( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - return AllocConcatHelper(ppwz, wzSource, cchSource, FALSE); -} - - -/******************************************************************** -StrAllocConcatSecure - allocates or reuses dynamic string memory and -adds an existing string. If the memory needs to reallocated, calls -SecureZeroMemory on the original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAllocConcatSecure( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - return AllocConcatHelper(ppwz, wzSource, cchSource, TRUE); -} - - -/******************************************************************** -AllocConcatHelper - allocates or reuses dynamic string memory and adds an existing string -If fZeroOnRealloc is true and the memory needs to reallocated, -calls SecureZeroMemory on original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -NOTE: cchSource does not have to equal the length of wzSource -NOTE: if cchSource == 0, length of wzSource is used instead -********************************************************************/ -static HRESULT AllocConcatHelper( - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in BOOL fZeroOnRealloc - ) -{ - Assert(ppwz && wzSource); // && *wzSource); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cchLen = 0; - - if (*ppwz) - { - cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(WCHAR); //convert the count in bytes to count in characters - - hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); - StrExitOnFailure(hr, "Failed to calculate length of string"); - } - - Assert(cchLen <= cch); - - if (0 == cchSource) - { - hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); - StrExitOnFailure(hr, "Failed to calculate length of string"); - } - - if (cch - cchLen < cchSource + 1) - { - cch = (cchSource + cchLen + 1) * 2; - hr = AllocHelper(ppwz, cch, fZeroOnRealloc); - StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzSource); - } - - if (*ppwz) - { - hr = ::StringCchCatNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - } - else - { - hr = E_UNEXPECTED; - StrExitOnFailure(hr, "for some reason our buffer is still null"); - } - -LExit: - return hr; -} - - -/******************************************************************** -StrAnsiAllocConcat - allocates or reuses dynamic string memory and adds an existing string - -NOTE: caller is responsible for freeing ppz even if function fails -NOTE: cchSource does not have to equal the length of pzSource -NOTE: if cchSource == 0, length of pzSource is used instead -********************************************************************/ -extern "C" HRESULT DAPI StrAnsiAllocConcat( - __deref_out_z LPSTR* ppz, - __in_z LPCSTR pzSource, - __in SIZE_T cchSource - ) -{ - Assert(ppz && pzSource); // && *pzSource); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - SIZE_T cchLen = 0; - - if (*ppz) - { - cch = MemSize(*ppz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(CHAR); // convert the count in bytes to count in characters - -#pragma prefast(push) -#pragma prefast(disable:25068) - hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); -#pragma prefast(pop) - StrExitOnFailure(hr, "Failed to calculate length of string"); - } - - Assert(cchLen <= cch); - - if (0 == cchSource) - { -#pragma prefast(push) -#pragma prefast(disable:25068) - hr = ::StringCchLengthA(pzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); -#pragma prefast(pop) - StrExitOnFailure(hr, "Failed to calculate length of string"); - } - - if (cch - cchLen < cchSource + 1) - { - cch = (cchSource + cchLen + 1) * 2; - hr = StrAnsiAlloc(ppz, cch); - StrExitOnFailure(hr, "failed to allocate string from string: %hs", pzSource); - } - - if (*ppz) - { -#pragma prefast(push) -#pragma prefast(disable:25068) - hr = ::StringCchCatNExA(*ppz, cch, pzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); -#pragma prefast(pop) - } - else - { - hr = E_UNEXPECTED; - StrExitOnFailure(hr, "for some reason our buffer is still null"); - } - -LExit: - return hr; -} - - -/******************************************************************** -StrAllocFormatted - allocates or reuses dynamic string memory and formats it - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT __cdecl StrAllocFormatted( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ) -{ - Assert(ppwz && wzFormat && *wzFormat); - - HRESULT hr = S_OK; - va_list args; - - va_start(args, wzFormat); - hr = StrAllocFormattedArgs(ppwz, wzFormat, args); - va_end(args); - - return hr; -} - - -/******************************************************************** -StrAllocConcatFormatted - allocates or reuses dynamic string memory -and adds a formatted string - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT __cdecl StrAllocConcatFormatted( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ) -{ - Assert(ppwz && wzFormat && *wzFormat); - - HRESULT hr = S_OK; - LPWSTR sczFormatted = NULL; - va_list args; - - va_start(args, wzFormat); - hr = StrAllocFormattedArgs(&sczFormatted, wzFormat, args); - va_end(args); - StrExitOnFailure(hr, "Failed to allocate formatted string"); - - hr = StrAllocConcat(ppwz, sczFormatted, 0); - -LExit: - ReleaseStr(sczFormatted); - - return hr; -} - - -/******************************************************************** -StrAllocConcatFormattedSecure - allocates or reuses dynamic string -memory and adds a formatted string. If the memory needs to be -reallocated, calls SecureZeroMemory on original block of memory after -it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT __cdecl StrAllocConcatFormattedSecure( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ) -{ - Assert(ppwz && wzFormat && *wzFormat); - - HRESULT hr = S_OK; - LPWSTR sczFormatted = NULL; - va_list args; - - va_start(args, wzFormat); - hr = StrAllocFormattedArgsSecure(&sczFormatted, wzFormat, args); - va_end(args); - StrExitOnFailure(hr, "Failed to allocate formatted string"); - - hr = StrAllocConcatSecure(ppwz, sczFormatted, 0); - -LExit: - ReleaseStr(sczFormatted); - - return hr; -} - - -/******************************************************************** -StrAllocFormattedSecure - allocates or reuses dynamic string memory -and formats it. If the memory needs to be reallocated, -calls SecureZeroMemory on original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT __cdecl StrAllocFormattedSecure( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ) -{ - Assert(ppwz && wzFormat && *wzFormat); - - HRESULT hr = S_OK; - va_list args; - - va_start(args, wzFormat); - hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args); - va_end(args); - - return hr; -} - - -/******************************************************************** -StrAnsiAllocFormatted - allocates or reuses dynamic ANSI string memory and formats it - -NOTE: caller is responsible for freeing ppsz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAnsiAllocFormatted( - __deref_out_z LPSTR* ppsz, - __in __format_string LPCSTR szFormat, - ... - ) -{ - Assert(ppsz && szFormat && *szFormat); - - HRESULT hr = S_OK; - va_list args; - - va_start(args, szFormat); - hr = StrAnsiAllocFormattedArgs(ppsz, szFormat, args); - va_end(args); - - return hr; -} - - -/******************************************************************** -StrAllocFormattedArgs - allocates or reuses dynamic string memory -and formats it with the passed in args - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAllocFormattedArgs( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - __in va_list args - ) -{ - return AllocFormattedArgsHelper(ppwz, FALSE, wzFormat, args); -} - - -/******************************************************************** -StrAllocFormattedArgsSecure - allocates or reuses dynamic string memory -and formats it with the passed in args. - -If the memory needs to reallocated, calls SecureZeroMemory on the -original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAllocFormattedArgsSecure( - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - __in va_list args - ) -{ - return AllocFormattedArgsHelper(ppwz, TRUE, wzFormat, args); -} - - -/******************************************************************** -AllocFormattedArgsHelper - allocates or reuses dynamic string memory -and formats it with the passed in args. - -If fZeroOnRealloc is true and the memory needs to reallocated, -calls SecureZeroMemory on original block of memory after it is moved. - -NOTE: caller is responsible for freeing ppwz even if function fails -********************************************************************/ -static HRESULT AllocFormattedArgsHelper( - __deref_out_z LPWSTR* ppwz, - __in BOOL fZeroOnRealloc, - __in __format_string LPCWSTR wzFormat, - __in va_list args - ) -{ - Assert(ppwz && wzFormat && *wzFormat); - - HRESULT hr = S_OK; - SIZE_T cch = 0; - LPWSTR pwzOriginal = NULL; - SIZE_T cbOriginal = 0; - size_t cchOriginal = 0; - - if (*ppwz) - { - cbOriginal = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cbOriginal) - { - hr = E_INVALIDARG; - StrExitOnRootFailure(hr, "failed to get size of destination string"); - } - - cch = cbOriginal / sizeof(WCHAR); //convert the count in bytes to count in characters - - hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, &cchOriginal); - StrExitOnRootFailure(hr, "failed to get length of original string"); - } - - if (0 == cch) // if there is no space in the string buffer - { - cch = 256; - - hr = AllocHelper(ppwz, cch, fZeroOnRealloc); - StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); - } - - // format the message (grow until it fits or there is a failure) - do - { - hr = ::StringCchVPrintfW(*ppwz, cch, wzFormat, args); - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - if (!pwzOriginal) - { - // this allows you to pass the original string as a formatting argument and not crash - // save the original string and free it after the printf is complete - pwzOriginal = *ppwz; - *ppwz = NULL; - - // StringCchVPrintfW starts writing to the string... - // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...); - pwzOriginal[cchOriginal] = 0; - } - - cch *= 2; - - hr = AllocHelper(ppwz, cch, fZeroOnRealloc); - StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); - - hr = S_FALSE; - } - } while (S_FALSE == hr); - StrExitOnRootFailure(hr, "failed to format string"); - -LExit: - if (pwzOriginal && fZeroOnRealloc) - { - SecureZeroMemory(pwzOriginal, cbOriginal); - } - - ReleaseStr(pwzOriginal); - - return hr; -} - - -/******************************************************************** -StrAnsiAllocFormattedArgs - allocates or reuses dynamic ANSI string memory -and formats it with the passed in args - -NOTE: caller is responsible for freeing ppsz even if function fails -********************************************************************/ -extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs( - __deref_out_z LPSTR* ppsz, - __in __format_string LPCSTR szFormat, - __in va_list args - ) -{ - Assert(ppsz && szFormat && *szFormat); - - HRESULT hr = S_OK; - SIZE_T cch = *ppsz ? MemSize(*ppsz) / sizeof(CHAR) : 0; - LPSTR pszOriginal = NULL; - size_t cchOriginal = 0; - - if (*ppsz) - { - cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnRootFailure(hr, "failed to get size of destination string"); - } - cch /= sizeof(CHAR); //convert the count in bytes to count in characters - - hr = ::StringCchLengthA(*ppsz, STRSAFE_MAX_CCH, &cchOriginal); - StrExitOnRootFailure(hr, "failed to get length of original string"); - } - - if (0 == cch) // if there is no space in the string buffer - { - cch = 256; - hr = StrAnsiAlloc(ppsz, cch); - StrExitOnFailure(hr, "failed to allocate string to format: %s", szFormat); - } - - // format the message (grow until it fits or there is a failure) - do - { -#pragma prefast(push) -#pragma prefast(disable:25068) // We intentionally don't use the unicode API here - hr = ::StringCchVPrintfA(*ppsz, cch, szFormat, args); -#pragma prefast(pop) - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - if (!pszOriginal) - { - // this allows you to pass the original string as a formatting argument and not crash - // save the original string and free it after the printf is complete - pszOriginal = *ppsz; - *ppsz = NULL; - // StringCchVPrintfW starts writing to the string... - // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...); - pszOriginal[cchOriginal] = 0; - } - cch *= 2; - hr = StrAnsiAlloc(ppsz, cch); - StrExitOnFailure(hr, "failed to allocate string to format: %hs", szFormat); - hr = S_FALSE; - } - } while (S_FALSE == hr); - StrExitOnRootFailure(hr, "failed to format string"); - -LExit: - ReleaseStr(pszOriginal); - - return hr; -} - - -/******************************************************************** -StrAllocFromError - returns the string for a particular error. - -********************************************************************/ -extern "C" HRESULT DAPI StrAllocFromError( - __inout LPWSTR *ppwzMessage, - __in HRESULT hrError, - __in_opt HMODULE hModule, - ... - ) -{ - HRESULT hr = S_OK; - DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM; - LPVOID pvMessage = NULL; - DWORD cchMessage = 0; - - if (hModule) - { - dwFlags |= FORMAT_MESSAGE_FROM_HMODULE; - } - - va_list args; - va_start(args, hModule); - cchMessage = ::FormatMessageW(dwFlags, static_cast(hModule), hrError, 0, reinterpret_cast(&pvMessage), 0, &args); - va_end(args); - - if (0 == cchMessage) - { - StrExitWithLastError(hr, "Failed to format message for error: 0x%x", hrError); - } - - hr = StrAllocString(ppwzMessage, reinterpret_cast(pvMessage), cchMessage); - StrExitOnFailure(hr, "Failed to allocate string for message."); - -LExit: - if (pvMessage) - { - ::LocalFree(pvMessage); - } - - return hr; -} - - -/******************************************************************** -StrMaxLength - returns maximum number of characters that can be stored in dynamic string p - -NOTE: assumes Unicode string -********************************************************************/ -extern "C" HRESULT DAPI StrMaxLength( - __in LPCVOID p, - __out SIZE_T* pcch - ) -{ - Assert(pcch); - - HRESULT hr = S_OK; - - if (p) - { - *pcch = MemSize(p); // get size of entire buffer - if (-1 == *pcch) - { - ExitFunction1(hr = E_FAIL); - } - - *pcch /= sizeof(WCHAR); // reduce to count of characters - } - else - { - *pcch = 0; - } - Assert(S_OK == hr); - -LExit: - return hr; -} - - -/******************************************************************** -StrSize - returns count of bytes in dynamic string p - -********************************************************************/ -extern "C" HRESULT DAPI StrSize( - __in LPCVOID p, - __out SIZE_T* pcb - ) -{ - Assert(p && pcb); - - HRESULT hr = S_OK; - - *pcb = MemSize(p); - if (-1 == *pcb) - { - hr = E_FAIL; - } - - return hr; -} - -/******************************************************************** -StrFree - releases dynamic string memory allocated by any StrAlloc*() functions - -********************************************************************/ -extern "C" HRESULT DAPI StrFree( - __in LPVOID p - ) -{ - Assert(p); - - HRESULT hr = MemFree(p); - StrExitOnFailure(hr, "failed to free string"); - -LExit: - return hr; -} - - -/**************************************************************************** -StrReplaceStringAll - Replaces wzOldSubString in ppOriginal with a wzNewSubString. -Replaces all instances. - -****************************************************************************/ -extern "C" HRESULT DAPI StrReplaceStringAll( - __inout LPWSTR* ppwzOriginal, - __in_z LPCWSTR wzOldSubString, - __in_z LPCWSTR wzNewSubString - ) -{ - HRESULT hr = S_OK; - DWORD dwStartIndex = 0; - - do - { - hr = StrReplaceString(ppwzOriginal, &dwStartIndex, wzOldSubString, wzNewSubString); - StrExitOnFailure(hr, "Failed to replace substring"); - } - while (S_OK == hr); - - hr = (0 == dwStartIndex) ? S_FALSE : S_OK; - -LExit: - return hr; -} - - -/**************************************************************************** -StrReplaceString - Replaces wzOldSubString in ppOriginal with a wzNewSubString. -Search for old substring starts at dwStartIndex. Does only 1 replace. - -****************************************************************************/ -extern "C" HRESULT DAPI StrReplaceString( - __inout LPWSTR* ppwzOriginal, - __inout DWORD* pdwStartIndex, - __in_z LPCWSTR wzOldSubString, - __in_z LPCWSTR wzNewSubString - ) -{ - Assert(ppwzOriginal && wzOldSubString && wzNewSubString); - - HRESULT hr = S_FALSE; - LPCWSTR wzSubLocation = NULL; - LPWSTR pwzBuffer = NULL; - size_t cchOldSubString = 0; - size_t cchNewSubString = 0; - - if (!*ppwzOriginal) - { - ExitFunction(); - } - - wzSubLocation = wcsstr(*ppwzOriginal + *pdwStartIndex, wzOldSubString); - if (!wzSubLocation) - { - ExitFunction(); - } - - if (wzOldSubString) - { - hr = ::StringCchLengthW(wzOldSubString, STRSAFE_MAX_CCH, &cchOldSubString); - StrExitOnRootFailure(hr, "Failed to get old string length."); - } - - if (wzNewSubString) - { - hr = ::StringCchLengthW(wzNewSubString, STRSAFE_MAX_CCH, &cchNewSubString); - StrExitOnRootFailure(hr, "Failed to get new string length."); - } - - hr = ::PtrdiffTToDWord(wzSubLocation - *ppwzOriginal, pdwStartIndex); - StrExitOnRootFailure(hr, "Failed to diff pointers."); - - hr = StrAllocString(&pwzBuffer, *ppwzOriginal, wzSubLocation - *ppwzOriginal); - StrExitOnFailure(hr, "Failed to duplicate string."); - - pwzBuffer[wzSubLocation - *ppwzOriginal] = '\0'; - - hr = StrAllocConcat(&pwzBuffer, wzNewSubString, 0); - StrExitOnFailure(hr, "Failed to append new string."); - - hr = StrAllocConcat(&pwzBuffer, wzSubLocation + cchOldSubString, 0); - StrExitOnFailure(hr, "Failed to append post string."); - - hr = StrFree(*ppwzOriginal); - StrExitOnFailure(hr, "Failed to free original string."); - - *ppwzOriginal = pwzBuffer; - *pdwStartIndex = *pdwStartIndex + static_cast(cchNewSubString); - hr = S_OK; - -LExit: - return hr; -} - - -static inline BYTE HexCharToByte( - __in WCHAR wc - ) -{ - Assert(L'0' <= wc && wc <= L'9' || L'a' <= wc && wc <= L'f' || L'A' <= wc && wc <= L'F'); // make sure wc is a hex character - - BYTE b; - if (L'0' <= wc && wc <= L'9') - { - b = (BYTE)(wc - L'0'); - } - else if ('a' <= wc && wc <= 'f') - { - b = (BYTE)(wc - L'0' - (L'a' - L'9' - 1)); - } - else // must be (L'A' <= wc && wc <= L'F') - { - b = (BYTE)(wc - L'0' - (L'A' - L'9' - 1)); - } - - Assert(0 <= b && b <= 15); - return b; -} - - -/**************************************************************************** -StrHexEncode - converts an array of bytes to a text string - -NOTE: wzDest must have space for cbSource * 2 + 1 characters -****************************************************************************/ -extern "C" HRESULT DAPI StrHexEncode( - __in_ecount(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __out_ecount(cchDest) LPWSTR wzDest, - __in SIZE_T cchDest - ) -{ - Assert(pbSource && wzDest); - - HRESULT hr = S_OK; - DWORD i; - BYTE b; - - if (cchDest < 2 * cbSource + 1) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); - } - - for (i = 0; i < cbSource; ++i) - { - b = (*pbSource) >> 4; - *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1)); - b = (*pbSource) & 0xF; - *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1)); - - ++pbSource; - } - - *wzDest = 0; - -LExit: - return hr; -} - - -/**************************************************************************** -StrAllocHexEncode - converts an array of bytes to an allocated text string - -****************************************************************************/ -HRESULT DAPI StrAllocHexEncode( - __in_ecount(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest - ) -{ - HRESULT hr = S_OK; - SIZE_T cchSource = sizeof(WCHAR) * (cbSource + 1); - - hr = StrAlloc(ppwzDest, cchSource); - StrExitOnFailure(hr, "Failed to allocate hex string."); - - hr = StrHexEncode(pbSource, cbSource, *ppwzDest, cchSource); - StrExitOnFailure(hr, "Failed to encode hex string."); - -LExit: - return hr; -} - - -/**************************************************************************** -StrHexDecode - converts a string of text to array of bytes - -NOTE: wzSource must contain even number of characters -****************************************************************************/ -extern "C" HRESULT DAPI StrHexDecode( - __in_z LPCWSTR wzSource, - __out_bcount(cbDest) BYTE* pbDest, - __in SIZE_T cbDest - ) -{ - Assert(wzSource && pbDest); - - HRESULT hr = S_OK; - size_t cchSource = 0; - size_t i = 0; - BYTE b = 0; - - hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource); - StrExitOnRootFailure(hr, "Failed to get length of hex string: %ls", wzSource); - - Assert(0 == cchSource % 2); - if (cbDest < cchSource / 2) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - StrExitOnRootFailure(hr, "Insufficient buffer to decode string '%ls' len: %Iu into %Iu bytes.", wzSource, cchSource, cbDest); - } - - for (i = 0; i < cchSource / 2; ++i) - { - b = HexCharToByte(*wzSource++); - (*pbDest) = b << 4; - - b = HexCharToByte(*wzSource++); - (*pbDest) |= b & 0xF; - - ++pbDest; - } - -LExit: - return hr; -} - - -/**************************************************************************** -StrAllocHexDecode - allocates a byte array hex-converted from string of text - -NOTE: wzSource must contain even number of characters -****************************************************************************/ -extern "C" HRESULT DAPI StrAllocHexDecode( - __in_z LPCWSTR wzSource, - __out_bcount(*pcbDest) BYTE** ppbDest, - __out_opt DWORD* pcbDest - ) -{ - Assert(wzSource && *wzSource && ppbDest); - - HRESULT hr = S_OK; - size_t cch = 0; - BYTE* pb = NULL; - DWORD cb = 0; - - hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cch); - StrExitOnFailure(hr, "Failed to calculate length of source string."); - - if (cch % 2) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "Invalid source parameter, string must be even length or it cannot be decoded."); - } - - cb = static_cast(cch / 2); - pb = static_cast(MemAlloc(cb, TRUE)); - StrExitOnNull(pb, hr, E_OUTOFMEMORY, "Failed to allocate memory for hex decode."); - - hr = StrHexDecode(wzSource, pb, cb); - StrExitOnFailure(hr, "Failed to decode source string."); - - *ppbDest = pb; - pb = NULL; - - if (pcbDest) - { - *pcbDest = cb; - } - -LExit: - ReleaseMem(pb); - - return hr; -} - - -/**************************************************************************** -Base85 encoding/decoding data - -****************************************************************************/ -const WCHAR Base85EncodeTable[] = L"!%'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~"; - -const BYTE Base85DecodeTable[256] = -{ - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 0, 85, 85, 85, 1, 85, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 85, 85, 85, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 85, 52, 53, 54, - 85, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85 -}; - -const UINT Base85PowerTable[4] = { 1, 85, 85*85, 85*85*85 }; - - -/**************************************************************************** -StrAllocBase85Encode - converts an array of bytes into an XML compatible string - -****************************************************************************/ -extern "C" HRESULT DAPI StrAllocBase85Encode( - __in_bcount_opt(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __deref_out_z LPWSTR* pwzDest - ) -{ - HRESULT hr = S_OK; - SIZE_T cchDest = 0; - LPWSTR wzDest; - DWORD_PTR iSource = 0; - DWORD_PTR iDest = 0; - - if (!pwzDest || !pbSource) - { - return E_INVALIDARG; - } - - // calc actual size of output - cchDest = cbSource / 4; - cchDest += cchDest * 4; - if (cbSource & 3) - { - cchDest += (cbSource & 3) + 1; - } - ++cchDest; // add room for null terminator - - hr = StrAlloc(pwzDest, cchDest); - StrExitOnFailure(hr, "failed to allocate destination string"); - - wzDest = *pwzDest; - - // first, encode full words - for (iSource = 0, iDest = 0; (iSource + 4 < cbSource) && (iDest + 5 < cchDest); iSource += 4, iDest += 5) - { - DWORD n = pbSource[iSource] + (pbSource[iSource + 1] << 8) + (pbSource[iSource + 2] << 16) + (pbSource[iSource + 3] << 24); - DWORD k = n / 85; - - //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); - wzDest[iDest] = Base85EncodeTable[n - k * 85]; - n = k / 85; - - //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable)); - wzDest[iDest + 1] = Base85EncodeTable[k - n * 85]; - k = n / 85; - - //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); - wzDest[iDest + 2] = Base85EncodeTable[n - k * 85]; - n = k / 85; - - //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable)); - wzDest[iDest + 3] = Base85EncodeTable[k - n * 85]; - - __assume(n <= DWORD_MAX / 85 / 85 / 85 / 85); - - //Assert(0 <= n && n < countof(Base85EncodeTable)); - wzDest[iDest + 4] = Base85EncodeTable[n]; - } - - // encode any remaining bytes - if (iSource < cbSource) - { - DWORD n = 0; - for (DWORD i = 0; iSource + i < cbSource; ++i) - { - n += pbSource[iSource + i] << (i << 3); - } - - for (/* iSource already initialized */; iSource < cbSource && iDest < cchDest; ++iSource, ++iDest) - { - DWORD k = n / 85; - - //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); - wzDest[iDest] = Base85EncodeTable[n - k * 85]; - - n = k; - } - - wzDest[iDest] = Base85EncodeTable[n]; - ++iDest; - } - Assert(iSource == cbSource); - Assert(iDest == cchDest - 1); - - wzDest[iDest] = L'\0'; - hr = S_OK; - -LExit: - return hr; -} - - -/**************************************************************************** -StrAllocBase85Decode - converts a string of text to array of bytes - -NOTE: Use MemFree() to release the allocated stream of bytes -****************************************************************************/ -extern "C" HRESULT DAPI StrAllocBase85Decode( - __in_z LPCWSTR wzSource, - __deref_out_bcount(*pcbDest) BYTE** ppbDest, - __out SIZE_T* pcbDest - ) -{ - HRESULT hr = S_OK; - size_t cchSource = 0; - DWORD_PTR i, n, k; - - BYTE* pbDest = 0; - SIZE_T cbDest = 0; - - if (!wzSource || !ppbDest || !pcbDest) - { - ExitFunction1(hr = E_INVALIDARG); - } - - hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource); - StrExitOnRootFailure(hr, "failed to get length of base 85 string: %ls", wzSource); - - // evaluate size of output and check it - k = cchSource / 5; - cbDest = k << 2; - k = cchSource - k * 5; - if (k) - { - if (1 == k) - { - // decode error -- encoded size cannot equal 1 mod 5 - return E_UNEXPECTED; - } - - cbDest += k - 1; - } - - *ppbDest = static_cast(MemAlloc(cbDest, FALSE)); - StrExitOnNull(*ppbDest, hr, E_OUTOFMEMORY, "failed allocate memory to decode the string"); - - pbDest = *ppbDest; - *pcbDest = cbDest; - - // decode full words first - while (5 <= cchSource) - { - k = Base85DecodeTable[wzSource[0]]; - if (85 == k) - { - // illegal symbol - return E_UNEXPECTED; - } - n = k; - - k = Base85DecodeTable[wzSource[1]]; - if (85 == k) - { - // illegal symbol - return E_UNEXPECTED; - } - n += k * 85; - - k = Base85DecodeTable[wzSource[2]]; - if (85 == k) - { - // illegal symbol - return E_UNEXPECTED; - } - n += k * (85 * 85); - - k = Base85DecodeTable[wzSource[3]]; - if (85 == k) - { - // illegal symbol - return E_UNEXPECTED; - } - n += k * (85 * 85 * 85); - - k = Base85DecodeTable[wzSource[4]]; - if (85 == k) - { - // illegal symbol - return E_UNEXPECTED; - } - k *= (85 * 85 * 85 * 85); - - // if (k + n > (1u << 32)) <=> (k > ~n) then decode error - if (k > ~n) - { - // overflow - return E_UNEXPECTED; - } - - n += k; - - pbDest[0] = (BYTE) n; - pbDest[1] = (BYTE) (n >> 8); - pbDest[2] = (BYTE) (n >> 16); - pbDest[3] = (BYTE) (n >> 24); - - wzSource += 5; - pbDest += 4; - cchSource -= 5; - } - - if (cchSource) - { - n = 0; - for (i = 0; i < cchSource; ++i) - { - k = Base85DecodeTable[wzSource[i]]; - if (85 == k) - { - // illegal symbol - return E_UNEXPECTED; - } - - n += k * Base85PowerTable[i]; - } - - for (i = 1; i < cchSource; ++i) - { - *pbDest++ = (BYTE)n; - n >>= 8; - } - - if (0 != n) - { - // decode error - return E_UNEXPECTED; - } - } - - hr = S_OK; - -LExit: - return hr; -} - - -/**************************************************************************** -MultiSzLen - calculates the length of a MULTISZ string including all nulls -including the double null terminator at the end of the MULTISZ. - -NOTE: returns 0 if the multisz in not properly terminated with two nulls -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzLen( - __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz, - __out SIZE_T* pcch -) -{ - Assert(pcch); - - HRESULT hr = S_OK; - LPCWSTR wz = pwzMultiSz; - DWORD_PTR dwMaxSize = 0; - - hr = StrMaxLength(pwzMultiSz, &dwMaxSize); - StrExitOnFailure(hr, "failed to get the max size of a string while calculating MULTISZ length"); - - *pcch = 0; - while (*pcch < dwMaxSize) - { - if (L'\0' == *wz && L'\0' == *(wz + 1)) - { - break; - } - - ++wz; - *pcch = *pcch + 1; - } - - // Add two for the last 2 NULLs (that we looked ahead at) - *pcch = *pcch + 2; - - // If we've walked off the end then the length is 0 - if (*pcch > dwMaxSize) - { - *pcch = 0; - } - -LExit: - return hr; -} - - -/**************************************************************************** -MultiSzPrepend - prepends a string onto the front of a MUTLISZ - -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzPrepend( - __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz, - __inout_opt SIZE_T* pcchMultiSz, - __in __nullnullterminated LPCWSTR pwzInsert - ) -{ - Assert(ppwzMultiSz && pwzInsert && *pwzInsert); - - HRESULT hr =S_OK; - LPWSTR pwzResult = NULL; - SIZE_T cchResult = 0; - SIZE_T cchInsert = 0; - SIZE_T cchMultiSz = 0; - - // Get the lengths of the MULTISZ (and prime it if it's not initialized) - if (pcchMultiSz && 0 != *pcchMultiSz) - { - cchMultiSz = *pcchMultiSz; - } - else - { - hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); - StrExitOnFailure(hr, "failed to get length of multisz"); - } - - hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast(&cchInsert)); - StrExitOnRootFailure(hr, "failed to get length of insert string"); - - cchResult = cchInsert + cchMultiSz + 1; - - // Allocate the result buffer - hr = StrAlloc(&pwzResult, cchResult + 1); - StrExitOnFailure(hr, "failed to allocate result string"); - - // Prepend - hr = ::StringCchCopyW(pwzResult, cchResult, pwzInsert); - StrExitOnRootFailure(hr, "failed to copy prepend string: %ls", pwzInsert); - - // If there was no MULTISZ, double null terminate our result, otherwise, copy the MULTISZ in - if (0 == cchMultiSz) - { - pwzResult[cchResult] = L'\0'; - ++cchResult; - } - else - { - // Copy the rest - ::CopyMemory(pwzResult + cchInsert + 1, *ppwzMultiSz, cchMultiSz * sizeof(WCHAR)); - - // Free the old buffer - ReleaseNullStr(*ppwzMultiSz); - } - - // Set the result - *ppwzMultiSz = pwzResult; - - if (pcchMultiSz) - { - *pcchMultiSz = cchResult; - } - - pwzResult = NULL; - -LExit: - ReleaseNullStr(pwzResult); - - return hr; -} - -/**************************************************************************** -MultiSzFindSubstring - case insensitive find of a string in a MULTISZ that contains the -specified sub string and returns the index of the -string in the MULTISZ, the address, neither, or both - -NOTE: returns S_FALSE if the string is not found -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzFindSubstring( - __in __nullnullterminated LPCWSTR pwzMultiSz, - __in __nullnullterminated LPCWSTR pwzSubstring, - __out_opt DWORD_PTR* pdwIndex, - __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn - ) -{ - Assert(pwzMultiSz && *pwzMultiSz && pwzSubstring && *pwzSubstring); - - HRESULT hr = S_FALSE; // Assume we won't find it (the glass is half empty) - LPCWSTR wz = pwzMultiSz; - DWORD_PTR dwIndex = 0; - SIZE_T cchMultiSz = 0; - SIZE_T cchProgress = 0; - - hr = MultiSzLen(pwzMultiSz, &cchMultiSz); - StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); - - // Find the string containing the sub string - hr = S_OK; - while (NULL == wcsistr(wz, pwzSubstring)) - { - // Slide through to the end of the current string - while (L'\0' != *wz && cchProgress < cchMultiSz) - { - ++wz; - ++cchProgress; - } - - // If we're done, we're done - if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz) - { - hr = S_FALSE; - break; - } - - // Move on to the next string - ++wz; - ++dwIndex; - } - Assert(S_OK == hr || S_FALSE == hr); - - // If we found it give them what they want - if (S_OK == hr) - { - if (pdwIndex) - { - *pdwIndex = dwIndex; - } - - if (ppwzFoundIn) - { - *ppwzFoundIn = wz; - } - } - -LExit: - return hr; -} - -/**************************************************************************** -MultiSzFindString - finds a string in a MULTISZ and returns the index of -the string in the MULTISZ, the address or both - -NOTE: returns S_FALSE if the string is not found -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzFindString( - __in __nullnullterminated LPCWSTR pwzMultiSz, - __in __nullnullterminated LPCWSTR pwzString, - __out_opt DWORD_PTR* pdwIndex, - __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound - ) -{ - Assert(pwzMultiSz && *pwzMultiSz && pwzString && *pwzString && (pdwIndex || ppwzFound)); - - HRESULT hr = S_FALSE; // Assume we won't find it - LPCWSTR wz = pwzMultiSz; - DWORD_PTR dwIndex = 0; - SIZE_T cchMutliSz = 0; - SIZE_T cchProgress = 0; - - hr = MultiSzLen(pwzMultiSz, &cchMutliSz); - StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); - - // Find the string - hr = S_OK; - while (0 != lstrcmpW(wz, pwzString)) - { - // Slide through to the end of the current string - while (L'\0' != *wz && cchProgress < cchMutliSz) - { - ++wz; - ++cchProgress; - } - - // If we're done, we're done - if (L'\0' == *(wz + 1) || cchProgress >= cchMutliSz) - { - hr = S_FALSE; - break; - } - - // Move on to the next string - ++wz; - ++dwIndex; - } - Assert(S_OK == hr || S_FALSE == hr); - - // If we found it give them what they want - if (S_OK == hr) - { - if (pdwIndex) - { - *pdwIndex = dwIndex; - } - - if (ppwzFound) - { - *ppwzFound = wz; - } - } - -LExit: - return hr; -} - -/**************************************************************************** -MultiSzRemoveString - removes string from a MULTISZ at the specified -index - -NOTE: does an in place removal without shrinking the memory allocation - -NOTE: returns S_FALSE if the MULTISZ has fewer strings than dwIndex -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzRemoveString( - __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, - __in DWORD_PTR dwIndex - ) -{ - Assert(ppwzMultiSz && *ppwzMultiSz); - - HRESULT hr = S_OK; - LPCWSTR wz = *ppwzMultiSz; - LPCWSTR wzNext = NULL; - DWORD_PTR dwCurrentIndex = 0; - SIZE_T cchMultiSz = 0; - SIZE_T cchProgress = 0; - - hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); - StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); - - // Find the index we want to remove - hr = S_OK; - while (dwCurrentIndex < dwIndex) - { - // Slide through to the end of the current string - while (L'\0' != *wz && cchProgress < cchMultiSz) - { - ++wz; - ++cchProgress; - } - - // If we're done, we're done - if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz) - { - hr = S_FALSE; - break; - } - - // Move on to the next string - ++wz; - ++cchProgress; - ++dwCurrentIndex; - } - Assert(S_OK == hr || S_FALSE == hr); - - // If we found the index to be removed - if (S_OK == hr) - { - wzNext = wz; - - // Slide through to the end of the current string - while (L'\0' != *wzNext && cchProgress < cchMultiSz) - { - ++wzNext; - ++cchProgress; - } - - // Something weird has happened if we're past the end of the MULTISZ - if (cchProgress > cchMultiSz) - { - hr = E_UNEXPECTED; - StrExitOnFailure(hr, "failed to move past the string to be removed from MULTISZ"); - } - - // Move on to the next character - ++wzNext; - ++cchProgress; - - ::MoveMemory((LPVOID)wz, (LPVOID)wzNext, (cchMultiSz - cchProgress) * sizeof(WCHAR)); - } - -LExit: - return hr; -} - -/**************************************************************************** -MultiSzInsertString - inserts new string at the specified index - -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzInsertString( - __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, - __inout_opt SIZE_T* pcchMultiSz, - __in DWORD_PTR dwIndex, - __in_z LPCWSTR pwzInsert - ) -{ - Assert(ppwzMultiSz && pwzInsert && *pwzInsert); - - HRESULT hr = S_OK; - LPCWSTR wz = *ppwzMultiSz; - DWORD_PTR dwCurrentIndex = 0; - SIZE_T cchProgress = 0; - LPWSTR pwzResult = NULL; - SIZE_T cchResult = 0; - SIZE_T cchString = 0; - SIZE_T cchMultiSz = 0; - - hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast(&cchString)); - StrExitOnRootFailure(hr, "failed to get length of insert string"); - - if (pcchMultiSz && 0 != *pcchMultiSz) - { - cchMultiSz = *pcchMultiSz; - } - else - { - hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); - StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); - } - - // Find the index we want to insert at - hr = S_OK; - while (dwCurrentIndex < dwIndex) - { - // Slide through to the end of the current string - while (L'\0' != *wz && cchProgress < cchMultiSz) - { - ++wz; - ++cchProgress; - } - - // If we're done, we're done - if ((dwCurrentIndex + 1 != dwIndex && L'\0' == *(wz + 1)) || cchProgress >= cchMultiSz) - { - hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND); - StrExitOnRootFailure(hr, "requested to insert into an invalid index: %u in a MULTISZ", dwIndex); - } - - // Move on to the next string - ++wz; - ++cchProgress; - ++dwCurrentIndex; - } - - // - // Insert the string - // - cchResult = cchMultiSz + cchString + 1; - - hr = StrAlloc(&pwzResult, cchResult); - StrExitOnFailure(hr, "failed to allocate result string for MULTISZ insert"); - - // Copy the part before the insert - ::CopyMemory(pwzResult, *ppwzMultiSz, cchProgress * sizeof(WCHAR)); - - // Copy the insert part - ::CopyMemory(pwzResult + cchProgress, pwzInsert, (cchString + 1) * sizeof(WCHAR)); - - // Copy the part after the insert - ::CopyMemory(pwzResult + cchProgress + cchString + 1, wz, (cchMultiSz - cchProgress) * sizeof(WCHAR)); - - // Free the old buffer - ReleaseNullStr(*ppwzMultiSz); - - // Set the result - *ppwzMultiSz = pwzResult; - - // If they wanted the resulting length, let 'em have it - if (pcchMultiSz) - { - *pcchMultiSz = cchResult; - } - - pwzResult = NULL; - -LExit: - ReleaseStr(pwzResult); - - return hr; -} - -/**************************************************************************** -MultiSzReplaceString - replaces string at the specified index with a new one - -****************************************************************************/ -extern "C" HRESULT DAPI MultiSzReplaceString( - __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, - __in DWORD_PTR dwIndex, - __in_z LPCWSTR pwzString - ) -{ - Assert(ppwzMultiSz && pwzString && *pwzString); - - HRESULT hr = S_OK; - - hr = MultiSzRemoveString(ppwzMultiSz, dwIndex); - StrExitOnFailure(hr, "failed to remove string from MULTISZ at the specified index: %u", dwIndex); - - hr = MultiSzInsertString(ppwzMultiSz, NULL, dwIndex, pwzString); - StrExitOnFailure(hr, "failed to insert string into MULTISZ at the specified index: %u", dwIndex); - -LExit: - return hr; -} - - -/**************************************************************************** -wcsistr - case insensitive find a substring - -****************************************************************************/ -extern "C" LPCWSTR DAPI wcsistr( - __in_z LPCWSTR wzString, - __in_z LPCWSTR wzCharSet - ) -{ - LPCWSTR wzSource = wzString; - LPCWSTR wzSearch = NULL; - SIZE_T cchSourceIndex = 0; - - // Walk through wzString (the source string) one character at a time - while (*wzSource) - { - cchSourceIndex = 0; - wzSearch = wzCharSet; - - // Look ahead in the source string until we get a full match or we hit the end of the source - while (L'\0' != wzSource[cchSourceIndex] && L'\0' != *wzSearch && towlower(wzSource[cchSourceIndex]) == towlower(*wzSearch)) - { - ++cchSourceIndex; - ++wzSearch; - } - - // If we found it, return the point that we found it at - if (L'\0' == *wzSearch) - { - return wzSource; - } - - // Walk ahead one character - ++wzSource; - } - - return NULL; -} - -/**************************************************************************** -StrStringToInt16 - converts a string to a signed 16-bit integer. - -****************************************************************************/ -extern "C" HRESULT DAPI StrStringToInt16( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out SHORT* psOut - ) -{ - HRESULT hr = S_OK; - LONGLONG ll = 0; - - hr = StrStringToInt64(wzIn, cchIn, &ll); - StrExitOnFailure(hr, "Failed to parse int64."); - - if (SHORT_MAX < ll || SHORT_MIN > ll) - { - ExitFunction1(hr = DISP_E_OVERFLOW); - } - *psOut = (SHORT)ll; - -LExit: - return hr; -} - -/**************************************************************************** -StrStringToUInt16 - converts a string to an unsigned 16-bit integer. - -****************************************************************************/ -extern "C" HRESULT DAPI StrStringToUInt16( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out USHORT* pusOut - ) -{ - HRESULT hr = S_OK; - ULONGLONG ull = 0; - - hr = StrStringToUInt64(wzIn, cchIn, &ull); - StrExitOnFailure(hr, "Failed to parse uint64."); - - if (USHORT_MAX < ull) - { - ExitFunction1(hr = DISP_E_OVERFLOW); - } - *pusOut = (USHORT)ull; - -LExit: - return hr; -} - -/**************************************************************************** -StrStringToInt32 - converts a string to a signed 32-bit integer. - -****************************************************************************/ -extern "C" HRESULT DAPI StrStringToInt32( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out INT* piOut - ) -{ - HRESULT hr = S_OK; - LONGLONG ll = 0; - - hr = StrStringToInt64(wzIn, cchIn, &ll); - StrExitOnFailure(hr, "Failed to parse int64."); - - if (INT_MAX < ll || INT_MIN > ll) - { - ExitFunction1(hr = DISP_E_OVERFLOW); - } - *piOut = (INT)ll; - -LExit: - return hr; -} - -/**************************************************************************** -StrStringToUInt32 - converts a string to an unsigned 32-bit integer. - -****************************************************************************/ -extern "C" HRESULT DAPI StrStringToUInt32( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out UINT* puiOut - ) -{ - HRESULT hr = S_OK; - ULONGLONG ull = 0; - - hr = StrStringToUInt64(wzIn, cchIn, &ull); - StrExitOnFailure(hr, "Failed to parse uint64."); - - if (UINT_MAX < ull) - { - ExitFunction1(hr = DISP_E_OVERFLOW); - } - *puiOut = (UINT)ull; - -LExit: - return hr; -} - -/**************************************************************************** -StrStringToInt64 - converts a string to a signed 64-bit integer. - -****************************************************************************/ -extern "C" HRESULT DAPI StrStringToInt64( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out LONGLONG* pllOut - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - INT iSign = 1; - INT nDigit = 0; - LARGE_INTEGER liValue = { }; - size_t cchString = 0; - - // get string length if not provided - if (0 >= cchIn) - { - hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString); - StrExitOnRootFailure(hr, "Failed to get length of string."); - - cchIn = (DWORD)cchString; - if (0 >= cchIn) - { - ExitFunction1(hr = E_INVALIDARG); - } - } - - // check sign - if (L'-' == wzIn[0]) - { - if (1 >= cchIn) - { - ExitFunction1(hr = E_INVALIDARG); - } - i = 1; - iSign = -1; - } - - // read digits - while (i < cchIn) - { - nDigit = wzIn[i] - L'0'; - if (0 > nDigit || 9 < nDigit) - { - ExitFunction1(hr = E_INVALIDARG); - } - liValue.QuadPart = liValue.QuadPart * 10 + nDigit * iSign; - - if ((liValue.HighPart ^ iSign) & INT_MIN) - { - ExitFunction1(hr = DISP_E_OVERFLOW); - } - ++i; - } - - *pllOut = liValue.QuadPart; - -LExit: - return hr; -} - -/**************************************************************************** -StrStringToUInt64 - converts a string to an unsigned 64-bit integer. - -****************************************************************************/ -extern "C" HRESULT DAPI StrStringToUInt64( - __in_z LPCWSTR wzIn, - __in DWORD cchIn, - __out ULONGLONG* pullOut - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - DWORD nDigit = 0; - ULONGLONG ullValue = 0; - ULONGLONG ull = 0; - size_t cchString = 0; - - // get string length if not provided - if (0 >= cchIn) - { - hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString); - StrExitOnRootFailure(hr, "Failed to get length of string."); - - cchIn = (DWORD)cchString; - if (0 >= cchIn) - { - ExitFunction1(hr = E_INVALIDARG); - } - } - - // read digits - while (i < cchIn) - { - nDigit = wzIn[i] - L'0'; - if (9 < nDigit) - { - ExitFunction1(hr = E_INVALIDARG); - } - ull = (ULONGLONG)(ullValue * 10 + nDigit); - - if (ull < ullValue) - { - ExitFunction1(hr = DISP_E_OVERFLOW); - } - ullValue = ull; - ++i; - } - - *pullOut = ullValue; - -LExit: - return hr; -} - -/**************************************************************************** -StrStringToUpper - alters the given string in-place to be entirely uppercase - -****************************************************************************/ -void DAPI StrStringToUpper( - __inout_z LPWSTR wzIn - ) -{ - ::CharUpperBuffW(wzIn, lstrlenW(wzIn)); -} - -/**************************************************************************** -StrStringToLower - alters the given string in-place to be entirely lowercase - -****************************************************************************/ -void DAPI StrStringToLower( - __inout_z LPWSTR wzIn - ) -{ - ::CharLowerBuffW(wzIn, lstrlenW(wzIn)); -} - -/**************************************************************************** -StrAllocStringToUpperInvariant - creates an upper-case copy of a string. - -****************************************************************************/ -extern "C" HRESULT DAPI StrAllocStringToUpperInvariant( - __deref_out_z LPWSTR* pscz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_UPPERCASE); -} - -/**************************************************************************** -StrAllocStringToLowerInvariant - creates an lower-case copy of a string. - -****************************************************************************/ -extern "C" HRESULT DAPI StrAllocStringToLowerInvariant( - __deref_out_z LPWSTR* pscz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_LOWERCASE); -} - -/**************************************************************************** -StrArrayAllocString - Allocates a string array. - -****************************************************************************/ -extern "C" HRESULT DAPI StrArrayAllocString( - __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, - __inout LPUINT pcStrArray, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource - ) -{ - HRESULT hr = S_OK; - UINT cNewStrArray; - - hr = ::UIntAdd(*pcStrArray, 1, &cNewStrArray); - StrExitOnFailure(hr, "Failed to increment the string array element count."); - - hr = MemEnsureArraySize(reinterpret_cast(prgsczStrArray), cNewStrArray, sizeof(LPWSTR), ARRAY_GROWTH_SIZE); - StrExitOnFailure(hr, "Failed to allocate memory for the string array."); - - hr = StrAllocString(&(*prgsczStrArray)[*pcStrArray], wzSource, cchSource); - StrExitOnFailure(hr, "Failed to allocate and assign the string."); - - *pcStrArray = cNewStrArray; - -LExit: - return hr; -} - -/**************************************************************************** -StrArrayFree - Frees a string array. - -Use ReleaseNullStrArray to nullify the arguments. - -****************************************************************************/ -extern "C" HRESULT DAPI StrArrayFree( - __in_ecount(cStrArray) LPWSTR *rgsczStrArray, - __in UINT cStrArray - ) -{ - HRESULT hr = S_OK; - - for (UINT i = 0; i < cStrArray; ++i) - { - if (NULL != rgsczStrArray[i]) - { - hr = StrFree(rgsczStrArray[i]); - StrExitOnFailure(hr, "Failed to free the string at index %u.", i); - } - } - - hr = MemFree(rgsczStrArray); - StrExitOnFailure(hr, "Failed to free memory for the string array."); - -LExit: - return hr; -} - -/**************************************************************************** -StrSplitAllocArray - Splits a string into an array. - -****************************************************************************/ -extern "C" HRESULT DAPI StrSplitAllocArray( - __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, - __inout LPUINT pcStrArray, - __in_z LPCWSTR wzSource, - __in_z LPCWSTR wzDelim - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCopy = NULL; - LPWSTR wzContext = NULL; - - // Copy wzSource so it is not modified. - hr = StrAllocString(&sczCopy, wzSource, 0); - StrExitOnFailure(hr, "Failed to copy the source string."); - - for (LPCWSTR wzToken = ::wcstok_s(sczCopy, wzDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, wzDelim, &wzContext)) - { - hr = StrArrayAllocString(prgsczStrArray, pcStrArray, wzToken, 0); - StrExitOnFailure(hr, "Failed to add the string to the string array."); - } - -LExit: - ReleaseStr(sczCopy); - - return hr; -} - -/**************************************************************************** -StrAllocStringMapInvariant - helper function for the ToUpper and ToLower. - -Note: Assumes source and destination buffers will be the same. -****************************************************************************/ -static HRESULT StrAllocStringMapInvariant( - __deref_out_z LPWSTR* pscz, - __in_z LPCWSTR wzSource, - __in SIZE_T cchSource, - __in DWORD dwMapFlags - ) -{ - HRESULT hr = S_OK; - - hr = StrAllocString(pscz, wzSource, cchSource); - StrExitOnFailure(hr, "Failed to allocate a copy of the source string."); - - if (0 == cchSource) - { - // Need the actual string size for LCMapString. This includes the null-terminator - // but LCMapString doesn't care either way. - hr = ::StringCchLengthW(*pscz, INT_MAX, reinterpret_cast(&cchSource)); - StrExitOnRootFailure(hr, "Failed to get the length of the string."); - } - else if (INT_MAX < cchSource) - { - StrExitOnRootFailure(hr = E_INVALIDARG, "Source string is too long: %Iu", cchSource); - } - - // Convert the copy of the string to upper or lower case in-place. - if (0 == ::LCMapStringW(LOCALE_INVARIANT, dwMapFlags, *pscz, static_cast(cchSource), *pscz, static_cast(cchSource))) - { - StrExitWithLastError(hr, "Failed to convert the string case."); - } - -LExit: - return hr; -} - -/**************************************************************************** -StrSecureZeroString - zeroes out string to the make sure the contents -don't remain in memory. - -****************************************************************************/ -extern "C" DAPI_(HRESULT) StrSecureZeroString( - __in LPWSTR pwz - ) -{ - HRESULT hr = S_OK; - SIZE_T cch; - - if (pwz) - { - cch = MemSize(pwz); - if (-1 == cch) - { - hr = E_INVALIDARG; - StrExitOnFailure(hr, "Failed to get size of string"); - } - else - { - SecureZeroMemory(pwz, cch); - } - } - -LExit: - return hr; -} - -/**************************************************************************** -StrSecureZeroFreeString - zeroes out string to the make sure the contents -don't remain in memory, then frees the string. - -****************************************************************************/ -extern "C" DAPI_(HRESULT) StrSecureZeroFreeString( - __in LPWSTR pwz - ) -{ - HRESULT hr = S_OK; - - hr = StrSecureZeroString(pwz); - ReleaseStr(pwz); - - return hr; -} diff --git a/src/dutil/svcutil.cpp b/src/dutil/svcutil.cpp deleted file mode 100644 index 1a39bfee..00000000 --- a/src/dutil/svcutil.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define SvcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) -#define SvcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) -#define SvcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) -#define SvcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) -#define SvcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) -#define SvcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) -#define SvcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SVCUTIL, p, x, e, s, __VA_ARGS__) -#define SvcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, p, x, s, __VA_ARGS__) -#define SvcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SVCUTIL, p, x, e, s, __VA_ARGS__) -#define SvcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, p, x, s, __VA_ARGS__) -#define SvcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SVCUTIL, e, x, s, __VA_ARGS__) -#define SvcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SVCUTIL, g, x, s, __VA_ARGS__) - -/******************************************************************** -SvcQueryConfig - queries the configuration of a service - -********************************************************************/ -extern "C" HRESULT DAPI SvcQueryConfig( - __in SC_HANDLE sch, - __out QUERY_SERVICE_CONFIGW** ppConfig - ) -{ - HRESULT hr = S_OK; - QUERY_SERVICE_CONFIGW* pConfig = NULL; - DWORD cbConfig = 0; - - if (!::QueryServiceConfigW(sch, NULL, 0, &cbConfig)) - { - DWORD er = ::GetLastError(); - if (ERROR_INSUFFICIENT_BUFFER == er) - { - pConfig = static_cast(MemAlloc(cbConfig, TRUE)); - SvcExitOnNull(pConfig, hr, E_OUTOFMEMORY, "Failed to allocate memory to get configuration."); - - if (!::QueryServiceConfigW(sch, pConfig, cbConfig, &cbConfig)) - { - SvcExitWithLastError(hr, "Failed to read service configuration."); - } - } - else - { - SvcExitOnWin32Error(er, hr, "Failed to query service configuration."); - } - } - - *ppConfig = pConfig; - pConfig = NULL; - -LExit: - ReleaseMem(pConfig); - - return hr; -} diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp deleted file mode 100644 index d200a0ea..00000000 --- a/src/dutil/thmutil.cpp +++ /dev/null @@ -1,5709 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define ThmExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) -#define ThmExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) -#define ThmExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) -#define ThmExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) -#define ThmExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) -#define ThmExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) -#define ThmExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__) -#define ThmExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__) -#define ThmExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__) -#define ThmExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__) -#define ThmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_THMUTIL, e, x, s, __VA_ARGS__) -#define ThmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_THMUTIL, g, x, s, __VA_ARGS__) - -// from CommCtrl.h -#ifndef BS_COMMANDLINK -#define BS_COMMANDLINK 0x0000000EL -#endif - -#ifndef BCM_SETNOTE -#define BCM_SETNOTE (BCM_FIRST + 0x0009) -#endif - -#ifndef BCM_SETSHIELD -#define BCM_SETSHIELD (BCM_FIRST + 0x000C) -#endif - -#ifndef LWS_NOPREFIX -#define LWS_NOPREFIX 0x0004 -#endif - -const DWORD THEME_INVALID_ID = 0xFFFFFFFF; -const COLORREF THEME_INVISIBLE_COLORREF = 0xFFFFFFFF; -const DWORD GROW_FONT_INSTANCES = 3; -const DWORD GROW_WINDOW_TEXT = 250; -const LPCWSTR THEME_WC_HYPERLINK = L"ThemeHyperLink"; -const LPCWSTR THEME_WC_PANEL = L"ThemePanel"; -const LPCWSTR THEME_WC_STATICOWNERDRAW = L"ThemeStaticOwnerDraw"; - -static Gdiplus::GdiplusStartupInput vgsi; -static Gdiplus::GdiplusStartupOutput vgso = { }; -static ULONG_PTR vgdiToken = 0; -static ULONG_PTR vgdiHookToken = 0; -static HMODULE vhHyperlinkRegisteredModule = NULL; -static HMODULE vhPanelRegisteredModule = NULL; -static HMODULE vhStaticOwnerDrawRegisteredModule = NULL; -static WNDPROC vpfnStaticOwnerDrawBaseWndProc = NULL; -static HMODULE vhModuleMsftEdit = NULL; -static HMODULE vhModuleRichEd = NULL; -static HCURSOR vhCursorHand = NULL; - -enum INTERNAL_CONTROL_STYLE -{ - INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED = 0x0001, - INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE = 0x0002, - INTERNAL_CONTROL_STYLE_DISABLED = 0x0004, - INTERNAL_CONTROL_STYLE_HIDDEN = 0x0008, - INTERNAL_CONTROL_STYLE_OWNER_DRAW = 0x0010, -}; - -struct MEMBUFFER_FOR_RICHEDIT -{ - BYTE* rgbData; - DWORD cbData; - - DWORD iData; -}; - - -// prototypes -static HRESULT RegisterWindowClasses( - __in_opt HMODULE hModule - ); -static HRESULT ParseTheme( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMDocument* pixd, - __out THEME** ppTheme - ); -static HRESULT ParseImage( - __in_opt HMODULE hModule, - __in_z_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __out HBITMAP* phImage - ); -static HRESULT ParseIcon( - __in_opt HMODULE hModule, - __in_z_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __out HICON* phIcon - ); -static HRESULT ParseWindow( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMElement* pElement, - __in THEME* pTheme - ); -static HRESULT GetFontColor( - __in IXMLDOMNode* pixn, - __in_z LPCWSTR wzAttributeName, - __out COLORREF* pColorRef, - __out DWORD* pdwSystemColor - ); -static HRESULT ParseFonts( - __in IXMLDOMElement* pElement, - __in THEME* pTheme - ); -static HRESULT ParsePages( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __in THEME* pTheme - ); -static HRESULT ParseImageLists( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __in THEME* pTheme - ); -static HRESULT ParseControls( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __in_opt THEME_PAGE* pPage - ); -static HRESULT ParseControl( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pixn, - __in THEME* pTheme, - __in THEME_CONTROL* pControl, - __in BOOL fSkipDimensions, - __in_opt THEME_PAGE* pPage - ); -static HRESULT ParseActions( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl - ); -static HRESULT ParseColumns( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl - ); -static HRESULT ParseRadioButtons( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pixn, - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __in THEME_PAGE* pPage - ); -static HRESULT ParseTabs( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl - ); -static HRESULT ParseText( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl, - __inout BOOL* pfAnyChildren -); -static HRESULT ParseTooltips( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl, - __inout BOOL* pfAnyChildren - ); -static HRESULT ParseNotes( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl, - __out BOOL* pfAnyChildren - ); -static HRESULT StopBillboard( - __in THEME* pTheme, - __in DWORD dwControl - ); -static HRESULT StartBillboard( - __in THEME* pTheme, - __in DWORD dwControl - ); -static HRESULT EnsureFontInstance( - __in THEME* pTheme, - __in THEME_FONT* pFont, - __out THEME_FONT_INSTANCE** ppFontInstance - ); -static HRESULT FindImageList( - __in THEME* pTheme, - __in_z LPCWSTR wzImageListName, - __out HIMAGELIST *phImageList - ); -static HRESULT LoadControls( - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, - __in DWORD cAssignControlIds - ); -static HRESULT ShowControl( - __in THEME* pTheme, - __in THEME_CONTROL* pControl, - __in int nCmdShow, - __in BOOL fSaveEditboxes, - __in THEME_SHOW_PAGE_REASON reason, - __in DWORD dwPageId, - __out_opt HWND* phwndFocus - ); -static HRESULT ShowControls( - __in THEME* pTheme, - __in_opt const THEME_CONTROL* pParentControl, - __in int nCmdShow, - __in BOOL fSaveEditboxes, - __in THEME_SHOW_PAGE_REASON reason, - __in DWORD dwPageId - ); -static HRESULT DrawButton( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ); -static void DrawControlText( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl, - __in BOOL fCentered, - __in BOOL fDrawFocusRect - ); -static HRESULT DrawHyperlink( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ); -static HRESULT DrawImage( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ); -static HRESULT DrawProgressBar( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ); -static BOOL DrawHoverControl( - __in THEME* pTheme, - __in BOOL fHover - ); -static DWORD CALLBACK RichEditStreamFromFileHandleCallback( - __in DWORD_PTR dwCookie, - __in_bcount(cb) LPBYTE pbBuff, - __in LONG cb, - __in LONG *pcb - ); -static DWORD CALLBACK RichEditStreamFromMemoryCallback( - __in DWORD_PTR dwCookie, - __in_bcount(cb) LPBYTE pbBuff, - __in LONG cb, - __in LONG *pcb - ); -static void FreeFontInstance( - __in THEME_FONT_INSTANCE* pFontInstance - ); -static void FreeFont( - __in THEME_FONT* pFont - ); -static void FreePage( - __in THEME_PAGE* pPage - ); -static void FreeControl( - __in THEME_CONTROL* pControl - ); -static void FreeConditionalText( - __in THEME_CONDITIONAL_TEXT* pConditionalText - ); -static void FreeImageList( - __in THEME_IMAGELIST* pImageList - ); -static void FreeAction( - __in THEME_ACTION* pAction - ); -static void FreeColumn( - __in THEME_COLUMN* pColumn - ); -static void FreeTab( - __in THEME_TAB* pTab - ); -static void CALLBACK OnBillboardTimer( - __in THEME* pTheme, - __in HWND hwnd, - __in UINT_PTR idEvent - ); -static void OnBrowseDirectory( - __in THEME* pTheme, - __in HWND hWnd, - __in const THEME_ACTION* pAction - ); -static BOOL OnButtonClicked( - __in THEME* pTheme, - __in HWND hWnd, - __in const THEME_CONTROL* pControl - ); -static BOOL OnDpiChanged( - __in THEME* pTheme, - __in WPARAM wParam, - __in LPARAM lParam - ); -static void OnNcCreate( - __in THEME* pTheme, - __in HWND hWnd, - __in LPARAM lParam - ); -static HRESULT OnRichEditEnLink( - __in LPARAM lParam, - __in HWND hWndRichEdit, - __in HWND hWnd - ); -static BOOL ControlIsType( - __in const THEME* pTheme, - __in DWORD dwControl, - __in THEME_CONTROL_TYPE type - ); -static const THEME_CONTROL* FindControlFromHWnd( - __in const THEME* pTheme, - __in HWND hWnd, - __in_opt const THEME_CONTROL* pParentControl = NULL - ); -static void GetControlDimensions( - __in const THEME_CONTROL* pControl, - __in const RECT* prcParent, - __out int* piWidth, - __out int* piHeight, - __out int* piX, - __out int* piY - ); -// Using iWidth as total width of listview, base width of columns, and "Expands" flag on columns -// calculates final width of each column (storing result in each column's nWidth value) -static HRESULT SizeListViewColumns( - __inout THEME_CONTROL* pControl - ); -static LRESULT CALLBACK ControlGroupDefWindowProc( - __in_opt THEME* pTheme, - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ); -static LRESULT CALLBACK PanelWndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ); -static LRESULT CALLBACK StaticOwnerDrawWndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ); -static HRESULT LocalizeControls( - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in const WIX_LOCALIZATION *pWixLoc - ); -static HRESULT LocalizeControl( - __in THEME_CONTROL* pControl, - __in const WIX_LOCALIZATION *pWixLoc - ); -static HRESULT LoadControlsString( - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in HMODULE hResModule - ); -static HRESULT LoadControlString( - __in THEME_CONTROL* pControl, - __in HMODULE hResModule - ); -static void ResizeControls( - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in const RECT* prcParent - ); -static void ResizeControl( - __in THEME_CONTROL* pControl, - __in const RECT* prcParent - ); -static void ScaleThemeFromWindow( - __in THEME* pTheme, - __in UINT nDpi, - __in int x, - __in int y - ); -static void ScaleTheme( - __in THEME* pTheme, - __in UINT nDpi, - __in int x, - __in int y, - __in DWORD dwStyle, - __in BOOL fMenu, - __in DWORD dwExStyle - ); -static void ScaleControls( - __in THEME* pTheme, - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in UINT nDpi - ); -static void ScaleControl( - __in THEME* pTheme, - __in THEME_CONTROL* pControl, - __in UINT nDpi - ); -static void GetControls( - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __out DWORD** ppcControls, - __out THEME_CONTROL*** pprgControls - ); -static void GetControls( - __in const THEME* pTheme, - __in_opt const THEME_CONTROL* pParentControl, - __out DWORD& cControls, - __out THEME_CONTROL*& rgControls - ); -static void UnloadControls( - __in DWORD cControls, - __in THEME_CONTROL* rgControls - ); - - -// Public functions. - -DAPI_(HRESULT) ThemeInitialize( - __in_opt HMODULE hModule - ) -{ - HRESULT hr = S_OK; - INITCOMMONCONTROLSEX icex = { }; - - DpiuInitialize(); - - hr = XmlInitialize(); - ThmExitOnFailure(hr, "Failed to initialize XML."); - - hr = RegisterWindowClasses(hModule); - ThmExitOnFailure(hr, "Failed to register theme window classes."); - - // Initialize GDI+ and common controls. - vgsi.SuppressBackgroundThread = TRUE; - - hr = GdipInitialize(&vgsi, &vgdiToken, &vgso); - ThmExitOnFailure(hr, "Failed to initialize GDI+."); - - icex.dwSize = sizeof(INITCOMMONCONTROLSEX); - icex.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS | ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_TAB_CLASSES | ICC_LINK_CLASS; - ::InitCommonControlsEx(&icex); - - (*vgso.NotificationHook)(&vgdiHookToken); - -LExit: - return hr; -} - - -DAPI_(void) ThemeUninitialize() -{ - if (vhModuleMsftEdit) - { - ::FreeLibrary(vhModuleMsftEdit); - vhModuleMsftEdit = NULL; - } - - if (vhModuleRichEd) - { - ::FreeLibrary(vhModuleRichEd); - vhModuleRichEd = NULL; - } - - if (vhHyperlinkRegisteredModule) - { - ::UnregisterClassW(THEME_WC_HYPERLINK, vhHyperlinkRegisteredModule); - vhHyperlinkRegisteredModule = NULL; - } - - if (vhPanelRegisteredModule) - { - ::UnregisterClassW(THEME_WC_PANEL, vhPanelRegisteredModule); - vhPanelRegisteredModule = NULL; - } - - if (vhStaticOwnerDrawRegisteredModule) - { - ::UnregisterClassW(THEME_WC_STATICOWNERDRAW, vhStaticOwnerDrawRegisteredModule); - vhStaticOwnerDrawRegisteredModule = NULL; - vpfnStaticOwnerDrawBaseWndProc = NULL; - } - - if (vgdiToken) - { - GdipUninitialize(vgdiToken); - vgdiToken = 0; - } - - XmlUninitialize(); - DpiuUninitialize(); -} - - -DAPI_(HRESULT) ThemeLoadFromFile( - __in_z LPCWSTR wzThemeFile, - __out THEME** ppTheme - ) -{ - HRESULT hr = S_OK; - IXMLDOMDocument* pixd = NULL; - LPWSTR sczRelativePath = NULL; - - hr = XmlLoadDocumentFromFile(wzThemeFile, &pixd); - ThmExitOnFailure(hr, "Failed to load theme resource as XML document."); - - hr = PathGetDirectory(wzThemeFile, &sczRelativePath); - ThmExitOnFailure(hr, "Failed to get relative path from theme file."); - - hr = ParseTheme(NULL, sczRelativePath, pixd, ppTheme); - ThmExitOnFailure(hr, "Failed to parse theme."); - -LExit: - ReleaseStr(sczRelativePath); - ReleaseObject(pixd); - - return hr; -} - - -DAPI_(HRESULT) ThemeLoadFromResource( - __in_opt HMODULE hModule, - __in_z LPCSTR szResource, - __out THEME** ppTheme - ) -{ - HRESULT hr = S_OK; - LPVOID pvResource = NULL; - DWORD cbResource = 0; - LPWSTR sczXml = NULL; - IXMLDOMDocument* pixd = NULL; - - hr = ResReadData(hModule, szResource, &pvResource, &cbResource); - ThmExitOnFailure(hr, "Failed to read theme from resource."); - - hr = StrAllocStringAnsi(&sczXml, reinterpret_cast(pvResource), cbResource, CP_UTF8); - ThmExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); - - hr = XmlLoadDocument(sczXml, &pixd); - ThmExitOnFailure(hr, "Failed to load theme resource as XML document."); - - hr = ParseTheme(hModule, NULL, pixd, ppTheme); - ThmExitOnFailure(hr, "Failed to parse theme."); - -LExit: - ReleaseObject(pixd); - ReleaseStr(sczXml); - - return hr; -} - - -DAPI_(void) ThemeFree( - __in THEME* pTheme - ) -{ - if (pTheme) - { - for (DWORD i = 0; i < pTheme->cFonts; ++i) - { - FreeFont(pTheme->rgFonts + i); - } - - for (DWORD i = 0; i < pTheme->cPages; ++i) - { - FreePage(pTheme->rgPages + i); - } - - for (DWORD i = 0; i < pTheme->cImageLists; ++i) - { - FreeImageList(pTheme->rgImageLists + i); - } - - for (DWORD i = 0; i < pTheme->cControls; ++i) - { - FreeControl(pTheme->rgControls + i); - } - - ReleaseMem(pTheme->rgControls); - ReleaseMem(pTheme->rgPages); - ReleaseMem(pTheme->rgFonts); - - if (pTheme->hImage) - { - ::DeleteBitmap(pTheme->hImage); - } - - ReleaseStr(pTheme->sczCaption); - ReleaseMem(pTheme); - } -} - -DAPI_(HRESULT) ThemeRegisterVariableCallbacks( - __in THEME* pTheme, - __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition, - __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString, - __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable, - __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable, - __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable, - __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable, - __in_opt LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); - - pTheme->pfnEvaluateCondition = pfnEvaluateCondition; - pTheme->pfnFormatString = pfnFormatString; - pTheme->pfnGetNumericVariable = pfnGetNumericVariable; - pTheme->pfnSetNumericVariable = pfnSetNumericVariable; - pTheme->pfnGetStringVariable = pfnGetStringVariable; - pTheme->pfnSetStringVariable = pfnSetStringVariable; - pTheme->pvVariableContext = pvContext; - -LExit: - return hr; -} - - -DAPI_(HRESULT) ThemeCreateParentWindow( - __in THEME* pTheme, - __in DWORD dwExStyle, - __in LPCWSTR szClassName, - __in LPCWSTR szWindowName, - __in DWORD dwStyle, - __in int x, - __in int y, - __in_opt HWND hwndParent, - __in_opt HINSTANCE hInstance, - __in_opt LPVOID lpParam, - __in THEME_WINDOW_INITIAL_POSITION initialPosition, - __out_opt HWND* phWnd - ) -{ - HRESULT hr = S_OK; - DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; - POINT pt = { }; - RECT* pMonitorRect = NULL; - HMENU hMenu = NULL; - HWND hWnd = NULL; - - if (pTheme->hwndParent) - { - ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeCreateParentWindow called after the theme was loaded."); - } - - if (THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES == initialPosition) - { - pt.x = x; - pt.y = y; - hr = DpiuGetMonitorContextFromPoint(&pt, &pMonitorContext); - if (SUCCEEDED(hr)) - { - pMonitorRect = &pMonitorContext->mi.rcWork; - if (pMonitorContext->nDpi != pTheme->nDpi) - { - ScaleTheme(pTheme, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top, dwStyle, NULL != hMenu, dwExStyle); - } - - x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pTheme->nWindowWidth) / 2; - y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pTheme->nWindowHeight) / 2; - } - else - { - hr = S_OK; - x = CW_USEDEFAULT; - y = CW_USEDEFAULT; - } - } - - hWnd = ::CreateWindowExW(dwExStyle, szClassName, szWindowName, dwStyle, x, y, pTheme->nWindowWidth, pTheme->nWindowHeight, hwndParent, hMenu, hInstance, lpParam); - ThmExitOnNullWithLastError(hWnd, hr, "Failed to create theme parent window."); - ThmExitOnNull(pTheme->hwndParent, hr, E_INVALIDSTATE, "Theme parent window is not set, make sure ThemeDefWindowProc is called for WM_NCCREATE."); - AssertSz(hWnd == pTheme->hwndParent, "Theme parent window does not equal newly created window."); - - if (phWnd) - { - *phWnd = hWnd; - } - -LExit: - ReleaseMem(pMonitorContext); - - return hr; -} - - -DAPI_(HRESULT) ThemeLoadControls( - __in THEME* pTheme, - __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, - __in DWORD cAssignControlIds - ) -{ - HRESULT hr = S_OK; - - if (!pTheme->hwndParent) - { - ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeLoadControls called before theme parent window created."); - } - - hr = LoadControls(pTheme, NULL, rgAssignControlIds, cAssignControlIds); - -LExit: - return hr; -} - - -DAPI_(void) ThemeUnloadControls( - __in THEME* pTheme - ) -{ - UnloadControls(pTheme->cControls, pTheme->rgControls); - - pTheme->hwndHover = NULL; - pTheme->hwndParent = NULL; -} - -DAPI_(HRESULT) ThemeLocalize( - __in THEME *pTheme, - __in const WIX_LOCALIZATION *pWixLoc - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCaption = NULL; - - hr = LocLocalizeString(pWixLoc, &pTheme->sczCaption); - ThmExitOnFailure(hr, "Failed to localize theme caption."); - - if (pTheme->pfnFormatString) - { - hr = pTheme->pfnFormatString(pTheme->sczCaption, &sczCaption, pTheme->pvVariableContext); - if (SUCCEEDED(hr)) - { - hr = ThemeUpdateCaption(pTheme, sczCaption); - } - } - - hr = LocalizeControls(pTheme->cControls, pTheme->rgControls, pWixLoc); - -LExit: - ReleaseStr(sczCaption); - - return hr; -} - -/******************************************************************** - ThemeLoadStrings - Loads string resources. - Must be called after loading a theme and before calling - ThemeLoadControls. -*******************************************************************/ -DAPI_(HRESULT) ThemeLoadStrings( - __in THEME* pTheme, - __in HMODULE hResModule - ) -{ - HRESULT hr = S_OK; - ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); - - if (UINT_MAX != pTheme->uStringId) - { - hr = ResReadString(hResModule, pTheme->uStringId, &pTheme->sczCaption); - ThmExitOnFailure(hr, "Failed to load theme caption."); - } - - hr = LoadControlsString(pTheme->cControls, pTheme->rgControls, hResModule); - -LExit: - return hr; -} - - -DAPI_(HRESULT) ThemeLoadRichEditFromFile( - __in THEME* pTheme, - __in DWORD dwControl, - __in_z LPCWSTR wzFileName, - __in HMODULE hModule - ) -{ - HRESULT hr = S_OK; - LPWSTR sczFile = NULL; - HANDLE hFile = INVALID_HANDLE_VALUE; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - - hr = PathRelativeToModule(&sczFile, wzFileName, hModule); - ThmExitOnFailure(hr, "Failed to read resource data."); - - hFile = ::CreateFileW(sczFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - ThmExitWithLastError(hr, "Failed to open RTF file."); - } - else - { - LONGLONG llRtfSize; - hr = FileSizeByHandle(hFile, &llRtfSize); - if (SUCCEEDED(hr)) - { - ::SendMessageW(hWnd, EM_EXLIMITTEXT, 0, static_cast(llRtfSize)); - } - - EDITSTREAM es = { }; - es.pfnCallback = RichEditStreamFromFileHandleCallback; - es.dwCookie = reinterpret_cast(hFile); - - ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast(&es)); - hr = es.dwError; - ThmExitOnFailure(hr, "Failed to update RTF stream."); - } - -LExit: - ReleaseStr(sczFile); - ReleaseFile(hFile); - - return hr; -} - - -DAPI_(HRESULT) ThemeLoadRichEditFromResource( - __in THEME* pTheme, - __in DWORD dwControl, - __in_z LPCSTR szResourceName, - __in HMODULE hModule - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - return ThemeLoadRichEditFromResourceToHWnd(hWnd, szResourceName, hModule); -} - -DAPI_(HRESULT) ThemeLoadRichEditFromResourceToHWnd( - __in HWND hWnd, - __in_z LPCSTR szResourceName, - __in HMODULE hModule - ) -{ - HRESULT hr = S_OK; - MEMBUFFER_FOR_RICHEDIT buffer = { }; - EDITSTREAM es = { }; - - hr = ResReadData(hModule, szResourceName, reinterpret_cast(&buffer.rgbData), &buffer.cbData); - ThmExitOnFailure(hr, "Failed to read resource data."); - - es.pfnCallback = RichEditStreamFromMemoryCallback; - es.dwCookie = reinterpret_cast(&buffer); - - ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast(&es)); - hr = es.dwError; - ThmExitOnFailure(hr, "Failed to update RTF stream."); - -LExit: - return hr; -} - - -DAPI_(BOOL) ThemeHandleKeyboardMessage( - __in_opt THEME* pTheme, - __in HWND /*hWnd*/, - __in MSG* pMsg - ) -{ - return pTheme ? ::IsDialogMessageW(pTheme->hwndParent, pMsg) : FALSE; -} - - -extern "C" LRESULT CALLBACK ThemeDefWindowProc( - __in_opt THEME* pTheme, - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - RECT rcParent = { }; - RECT *pRect = NULL; - - if (pTheme) - { - switch (uMsg) - { - case WM_NCCREATE: - if (pTheme->hwndParent) - { - AssertSz(FALSE, "WM_NCCREATE called multiple times"); - } - else - { - OnNcCreate(pTheme, hWnd, lParam); - } - break; - - case WM_NCHITTEST: - if (pTheme->dwStyle & WS_POPUP) - { - return HTCAPTION; // allow pop-up windows to be moved by grabbing any non-control. - } - break; - - case WM_DPICHANGED: - if (OnDpiChanged(pTheme, wParam, lParam)) - { - return 0; - } - break; - - case WM_SIZING: - if (pTheme->fAutoResize) - { - pRect = reinterpret_cast(lParam); - if (pRect->right - pRect->left < pTheme->nMinimumWidth) - { - if (wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT) - { - pRect->left = pRect->right - pTheme->nMinimumWidth; - } - else - { - pRect->right = pRect->left + pTheme->nMinimumWidth; - } - } - if (pRect->bottom - pRect->top < pTheme->nMinimumHeight) - { - if (wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT) - { - pRect->bottom = pRect->top + pTheme->nMinimumHeight; - } - else - { - pRect->top = pRect->bottom - pTheme->nMinimumHeight; - } - } - - return TRUE; - } - break; - - case WM_SIZE: - if (pTheme->fAutoResize || pTheme->fForceResize) - { - pTheme->fForceResize = FALSE; - ::GetClientRect(pTheme->hwndParent, &rcParent); - ResizeControls(pTheme->cControls, pTheme->rgControls, &rcParent); - return 0; - } - break; - } - } - - return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); -} - - -DAPI_(void) ThemeGetPageIds( - __in const THEME* pTheme, - __in_ecount(cGetPages) LPCWSTR* rgwzFindNames, - __inout_ecount(cGetPages) DWORD* rgdwPageIds, - __in DWORD cGetPages - ) -{ - for (DWORD i = 0; i < cGetPages; ++i) - { - LPCWSTR wzFindName = rgwzFindNames[i]; - for (DWORD j = 0; j < pTheme->cPages; ++j) - { - LPCWSTR wzPageName = pTheme->rgPages[j].sczName; - if (wzPageName && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPageName, -1, wzFindName, -1)) - { - rgdwPageIds[i] = j + 1; // add one to make the page ids 1-based (so zero is invalid). - break; - } - } - } -} - - -DAPI_(THEME_PAGE*) ThemeGetPage( - __in const THEME* pTheme, - __in DWORD dwPage - ) -{ - DWORD iPage = dwPage - 1; - THEME_PAGE* pPage = NULL; - - if (iPage < pTheme->cPages) - { - pPage = pTheme->rgPages + iPage; - } - - return pPage; -} - - -DAPI_(HRESULT) ThemeShowPage( - __in THEME* pTheme, - __in DWORD dwPage, - __in int nCmdShow - ) -{ - return ThemeShowPageEx(pTheme, dwPage, nCmdShow, THEME_SHOW_PAGE_REASON_DEFAULT); -} - - -DAPI_(HRESULT) ThemeShowPageEx( - __in THEME* pTheme, - __in DWORD dwPage, - __in int nCmdShow, - __in THEME_SHOW_PAGE_REASON reason - ) -{ - HRESULT hr = S_OK; - BOOL fHide = SW_HIDE == nCmdShow; - BOOL fSaveEditboxes = FALSE; - THEME_SAVEDVARIABLE* pSavedVariable = NULL; - THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPage); - - if (pPage) - { - if (fHide) - { - switch (reason) - { - case THEME_SHOW_PAGE_REASON_DEFAULT: - // Set the variables in the loop below. - fSaveEditboxes = TRUE; - break; - case THEME_SHOW_PAGE_REASON_CANCEL: - if (pPage->cSavedVariables && pTheme->pfnSetStringVariable) - { - // Best effort to cancel any changes to the variables. - for (DWORD v = 0; v < pPage->cSavedVariables; ++v) - { - pSavedVariable = pPage->rgSavedVariables + v; - if (pSavedVariable->wzName) - { - pTheme->pfnSetStringVariable(pSavedVariable->wzName, pSavedVariable->sczValue, FALSE, pTheme->pvVariableContext); - } - } - } - break; - } - - if (THEME_SHOW_PAGE_REASON_REFRESH != reason) - { - pPage->cSavedVariables = 0; - if (pPage->rgSavedVariables) - { - SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables)); - } - } - - pTheme->dwCurrentPageId = 0; - } - else - { - if (THEME_SHOW_PAGE_REASON_REFRESH == reason) - { - fSaveEditboxes = TRUE; - } - else - { - hr = MemEnsureArraySize(reinterpret_cast(&pPage->rgSavedVariables), pPage->cControlIndices, sizeof(THEME_SAVEDVARIABLE), pPage->cControlIndices); - ThmExitOnFailure(hr, "Failed to allocate memory for saved variables."); - - SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables)); - pPage->cSavedVariables = pPage->cControlIndices; - - // Save the variables in the loop below. - } - - pTheme->dwCurrentPageId = dwPage; - } - } - - hr = ShowControls(pTheme, NULL, nCmdShow, fSaveEditboxes, reason, dwPage); - ThmExitOnFailure(hr, "Failed to show page controls."); - -LExit: - return hr; -} - - -DAPI_(BOOL) ThemeControlExists( - __in const THEME* pTheme, - __in DWORD dwControl - ) -{ - BOOL fExists = FALSE; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - if (hWnd) - { - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); - fExists = (pControl && hWnd == pControl->hWnd); - } - - return fExists; -} - - -DAPI_(void) ThemeControlEnable( - __in THEME* pTheme, - __in DWORD dwControl, - __in BOOL fEnable - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); - if (pControl) - { - pControl->dwInternalStyle = fEnable ? (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_DISABLED) : (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_DISABLED); - ::EnableWindow(hWnd, fEnable); - - if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED) - { - ::ShowWindow(hWnd, fEnable ? SW_SHOW : SW_HIDE); - } - } -} - - -DAPI_(BOOL) ThemeControlEnabled( - __in THEME* pTheme, - __in DWORD dwControl - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); - return pControl && !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED); -} - - -DAPI_(void) ThemeControlElevates( - __in THEME* pTheme, - __in DWORD dwControl, - __in BOOL fElevates - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - ::SendMessageW(hWnd, BCM_SETSHIELD, 0, fElevates); -} - - -DAPI_(void) ThemeShowControl( - __in THEME* pTheme, - __in DWORD dwControl, - __in int nCmdShow - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - ::ShowWindow(hWnd, nCmdShow); - - // Save the control's visible state. - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); - if (pControl) - { - pControl->dwInternalStyle = (SW_HIDE == nCmdShow) ? (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_HIDDEN) : (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_HIDDEN); - } -} - - -DAPI_(void) ThemeShowControlEx( - __in THEME* pTheme, - __in DWORD dwControl, - __in int nCmdShow - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); - if (pControl) - { - ShowControl(pTheme, pControl, nCmdShow, THEME_CONTROL_TYPE_EDITBOX == pControl->type, THEME_SHOW_PAGE_REASON_REFRESH, 0, NULL); - } -} - - -DAPI_(BOOL) ThemeControlVisible( - __in THEME* pTheme, - __in DWORD dwControl - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - return ::IsWindowVisible(hWnd); -} - - -DAPI_(BOOL) ThemePostControlMessage( - __in THEME* pTheme, - __in DWORD dwControl, - __in UINT Msg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - - if (!::PostMessageW(hWnd, Msg, wParam, lParam)) - { - er = ::GetLastError(); - hr = HRESULT_FROM_WIN32(er); - } - - return SUCCEEDED(hr); -} - - -DAPI_(LRESULT) ThemeSendControlMessage( - __in const THEME* pTheme, - __in DWORD dwControl, - __in UINT Msg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - return ::SendMessageW(hWnd, Msg, wParam, lParam); -} - - -DAPI_(HRESULT) ThemeDrawBackground( - __in THEME* pTheme, - __in PAINTSTRUCT* pps - ) -{ - HRESULT hr = S_FALSE; - - if (pTheme->hImage && 0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY && pps->fErase) - { - HDC hdcMem = ::CreateCompatibleDC(pps->hdc); - HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pTheme->hImage)); - DWORD dwSourceWidth = pTheme->nDefaultDpiWidth; - DWORD dwSourceHeight = pTheme->nDefaultDpiHeight; - - ::StretchBlt(pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight, hdcMem, pTheme->nSourceX, pTheme->nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); - - ::SelectObject(hdcMem, hDefaultBitmap); - ::DeleteDC(hdcMem); - - hr = S_OK; - } - - return hr; -} - - -DAPI_(HRESULT) ThemeDrawControl( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis - ) -{ - HRESULT hr = S_OK; - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, pdis->hwndItem); - - AssertSz(pControl, "Expected control window from owner draw window."); - AssertSz(pControl->hWnd == pdis->hwndItem, "Expected control window to match owner draw window."); - AssertSz(pControl->nWidth < 1 || pControl->nWidth == pdis->rcItem.right - pdis->rcItem.left, "Expected control window width to match owner draw window width."); - AssertSz(pControl->nHeight < 1 || pControl->nHeight == pdis->rcItem.bottom - pdis->rcItem.top, "Expected control window height to match owner draw window height."); - - switch (pControl->type) - { - case THEME_CONTROL_TYPE_BUTTON: - hr = DrawButton(pTheme, pdis, pControl); - ThmExitOnFailure(hr, "Failed to draw button."); - break; - - case THEME_CONTROL_TYPE_HYPERLINK: - hr = DrawHyperlink(pTheme, pdis, pControl); - ThmExitOnFailure(hr, "Failed to draw hyperlink."); - break; - - case THEME_CONTROL_TYPE_IMAGE: - hr = DrawImage(pTheme, pdis, pControl); - ThmExitOnFailure(hr, "Failed to draw image."); - break; - - case THEME_CONTROL_TYPE_PROGRESSBAR: - hr = DrawProgressBar(pTheme, pdis, pControl); - ThmExitOnFailure(hr, "Failed to draw progress bar."); - break; - - default: - hr = E_UNEXPECTED; - ThmExitOnRootFailure(hr, "Did not specify an owner draw control to draw."); - } - -LExit: - return hr; -} - - -DAPI_(BOOL) ThemeHoverControl( - __in THEME* pTheme, - __in HWND hwndParent, - __in HWND hwndControl - ) -{ - BOOL fHovered = FALSE; - if (hwndControl != pTheme->hwndHover) - { - if (pTheme->hwndHover && pTheme->hwndHover != hwndParent) - { - DrawHoverControl(pTheme, FALSE); - } - - pTheme->hwndHover = hwndControl; - - if (pTheme->hwndHover && pTheme->hwndHover != hwndParent) - { - fHovered = DrawHoverControl(pTheme, TRUE); - } - } - - return fHovered; -} - - -DAPI_(BOOL) ThemeIsControlChecked( - __in THEME* pTheme, - __in DWORD dwControl - ) -{ - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - return BST_CHECKED == ::SendMessageW(hWnd, BM_GETCHECK, 0, 0); -} - - -DAPI_(BOOL) ThemeSetControlColor( - __in THEME* pTheme, - __in HDC hdc, - __in HWND hWnd, - __out HBRUSH* phBackgroundBrush - ) -{ - THEME_FONT* pFont = NULL; - BOOL fHasBackground = FALSE; - - *phBackgroundBrush = NULL; - - if (hWnd == pTheme->hwndParent) - { - pFont = (THEME_INVALID_ID == pTheme->dwFontId) ? NULL : pTheme->rgFonts + pTheme->dwFontId; - } - else - { - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); - pFont = (!pControl || THEME_INVALID_ID == pControl->dwFontId) ? NULL : pTheme->rgFonts + pControl->dwFontId; - } - - if (pFont) - { - if (pFont->hForeground) - { - ::SetTextColor(hdc, pFont->crForeground); - } - - if (pFont->hBackground) - { - ::SetBkColor(hdc, pFont->crBackground); - - *phBackgroundBrush = pFont->hBackground; - fHasBackground = TRUE; - } - else - { - ::SetBkMode(hdc, TRANSPARENT); - *phBackgroundBrush = static_cast(::GetStockObject(NULL_BRUSH)); - fHasBackground = TRUE; - } - } - - return fHasBackground; -} - - -DAPI_(HRESULT) ThemeSetProgressControl( - __in THEME* pTheme, - __in DWORD dwControl, - __in DWORD dwProgressPercentage - ) -{ - HRESULT hr = E_NOTFOUND; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - - if (hWnd) - { - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); - if (pControl && THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type) - { - DWORD dwCurrentProgress = LOWORD(pControl->dwData); - - if (dwCurrentProgress != dwProgressPercentage) - { - DWORD dwColor = HIWORD(pControl->dwData); - pControl->dwData = MAKEDWORD(dwProgressPercentage, dwColor); - - if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW) - { - if (!::InvalidateRect(hWnd, NULL, FALSE)) - { - ThmExitWithLastError(hr, "Failed to invalidate progress bar window."); - } - } - else - { - ::SendMessageW(hWnd, PBM_SETPOS, dwProgressPercentage, 0); - } - - hr = S_OK; - } - else - { - hr = S_FALSE; - } - } - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) ThemeSetProgressControlColor( - __in THEME* pTheme, - __in DWORD dwControl, - __in DWORD dwColorIndex - ) -{ - HRESULT hr = S_FALSE; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - if (hWnd) - { - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); - - // Only set color on owner draw progress bars. - if (pControl && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW)) - { - DWORD dwCurrentColor = HIWORD(pControl->dwData); - - if (dwCurrentColor != dwColorIndex) - { - DWORD dwCurrentProgress = LOWORD(pControl->dwData); - pControl->dwData = MAKEDWORD(dwCurrentProgress, dwColorIndex); - - if (!::InvalidateRect(hWnd, NULL, FALSE)) - { - ThmExitWithLastError(hr, "Failed to invalidate progress bar window."); - } - - hr = S_OK; - } - } - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) ThemeSetTextControl( - __in const THEME* pTheme, - __in DWORD dwControl, - __in_z_opt LPCWSTR wzText - ) -{ - return ThemeSetTextControlEx(pTheme, dwControl, FALSE, wzText); -} - - -DAPI_(HRESULT) ThemeSetTextControlEx( - __in const THEME* pTheme, - __in DWORD dwControl, - __in BOOL fUpdate, - __in_z_opt LPCWSTR wzText - ) -{ - HRESULT hr = S_OK; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - - if (hWnd) - { - if (fUpdate) - { - ::ShowWindow(hWnd, SW_HIDE); - } - - if (!::SetWindowTextW(hWnd, wzText)) - { - ThmExitWithLastError(hr, "Failed to set control text."); - } - - if (fUpdate) - { - ::ShowWindow(hWnd, SW_SHOW); - } - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) ThemeGetTextControl( - __in const THEME* pTheme, - __in DWORD dwControl, - __inout_z LPWSTR* psczText - ) -{ - HRESULT hr = S_OK; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - SIZE_T cbSize = 0; - DWORD cchText = 0; - DWORD cchTextRead = 0; - - // Ensure the string has room for at least one character. - hr = StrMaxLength(*psczText, &cbSize); - ThmExitOnFailure(hr, "Failed to get text buffer length."); - - cchText = (DWORD)min(DWORD_MAX, cbSize); - - if (!cchText) - { - cchText = GROW_WINDOW_TEXT; - - hr = StrAlloc(psczText, cchText); - ThmExitOnFailure(hr, "Failed to grow text buffer."); - } - - // Read (and keep growing buffer) until we finally read less than there - // is room in the buffer. - for (;;) - { - cchTextRead = ::GetWindowTextW(hWnd, *psczText, cchText); - if (cchTextRead + 1 < cchText) - { - break; - } - else - { - cchText = cchTextRead + GROW_WINDOW_TEXT; - - hr = StrAlloc(psczText, cchText); - ThmExitOnFailure(hr, "Failed to grow text buffer again."); - } - } - -LExit: - return hr; -} - - -DAPI_(HRESULT) ThemeUpdateCaption( - __in THEME* pTheme, - __in_z LPCWSTR wzCaption - ) -{ - HRESULT hr = S_OK; - - hr = StrAllocString(&pTheme->sczCaption, wzCaption, 0); - ThmExitOnFailure(hr, "Failed to update theme caption."); - -LExit: - return hr; -} - - -DAPI_(void) ThemeSetFocus( - __in THEME* pTheme, - __in DWORD dwControl - ) -{ - HWND hwndFocus = ::GetDlgItem(pTheme->hwndParent, dwControl); - if (hwndFocus && !ThemeControlEnabled(pTheme, dwControl)) - { - hwndFocus = ::GetNextDlgTabItem(pTheme->hwndParent, hwndFocus, FALSE); - } - - if (hwndFocus) - { - ::SetFocus(hwndFocus); - } -} - - -DAPI_(void) ThemeShowChild( - __in THEME* pTheme, - __in THEME_CONTROL* pParentControl, - __in DWORD dwIndex - ) -{ - // show one child, hide the rest - for (DWORD i = 0; i < pParentControl->cControls; ++i) - { - THEME_CONTROL* pControl = pParentControl->rgControls + i; - ShowControl(pTheme, pControl, dwIndex == i ? SW_SHOW : SW_HIDE, FALSE, THEME_SHOW_PAGE_REASON_DEFAULT, 0, NULL); - } -} - - -// Internal functions. - -static HRESULT RegisterWindowClasses( - __in_opt HMODULE hModule - ) -{ - HRESULT hr = S_OK; - WNDCLASSW wcHyperlink = { }; - WNDCLASSW wcPanel = { }; - WNDCLASSW wcStaticOwnerDraw = { }; - WNDPROC pfnStaticOwnerDrawBaseWndProc = NULL; - - vhCursorHand = ::LoadCursorA(NULL, IDC_HAND); - - // Base the theme hyperlink class on a button but give it the "hand" icon. - if (!::GetClassInfoW(NULL, WC_BUTTONW, &wcHyperlink)) - { - ThmExitWithLastError(hr, "Failed to get button window class."); - } - - wcHyperlink.lpszClassName = THEME_WC_HYPERLINK; -#pragma prefast(push) -#pragma prefast(disable:25068) - wcHyperlink.hCursor = vhCursorHand; -#pragma prefast(pop) - - if (!::RegisterClassW(&wcHyperlink)) - { - ThmExitWithLastError(hr, "Failed to get button window class."); - } - vhHyperlinkRegisteredModule = hModule; - - // Panel is its own do-nothing class. - wcPanel.lpfnWndProc = PanelWndProc; - wcPanel.hInstance = hModule; - wcPanel.hCursor = ::LoadCursorW(NULL, (LPCWSTR) IDC_ARROW); - wcPanel.lpszClassName = THEME_WC_PANEL; - if (!::RegisterClassW(&wcPanel)) - { - ThmExitWithLastError(hr, "Failed to register window."); - } - vhPanelRegisteredModule = hModule; - - if (!::GetClassInfoW(NULL, WC_STATICW, &wcStaticOwnerDraw)) - { - ThmExitWithLastError(hr, "Failed to get static window class."); - } - - pfnStaticOwnerDrawBaseWndProc = wcStaticOwnerDraw.lpfnWndProc; - wcStaticOwnerDraw.lpfnWndProc = StaticOwnerDrawWndProc; - wcStaticOwnerDraw.hInstance = hModule; - wcStaticOwnerDraw.lpszClassName = THEME_WC_STATICOWNERDRAW; - if (!::RegisterClassW(&wcStaticOwnerDraw)) - { - ThmExitWithLastError(hr, "Failed to register OwnerDraw window class."); - } - vhStaticOwnerDrawRegisteredModule = hModule; - vpfnStaticOwnerDrawBaseWndProc = pfnStaticOwnerDrawBaseWndProc; - - -LExit: - return hr; -} - -static HRESULT ParseTheme( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMDocument* pixd, - __out THEME** ppTheme - ) -{ - static WORD wThemeId = 0; - - HRESULT hr = S_OK; - THEME* pTheme = NULL; - IXMLDOMElement *pThemeElement = NULL; - - hr = pixd->get_documentElement(&pThemeElement); - ThmExitOnFailure(hr, "Failed to get theme element."); - - pTheme = static_cast(MemAlloc(sizeof(THEME), TRUE)); - ThmExitOnNull(pTheme, hr, E_OUTOFMEMORY, "Failed to allocate memory for theme."); - - pTheme->wId = ++wThemeId; - pTheme->nDpi = USER_DEFAULT_SCREEN_DPI; - - // Parse the optional background resource image. - hr = ParseImage(hModule, wzRelativePath, pThemeElement, &pTheme->hImage); - ThmExitOnFailure(hr, "Failed while parsing theme image."); - - // Parse the fonts. - hr = ParseFonts(pThemeElement, pTheme); - ThmExitOnFailure(hr, "Failed to parse theme fonts."); - - // Parse the window element. - hr = ParseWindow(hModule, wzRelativePath, pThemeElement, pTheme); - ThmExitOnFailure(hr, "Failed to parse theme window element."); - - *ppTheme = pTheme; - pTheme = NULL; - -LExit: - ReleaseObject(pThemeElement); - - if (pTheme) - { - ThemeFree(pTheme); - } - - return hr; -} - -static HRESULT ParseImage( - __in_opt HMODULE hModule, - __in_z_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __out HBITMAP* phImage - ) -{ - HRESULT hr = S_OK; - BSTR bstr = NULL; - LPWSTR sczImageFile = NULL; - int iResourceId = 0; - Gdiplus::Bitmap* pBitmap = NULL; - *phImage = NULL; - - hr = XmlGetAttribute(pElement, L"ImageResource", &bstr); - ThmExitOnFailure(hr, "Failed to get image resource attribute."); - - if (S_OK == hr) - { - iResourceId = wcstol(bstr, NULL, 10); - - hr = GdipBitmapFromResource(hModule, MAKEINTRESOURCE(iResourceId), &pBitmap); - // Don't fail. - } - - ReleaseNullBSTR(bstr); - - // Parse the optional background image from a given file. - if (!pBitmap) - { - hr = XmlGetAttribute(pElement, L"ImageFile", &bstr); - ThmExitOnFailure(hr, "Failed to get image file attribute."); - - if (S_OK == hr) - { - if (wzRelativePath) - { - hr = PathConcat(wzRelativePath, bstr, &sczImageFile); - ThmExitOnFailure(hr, "Failed to combine image file path."); - } - else - { - hr = PathRelativeToModule(&sczImageFile, bstr, hModule); - ThmExitOnFailure(hr, "Failed to get image filename."); - } - - hr = GdipBitmapFromFile(sczImageFile, &pBitmap); - // Don't fail. - } - } - - // If there is an image, convert it into a bitmap handle. - if (pBitmap) - { - Gdiplus::Color black; - Gdiplus::Status gs = pBitmap->GetHBITMAP(black, phImage); - ThmExitOnGdipFailure(gs, hr, "Failed to convert GDI+ bitmap into HBITMAP."); - } - - hr = S_OK; - -LExit: - if (pBitmap) - { - delete pBitmap; - } - - ReleaseStr(sczImageFile); - ReleaseBSTR(bstr); - - return hr; -} - - -static HRESULT ParseIcon( - __in_opt HMODULE hModule, - __in_z_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __out HICON* phIcon - ) -{ - HRESULT hr = S_OK; - BSTR bstr = NULL; - LPWSTR sczImageFile = NULL; - int iResourceId = 0; - *phIcon = NULL; - - hr = XmlGetAttribute(pElement, L"IconResource", &bstr); - ThmExitOnFailure(hr, "Failed to get icon resource attribute."); - - if (S_OK == hr) - { - iResourceId = wcstol(bstr, NULL, 10); - - *phIcon = reinterpret_cast(::LoadImageW(hModule, MAKEINTRESOURCEW(iResourceId), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE)); - ThmExitOnNullWithLastError(*phIcon, hr, "Failed to load icon."); - } - else - { - ReleaseNullBSTR(bstr); - - hr = XmlGetAttribute(pElement, L"IconFile", &bstr); - ThmExitOnFailure(hr, "Failed to get icon file attribute."); - - if (S_OK == hr) - { - if (wzRelativePath) - { - hr = PathConcat(wzRelativePath, bstr, &sczImageFile); - ThmExitOnFailure(hr, "Failed to combine image file path."); - } - else - { - hr = PathRelativeToModule(&sczImageFile, bstr, hModule); - ThmExitOnFailure(hr, "Failed to get image filename."); - } - - *phIcon = reinterpret_cast(::LoadImageW(NULL, sczImageFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE)); - ThmExitOnNullWithLastError(*phIcon, hr, "Failed to load icon: %ls.", sczImageFile); - } - } - -LExit: - ReleaseStr(sczImageFile); - ReleaseBSTR(bstr); - - return hr; -} - - -static HRESULT ParseWindow( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMElement* pElement, - __in THEME* pTheme - ) -{ - HRESULT hr = S_OK; - IXMLDOMNode* pixn = NULL; - DWORD dwValue = 0; - BSTR bstr = NULL; - LPWSTR sczIconFile = NULL; - - hr = XmlSelectSingleNode(pElement, L"Window", &pixn); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find window element."); - - hr = XmlGetYesNoAttribute(pixn, L"AutoResize", &pTheme->fAutoResize); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to get window AutoResize attribute."); - - hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Failed to find window Width attribute."); - } - ThmExitOnFailure(hr, "Failed to get window Width attribute."); - - pTheme->nWidth = pTheme->nDefaultDpiWidth = pTheme->nWindowWidth = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Failed to find window Height attribute."); - } - ThmExitOnFailure(hr, "Failed to get window Height attribute."); - - pTheme->nHeight = pTheme->nDefaultDpiHeight = pTheme->nWindowHeight = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", &dwValue); - if (S_FALSE == hr) - { - dwValue = 0; - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to get window MinimumWidth attribute."); - - pTheme->nMinimumWidth = pTheme->nDefaultDpiMinimumWidth = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", &dwValue); - if (S_FALSE == hr) - { - dwValue = 0; - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to get window MinimumHeight attribute."); - - pTheme->nMinimumHeight = pTheme->nDefaultDpiMinimumHeight = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"FontId", &pTheme->dwFontId); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Failed to find window FontId attribute."); - } - ThmExitOnFailure(hr, "Failed to get window FontId attribute."); - - // Get the optional window icon from a resource. - hr = XmlGetAttribute(pixn, L"IconResource", &bstr); - ThmExitOnFailure(hr, "Failed to get window IconResource attribute."); - - if (S_OK == hr) - { - pTheme->hIcon = ::LoadIconW(hModule, bstr); - ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconResource."); - - ReleaseNullBSTR(bstr); - } - - // Get the optional window icon from a file. - hr = XmlGetAttribute(pixn, L"IconFile", &bstr); - ThmExitOnFailure(hr, "Failed to get window IconFile attribute."); - - if (S_OK == hr) - { - if (wzRelativePath) - { - hr = PathConcat(wzRelativePath, bstr, &sczIconFile); - ThmExitOnFailure(hr, "Failed to combine icon file path."); - } - else - { - hr = PathRelativeToModule(&sczIconFile, bstr, hModule); - ThmExitOnFailure(hr, "Failed to get icon filename."); - } - - pTheme->hIcon = ::LoadImageW(NULL, sczIconFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE); - ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconFile: %ls.", bstr); - - ReleaseNullBSTR(bstr); - } - - hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast(&pTheme->nSourceX)); - if (S_FALSE == hr) - { - pTheme->nSourceX = -1; - } - ThmExitOnFailure(hr, "Failed to get window SourceX attribute."); - - hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast(&pTheme->nSourceY)); - if (S_FALSE == hr) - { - pTheme->nSourceY = -1; - } - ThmExitOnFailure(hr, "Failed to get window SourceY attribute."); - - // Parse the optional window style. - hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pTheme->dwStyle); - ThmExitOnFailure(hr, "Failed to get theme window style (Window@HexStyle) attribute."); - - if (S_FALSE == hr) - { - pTheme->dwStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU; - pTheme->dwStyle |= (0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY) ? WS_POPUP : WS_OVERLAPPED; - } - - hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast(&pTheme->uStringId)); - ThmExitOnFailure(hr, "Failed to get window StringId attribute."); - - if (S_FALSE == hr) - { - pTheme->uStringId = UINT_MAX; - - hr = XmlGetAttribute(pixn, L"Caption", &bstr); - ThmExitOnFailure(hr, "Failed to get window Caption attribute."); - - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Window elements must contain the Caption or StringId attribute."); - } - - hr = StrAllocString(&pTheme->sczCaption, bstr, 0); - ThmExitOnFailure(hr, "Failed to copy window Caption attribute."); - } - - // Parse any image lists. - hr = ParseImageLists(hModule, wzRelativePath, pixn, pTheme); - ThmExitOnFailure(hr, "Failed to parse image lists."); - - // Parse the pages. - hr = ParsePages(hModule, wzRelativePath, pixn, pTheme); - ThmExitOnFailure(hr, "Failed to parse theme pages."); - - // Parse the non-paged controls. - hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, NULL); - ThmExitOnFailure(hr, "Failed to parse theme controls."); - -LExit: - ReleaseStr(sczIconFile); - ReleaseBSTR(bstr); - ReleaseObject(pixn); - - return hr; -} - - -static HRESULT ParseFonts( - __in IXMLDOMElement* pElement, - __in THEME* pTheme - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixn = NULL; - BSTR bstrName = NULL; - DWORD dwId = 0; - COLORREF crForeground = THEME_INVISIBLE_COLORREF; - COLORREF crBackground = THEME_INVISIBLE_COLORREF; - DWORD dwSystemForegroundColor = FALSE; - DWORD dwSystemBackgroundColor = FALSE; - - hr = XmlSelectNodes(pElement, L"Font", &pixnl); - ThmExitOnFailure(hr, "Failed to find font elements."); - - hr = pixnl->get_length(reinterpret_cast(&pTheme->cFonts)); - ThmExitOnFailure(hr, "Failed to count the number of theme fonts."); - - if (!pTheme->cFonts) - { - ExitFunction1(hr = S_OK); - } - - pTheme->rgFonts = static_cast(MemAlloc(sizeof(THEME_FONT) * pTheme->cFonts, TRUE)); - ThmExitOnNull(pTheme->rgFonts, hr, E_OUTOFMEMORY, "Failed to allocate theme fonts."); - - while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) - { - hr = XmlGetAttributeNumber(pixn, L"Id", &dwId); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find font id."); - - if (pTheme->cFonts <= dwId) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Invalid theme font id."); - } - - THEME_FONT* pFont = pTheme->rgFonts + dwId; - if (pFont->cFontInstances) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Theme font id duplicated."); - } - - pFont->lfQuality = CLEARTYPE_QUALITY; - - hr = XmlGetText(pixn, &bstrName); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to get font name."); - - hr = StrAllocString(&pFont->sczFaceName, bstrName, 0); - ThmExitOnFailure(hr, "Failed to copy font name."); - - hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pFont->lfHeight)); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find font height attribute."); - - hr = XmlGetAttributeNumber(pixn, L"Weight", reinterpret_cast(&pFont->lfWeight)); - if (S_FALSE == hr) - { - pFont->lfWeight = FW_DONTCARE; - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to find font weight attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"Underline", reinterpret_cast(&pFont->lfUnderline)); - if (E_NOTFOUND == hr) - { - pFont->lfUnderline = FALSE; - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to find font underline attribute."); - - hr = GetFontColor(pixn, L"Foreground", &crForeground, &dwSystemForegroundColor); - ThmExitOnFailure(hr, "Failed to find font foreground color."); - - hr = GetFontColor(pixn, L"Background", &crBackground, &dwSystemBackgroundColor); - ThmExitOnFailure(hr, "Failed to find font background color."); - - pFont->crForeground = crForeground; - if (THEME_INVISIBLE_COLORREF != pFont->crForeground) - { - pFont->hForeground = dwSystemForegroundColor ? ::GetSysColorBrush(dwSystemForegroundColor) : ::CreateSolidBrush(pFont->crForeground); - ThmExitOnNull(pFont->hForeground, hr, E_OUTOFMEMORY, "Failed to create text foreground brush."); - } - - pFont->crBackground = crBackground; - if (THEME_INVISIBLE_COLORREF != pFont->crBackground) - { - pFont->hBackground = dwSystemBackgroundColor ? ::GetSysColorBrush(dwSystemBackgroundColor) : ::CreateSolidBrush(pFont->crBackground); - ThmExitOnNull(pFont->hBackground, hr, E_OUTOFMEMORY, "Failed to create text background brush."); - } - - ReleaseNullBSTR(bstrName); - ReleaseNullObject(pixn); - } - ThmExitOnFailure(hr, "Failed to enumerate all fonts."); - - if (S_FALSE == hr) - { - hr = S_OK; - } - -LExit: - ReleaseBSTR(bstrName); - ReleaseObject(pixn); - ReleaseObject(pixnl); - - return hr; -} - - -static HRESULT GetFontColor( - __in IXMLDOMNode* pixn, - __in_z LPCWSTR wzAttributeName, - __out COLORREF* pColorRef, - __out DWORD* pdwSystemColor - ) -{ - HRESULT hr = S_OK; - BSTR bstr = NULL; - - *pdwSystemColor = 0; - - hr = XmlGetAttribute(pixn, wzAttributeName, &bstr); - if (S_FALSE == hr) - { - *pColorRef = THEME_INVISIBLE_COLORREF; - ExitFunction1(hr = S_OK); - } - ThmExitOnFailure(hr, "Failed to find font %ls color.", wzAttributeName); - - if (pdwSystemColor) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btnface", -1)) - { - *pdwSystemColor = COLOR_BTNFACE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btntext", -1)) - { - *pdwSystemColor = COLOR_BTNTEXT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"graytext", -1)) - { - *pdwSystemColor = COLOR_GRAYTEXT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlight", -1)) - { - *pdwSystemColor = COLOR_HIGHLIGHT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlighttext", -1)) - { - *pdwSystemColor = COLOR_HIGHLIGHTTEXT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"hotlight", -1)) - { - *pdwSystemColor = COLOR_HOTLIGHT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"window", -1)) - { - *pdwSystemColor = COLOR_WINDOW; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"windowtext", -1)) - { - *pdwSystemColor = COLOR_WINDOWTEXT; - } - else - { - *pColorRef = wcstoul(bstr, NULL, 16); - } - - if (*pdwSystemColor) - { - *pColorRef = ::GetSysColor(*pdwSystemColor); - } - } - -LExit: - ReleaseBSTR(bstr); - - return hr; -} - -static HRESULT ParsePages( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __in THEME* pTheme - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixn = NULL; - BSTR bstrType = NULL; - THEME_PAGE* pPage = NULL; - DWORD iPage = 0; - - hr = XmlSelectNodes(pElement, L"Page", &pixnl); - ThmExitOnFailure(hr, "Failed to find page elements."); - - hr = pixnl->get_length(reinterpret_cast(&pTheme->cPages)); - ThmExitOnFailure(hr, "Failed to count the number of theme pages."); - - if (!pTheme->cPages) - { - ExitFunction1(hr = S_OK); - } - - pTheme->rgPages = static_cast(MemAlloc(sizeof(THEME_PAGE) * pTheme->cPages, TRUE)); - ThmExitOnNull(pTheme->rgPages, hr, E_OUTOFMEMORY, "Failed to allocate theme pages."); - - while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType))) - { - pPage = pTheme->rgPages + iPage; - - pPage->wId = static_cast(iPage + 1); - - hr = XmlGetAttributeEx(pixn, L"Name", &pPage->sczName); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying page Name."); - - hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, pPage); - ThmExitOnFailure(hr, "Failed to parse page controls."); - - ++iPage; - - ReleaseNullBSTR(bstrType); - ReleaseNullObject(pixn); - } - ThmExitOnFailure(hr, "Failed to enumerate all pages."); - - if (S_FALSE == hr) - { - hr = S_OK; - } - -LExit: - ReleaseBSTR(bstrType); - ReleaseObject(pixn); - ReleaseObject(pixnl); - - return hr; -} - - -static HRESULT ParseImageLists( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __in THEME* pTheme - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnlImageLists = NULL; - IXMLDOMNode* pixnImageList = NULL; - IXMLDOMNodeList* pixnlImages = NULL; - IXMLDOMNode* pixnImage = NULL; - DWORD dwImageListIndex = 0; - DWORD dwImageCount = 0; - HBITMAP hBitmap = NULL; - BITMAP bm = { }; - BSTR bstr = NULL; - DWORD i = 0; - int iRetVal = 0; - - hr = XmlSelectNodes(pElement, L"ImageList", &pixnlImageLists); - ThmExitOnFailure(hr, "Failed to find ImageList elements."); - - hr = pixnlImageLists->get_length(reinterpret_cast(&pTheme->cImageLists)); - ThmExitOnFailure(hr, "Failed to count the number of image lists."); - - if (!pTheme->cImageLists) - { - ExitFunction1(hr = S_OK); - } - - pTheme->rgImageLists = static_cast(MemAlloc(sizeof(THEME_IMAGELIST) * pTheme->cImageLists, TRUE)); - ThmExitOnNull(pTheme->rgImageLists, hr, E_OUTOFMEMORY, "Failed to allocate theme image lists."); - - while (S_OK == (hr = XmlNextElement(pixnlImageLists, &pixnImageList, NULL))) - { - hr = XmlGetAttribute(pixnImageList, L"Name", &bstr); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find ImageList/@Name attribute."); - - hr = StrAllocString(&pTheme->rgImageLists[dwImageListIndex].sczName, bstr, 0); - ThmExitOnFailure(hr, "Failed to make copy of ImageList name."); - - hr = XmlSelectNodes(pixnImageList, L"Image", &pixnlImages); - ThmExitOnFailure(hr, "Failed to select child Image nodes."); - - hr = pixnlImages->get_length(reinterpret_cast(&dwImageCount)); - ThmExitOnFailure(hr, "Failed to count the number of images in list."); - - if (0 < dwImageCount) - { - i = 0; - while (S_OK == (hr = XmlNextElement(pixnlImages, &pixnImage, NULL))) - { - if (hBitmap) - { - ::DeleteObject(hBitmap); - hBitmap = NULL; - } - hr = ParseImage(hModule, wzRelativePath, pixnImage, &hBitmap); - ThmExitOnFailure(hr, "Failed to parse image: %u", i); - - if (0 == i) - { - ::GetObjectW(hBitmap, sizeof(BITMAP), &bm); - - pTheme->rgImageLists[dwImageListIndex].hImageList = ImageList_Create(bm.bmWidth, bm.bmHeight, ILC_COLOR24, dwImageCount, 0); - ThmExitOnNullWithLastError(pTheme->rgImageLists[dwImageListIndex].hImageList, hr, "Failed to create image list."); - } - - iRetVal = ImageList_Add(pTheme->rgImageLists[dwImageListIndex].hImageList, hBitmap, NULL); - if (-1 == iRetVal) - { - ThmExitWithLastError(hr, "Failed to add image %u to image list.", i); - } - - ++i; - } - } - ++dwImageListIndex; - } - -LExit: - if (hBitmap) - { - ::DeleteObject(hBitmap); - } - ReleaseBSTR(bstr); - ReleaseObject(pixnlImageLists); - ReleaseObject(pixnImageList); - ReleaseObject(pixnlImages); - ReleaseObject(pixnImage); - - return hr; -} - -static void GetControls( - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __out DWORD** ppcControls, - __out THEME_CONTROL*** pprgControls - ) -{ - if (pParentControl) - { - *ppcControls = &pParentControl->cControls; - *pprgControls = &pParentControl->rgControls; - } - else - { - *ppcControls = &pTheme->cControls; - *pprgControls = &pTheme->rgControls; - } -} - -static void GetControls( - __in const THEME* pTheme, - __in_opt const THEME_CONTROL* pParentControl, - __out DWORD& cControls, - __out THEME_CONTROL*& rgControls - ) -{ - if (pParentControl) - { - cControls = pParentControl->cControls; - rgControls = pParentControl->rgControls; - } - else - { - cControls = pTheme->cControls; - rgControls = pTheme->rgControls; - } -} - -static HRESULT ParseControls( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pElement, - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __in_opt THEME_PAGE* pPage - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixn = NULL; - BSTR bstrType = NULL; - DWORD cNewControls = 0; - DWORD iControl = 0; - DWORD iPageControl = 0; - DWORD* pcControls = NULL; - THEME_CONTROL** prgControls = NULL; - - GetControls(pTheme, pParentControl, &pcControls, &prgControls); - - hr = ParseRadioButtons(hModule, wzRelativePath, pElement, pTheme, pParentControl, pPage); - ThmExitOnFailure(hr, "Failed to parse radio buttons."); - - hr = XmlSelectNodes(pElement, L"Billboard|Button|Checkbox|Combobox|CommandLink|Editbox|Hyperlink|Hypertext|ImageControl|Label|ListView|Panel|Progressbar|Richedit|Static|Tabs|TreeView", &pixnl); - ThmExitOnFailure(hr, "Failed to find control elements."); - - hr = pixnl->get_length(reinterpret_cast(&cNewControls)); - ThmExitOnFailure(hr, "Failed to count the number of theme controls."); - - if (!cNewControls) - { - ExitFunction1(hr = S_OK); - } - - hr = MemReAllocArray(reinterpret_cast(prgControls), *pcControls, sizeof(THEME_CONTROL), cNewControls); - ThmExitOnFailure(hr, "Failed to reallocate theme controls."); - - cNewControls += *pcControls; - - if (pPage) - { - iPageControl = pPage->cControlIndices; - pPage->cControlIndices += cNewControls; - } - - iControl = *pcControls; - *pcControls = cNewControls; - - while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType))) - { - THEME_CONTROL_TYPE type = THEME_CONTROL_TYPE_UNKNOWN; - - if (!bstrType) - { - hr = E_UNEXPECTED; - ThmExitOnFailure(hr, "Null element encountered!"); - } - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Billboard", -1)) - { - type = THEME_CONTROL_TYPE_BILLBOARD; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Button", -1)) - { - type = THEME_CONTROL_TYPE_BUTTON; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Checkbox", -1)) - { - type = THEME_CONTROL_TYPE_CHECKBOX; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Combobox", -1)) - { - type = THEME_CONTROL_TYPE_COMBOBOX; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CommandLink", -1)) - { - type = THEME_CONTROL_TYPE_COMMANDLINK; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Editbox", -1)) - { - type = THEME_CONTROL_TYPE_EDITBOX; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hyperlink", -1)) - { - type = THEME_CONTROL_TYPE_HYPERLINK; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hypertext", -1)) - { - type = THEME_CONTROL_TYPE_HYPERTEXT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ImageControl", -1)) - { - type = THEME_CONTROL_TYPE_IMAGE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Label", -1)) - { - type = THEME_CONTROL_TYPE_LABEL; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ListView", -1)) - { - type = THEME_CONTROL_TYPE_LISTVIEW; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Panel", -1)) - { - type = THEME_CONTROL_TYPE_PANEL; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Progressbar", -1)) - { - type = THEME_CONTROL_TYPE_PROGRESSBAR; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Richedit", -1)) - { - type = THEME_CONTROL_TYPE_RICHEDIT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Static", -1)) - { - type = THEME_CONTROL_TYPE_STATIC; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Tabs", -1)) - { - type = THEME_CONTROL_TYPE_TAB; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"TreeView", -1)) - { - type = THEME_CONTROL_TYPE_TREEVIEW; - } - - if (THEME_CONTROL_TYPE_UNKNOWN != type) - { - THEME_CONTROL* pControl = *prgControls + iControl; - pControl->type = type; - - // billboard children are always the size of the billboard - BOOL fBillboardSizing = pParentControl && THEME_CONTROL_TYPE_BILLBOARD == pParentControl->type; - - hr = ParseControl(hModule, wzRelativePath, pixn, pTheme, pControl, fBillboardSizing, pPage); - ThmExitOnFailure(hr, "Failed to parse control."); - - if (fBillboardSizing) - { - pControl->nX = pControl->nDefaultDpiX = 0; - pControl->nY = pControl->nDefaultDpiY = 0; - pControl->nWidth = pControl->nDefaultDpiWidth = 0; - pControl->nHeight = pControl->nDefaultDpiHeight = 0; - } - - if (pPage) - { - pControl->wPageId = pPage->wId; - ++iPageControl; - } - - ++iControl; - } - - ReleaseNullBSTR(bstrType); - ReleaseNullObject(pixn); - } - ThmExitOnFailure(hr, "Failed to enumerate all controls."); - - if (S_FALSE == hr) - { - hr = S_OK; - } - - AssertSz(iControl == cNewControls, "The number of parsed controls didn't match the number of expected controls."); - -LExit: - ReleaseBSTR(bstrType); - ReleaseObject(pixn); - ReleaseObject(pixnl); - - return hr; -} - - -static HRESULT ParseControl( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pixn, - __in THEME* pTheme, - __in THEME_CONTROL* pControl, - __in BOOL fSkipDimensions, - __in_opt THEME_PAGE* pPage - ) -{ - HRESULT hr = S_OK; - DWORD dwValue = 0; - BOOL fValue = FALSE; - BSTR bstrText = NULL; - BOOL fAnyTextChildren = FALSE; - BOOL fAnyNoteChildren = FALSE; - - hr = XmlGetAttributeEx(pixn, L"Name", &pControl->sczName); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying control Name attribute."); - - hr = XmlGetAttributeEx(pixn, L"EnableCondition", &pControl->sczEnableCondition); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying control EnableCondition attribute."); - - hr = XmlGetAttributeEx(pixn, L"VisibleCondition", &pControl->sczVisibleCondition); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying control VisibleCondition attribute."); - - if (!fSkipDimensions) - { - hr = XmlGetAttributeNumber(pixn, L"X", &dwValue); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find control X attribute."); - - pControl->nX = pControl->nDefaultDpiX = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"Y", &dwValue); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find control Y attribute."); - - pControl->nY = pControl->nDefaultDpiY = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find control Height attribute."); - - pControl->nHeight = pControl->nDefaultDpiHeight = dwValue; - - hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - ThmExitOnFailure(hr, "Failed to find control Width attribute."); - - pControl->nWidth = pControl->nDefaultDpiWidth = dwValue; - } - - // Parse the optional background resource image. - hr = ParseImage(hModule, wzRelativePath, pixn, &pControl->hImage); - ThmExitOnFailure(hr, "Failed while parsing control image."); - - hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast(&pControl->nSourceX)); - if (S_FALSE == hr) - { - pControl->nSourceX = -1; - } - ThmExitOnFailure(hr, "Failed when querying control SourceX attribute."); - - hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast(&pControl->nSourceY)); - if (S_FALSE == hr) - { - pControl->nSourceY = -1; - } - ThmExitOnFailure(hr, "Failed when querying control SourceY attribute."); - - hr = XmlGetAttributeNumber(pixn, L"FontId", &pControl->dwFontId); - if (S_FALSE == hr) - { - pControl->dwFontId = THEME_INVALID_ID; - } - ThmExitOnFailure(hr, "Failed when querying control FontId attribute."); - - // Parse the optional window style. - hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pControl->dwStyle); - ThmExitOnFailure(hr, "Failed when querying control HexStyle attribute."); - - // Parse the tabstop bit "shortcut nomenclature", this could have been set with the style above. - hr = XmlGetYesNoAttribute(pixn, L"TabStop", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else - { - ThmExitOnFailure(hr, "Failed when querying control TabStop attribute."); - - if (fValue) - { - pControl->dwStyle |= WS_TABSTOP; - } - } - - hr = XmlGetYesNoAttribute(pixn, L"Visible", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else - { - ThmExitOnFailure(hr, "Failed when querying control Visible attribute."); - - if (fValue) - { - pControl->dwStyle |= WS_VISIBLE; - } - } - - hr = XmlGetYesNoAttribute(pixn, L"HideWhenDisabled", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else - { - ThmExitOnFailure(hr, "Failed when querying control HideWhenDisabled attribute."); - - if (fValue) - { - pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED; - } - } - - hr = XmlGetYesNoAttribute(pixn, L"DisableAutomaticBehavior", &pControl->fDisableVariableFunctionality); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else - { - ThmExitOnFailure(hr, "Failed when querying control DisableAutomaticBehavior attribute."); - } - - hr = ParseActions(pixn, pControl); - ThmExitOnFailure(hr, "Failed to parse action nodes of the control."); - - hr = ParseText(pixn, pControl, &fAnyTextChildren); - ThmExitOnFailure(hr, "Failed to parse text nodes of the control."); - - hr = ParseTooltips(pixn, pControl, &fAnyTextChildren); - ThmExitOnFailure(hr, "Failed to parse control Tooltip."); - - if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) - { - hr = ParseNotes(pixn, pControl, &fAnyNoteChildren); - ThmExitOnFailure(hr, "Failed to parse note text nodes of the control."); - } - - if (fAnyTextChildren || fAnyNoteChildren) - { - pControl->uStringId = UINT_MAX; - } - else - { - hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast(&pControl->uStringId)); - ThmExitOnFailure(hr, "Failed when querying control StringId attribute."); - - if (S_FALSE == hr) - { - pControl->uStringId = UINT_MAX; - - if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type || THEME_CONTROL_TYPE_PANEL == pControl->type) - { - // Billboards and panels have child elements and we don't want to pick up child element text in the parents. - hr = S_OK; - } - else - { - hr = XmlGetText(pixn, &bstrText); - ThmExitOnFailure(hr, "Failed to get control inner text."); - - if (S_OK == hr) - { - hr = StrAllocString(&pControl->sczText, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy control text."); - - ReleaseNullBSTR(bstrText); - } - else if (S_FALSE == hr) - { - hr = S_OK; - } - } - } - } - - if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type) - { - hr = XmlGetYesNoAttribute(pixn, L"Loop", &pControl->fBillboardLoops); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying Billboard/@Loop attribute."); - - pControl->wBillboardInterval = 5000; - hr = XmlGetAttributeNumber(pixn, L"Interval", &dwValue); - if (S_OK == hr && dwValue) - { - pControl->wBillboardInterval = static_cast(dwValue & 0xFFFF); - } - ThmExitOnFailure(hr, "Failed when querying Billboard/@Interval attribute."); - - hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage); - ThmExitOnFailure(hr, "Failed to parse billboard children."); - } - else if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) - { - hr = ParseIcon(hModule, wzRelativePath, pixn, &pControl->hIcon); - ThmExitOnFailure(hr, "Failed while parsing control icon."); - } - else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type) - { - hr = XmlGetYesNoAttribute(pixn, L"FileSystemAutoComplete", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else - { - ThmExitOnFailure(hr, "Failed when querying Editbox/@FileSystemAutoComplete attribute."); - - if (fValue) - { - pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE; - } - } - } - else if (THEME_CONTROL_TYPE_HYPERLINK == pControl->type || THEME_CONTROL_TYPE_BUTTON == pControl->type) - { - hr = XmlGetAttributeNumber(pixn, L"HoverFontId", &pControl->dwFontHoverId); - if (S_FALSE == hr) - { - pControl->dwFontHoverId = THEME_INVALID_ID; - } - ThmExitOnFailure(hr, "Failed when querying control HoverFontId attribute."); - - hr = XmlGetAttributeNumber(pixn, L"SelectedFontId", &pControl->dwFontSelectedId); - if (S_FALSE == hr) - { - pControl->dwFontSelectedId = THEME_INVALID_ID; - } - ThmExitOnFailure(hr, "Failed when querying control SelectedFontId attribute."); - } - else if (THEME_CONTROL_TYPE_LABEL == pControl->type) - { - hr = XmlGetYesNoAttribute(pixn, L"Center", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= SS_CENTER; - } - ThmExitOnFailure(hr, "Failed when querying Label/@Center attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"DisablePrefix", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= SS_NOPREFIX; - } - ThmExitOnFailure(hr, "Failed when querying Label/@DisablePrefix attribute."); - } - else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) - { - // Parse the optional extended window style. - hr = XmlGetAttributeNumberBase(pixn, L"HexExtendedStyle", 16, &pControl->dwExtendedStyle); - ThmExitOnFailure(hr, "Failed when querying ListView/@HexExtendedStyle attribute."); - - hr = XmlGetAttribute(pixn, L"ImageList", &bstrText); - if (S_FALSE != hr) - { - ThmExitOnFailure(hr, "Failed when querying ListView/@ImageList attribute."); - - hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[0]); - ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageList for ListView.", bstrText); - } - - hr = XmlGetAttribute(pixn, L"ImageListSmall", &bstrText); - if (S_FALSE != hr) - { - ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListSmall attribute."); - - hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[1]); - ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListSmall for ListView.", bstrText); - } - - hr = XmlGetAttribute(pixn, L"ImageListState", &bstrText); - if (S_FALSE != hr) - { - ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListState attribute."); - - hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[2]); - ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListState for ListView.", bstrText); - } - - hr = XmlGetAttribute(pixn, L"ImageListGroupHeader", &bstrText); - if (S_FALSE != hr) - { - ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListGroupHeader attribute."); - - hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[3]); - ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListGroupHeader for ListView.", bstrText); - } - - hr = ParseColumns(pixn, pControl); - ThmExitOnFailure(hr, "Failed to parse columns."); - } - else if (THEME_CONTROL_TYPE_PANEL == pControl->type) - { - hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage); - ThmExitOnFailure(hr, "Failed to parse panel children."); - } - else if (THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type) - { - hr = XmlGetAttributeEx(pixn, L"Value", &pControl->sczValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying RadioButton/@Value attribute."); - } - else if (THEME_CONTROL_TYPE_TAB == pControl->type) - { - hr = ParseTabs(pixn, pControl); - ThmExitOnFailure(hr, "Failed to parse tabs"); - } - else if (THEME_CONTROL_TYPE_TREEVIEW == pControl->type) - { - pControl->dwStyle |= TVS_DISABLEDRAGDROP; - - hr = XmlGetYesNoAttribute(pixn, L"EnableDragDrop", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle &= ~TVS_DISABLEDRAGDROP; - } - ThmExitOnFailure(hr, "Failed when querying TreeView/@EnableDragDrop attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"FullRowSelect", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= TVS_FULLROWSELECT; - } - ThmExitOnFailure(hr, "Failed when querying TreeView/@FullRowSelect attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"HasButtons", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= TVS_HASBUTTONS; - } - ThmExitOnFailure(hr, "Failed when querying TreeView/@HasButtons attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"AlwaysShowSelect", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= TVS_SHOWSELALWAYS; - } - ThmExitOnFailure(hr, "Failed when querying TreeView/@AlwaysShowSelect attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"LinesAtRoot", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= TVS_LINESATROOT; - } - ThmExitOnFailure(hr, "Failed when querying TreeView/@LinesAtRoot attribute."); - - hr = XmlGetYesNoAttribute(pixn, L"HasLines", &fValue); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (fValue) - { - pControl->dwStyle |= TVS_HASLINES; - } - ThmExitOnFailure(hr, "Failed when querying TreeView/@HasLines attribute."); - } - -LExit: - ReleaseBSTR(bstrText); - - return hr; -} - - -static HRESULT ParseActions( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixnChild = NULL; - BSTR bstrType = NULL; - - hr = XmlSelectNodes(pixn, L"BrowseDirectoryAction|ChangePageAction|CloseWindowAction", &pixnl); - ThmExitOnFailure(hr, "Failed to select child action nodes."); - - hr = pixnl->get_length(reinterpret_cast(&pControl->cActions)); - ThmExitOnFailure(hr, "Failed to count the number of action nodes."); - - if (0 < pControl->cActions) - { - MemAllocArray(reinterpret_cast(&pControl->rgActions), sizeof(THEME_ACTION), pControl->cActions); - ThmExitOnNull(pControl->rgActions, hr, E_OUTOFMEMORY, "Failed to allocate THEME_ACTION structs."); - - i = 0; - while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, &bstrType))) - { - if (!bstrType) - { - hr = E_UNEXPECTED; - ThmExitOnFailure(hr, "Null element encountered!"); - } - - THEME_ACTION* pAction = pControl->rgActions + i; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"BrowseDirectoryAction", -1)) - { - pAction->type = THEME_ACTION_TYPE_BROWSE_DIRECTORY; - - hr = XmlGetAttributeEx(pixnChild, L"VariableName", &pAction->BrowseDirectory.sczVariableName); - ThmExitOnFailure(hr, "Failed when querying BrowseDirectoryAction/@VariableName attribute."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ChangePageAction", -1)) - { - pAction->type = THEME_ACTION_TYPE_CHANGE_PAGE; - - hr = XmlGetAttributeEx(pixnChild, L"Page", &pAction->ChangePage.sczPageName); - ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Page attribute."); - - hr = XmlGetYesNoAttribute(pixnChild, L"Cancel", &pAction->ChangePage.fCancel); - if (E_NOTFOUND != hr) - { - ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Cancel attribute."); - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CloseWindowAction", -1)) - { - pAction->type = THEME_ACTION_TYPE_CLOSE_WINDOW; - } - else - { - hr = E_UNEXPECTED; - ThmExitOnFailure(hr, "Unexpected element encountered: %ls", bstrType); - } - - hr = XmlGetAttributeEx(pixnChild, L"Condition", &pAction->sczCondition); - if (E_NOTFOUND != hr) - { - ThmExitOnFailure(hr, "Failed when querying %ls/@Condition attribute.", bstrType); - } - - if (!pAction->sczCondition) - { - if (pControl->pDefaultAction) - { - hr = E_INVALIDDATA; - ThmExitOnFailure(hr, "Control '%ls' has multiple actions without a condition.", pControl->sczName); - } - - pControl->pDefaultAction = pAction; - } - - ++i; - ReleaseNullBSTR(bstrType); - ReleaseObject(pixnChild); - } - } - -LExit: - ReleaseObject(pixnl); - ReleaseObject(pixnChild); - ReleaseBSTR(bstrType); - - return hr; -} - - -static HRESULT ParseColumns( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixnChild = NULL; - BSTR bstrText = NULL; - DWORD dwValue = 0; - - hr = XmlSelectNodes(pixn, L"Column", &pixnl); - ThmExitOnFailure(hr, "Failed to select child column nodes."); - - hr = pixnl->get_length(reinterpret_cast(&pControl->cColumns)); - ThmExitOnFailure(hr, "Failed to count the number of control columns."); - - if (0 < pControl->cColumns) - { - hr = MemAllocArray(reinterpret_cast(&pControl->ptcColumns), sizeof(THEME_COLUMN), pControl->cColumns); - ThmExitOnFailure(hr, "Failed to allocate column structs."); - - i = 0; - while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) - { - THEME_COLUMN* pColumn = pControl->ptcColumns + i; - - hr = XmlGetText(pixnChild, &bstrText); - ThmExitOnFailure(hr, "Failed to get inner text of column element."); - - hr = XmlGetAttributeNumber(pixnChild, L"Width", &dwValue); - if (S_FALSE == hr) - { - dwValue = 100; - } - ThmExitOnFailure(hr, "Failed to get column width attribute."); - - pColumn->nBaseWidth = pColumn->nDefaultDpiBaseWidth = dwValue; - - hr = XmlGetYesNoAttribute(pixnChild, L"Expands", reinterpret_cast(&pColumn->fExpands)); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to get expands attribute."); - - hr = StrAllocString(&pColumn->pszName, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy column name."); - - ++i; - ReleaseNullBSTR(bstrText); - } - } - -LExit: - ReleaseObject(pixnl); - ReleaseObject(pixnChild); - ReleaseBSTR(bstrText); - - return hr; -} - - -static HRESULT ParseRadioButtons( - __in_opt HMODULE hModule, - __in_opt LPCWSTR wzRelativePath, - __in IXMLDOMNode* pixn, - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __in THEME_PAGE* pPage - ) -{ - HRESULT hr = S_OK; - DWORD cRadioButtons = 0; - DWORD iControl = 0; - DWORD iPageControl = 0; - IXMLDOMNodeList* pixnlRadioButtons = NULL; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixnRadioButtons = NULL; - IXMLDOMNode* pixnChild = NULL; - LPWSTR sczName = NULL; - THEME_CONTROL* pControl = NULL; - BOOL fFirst = FALSE; - DWORD* pcControls = NULL; - THEME_CONTROL** prgControls = NULL; - - GetControls(pTheme, pParentControl, &pcControls, &prgControls); - - hr = XmlSelectNodes(pixn, L"RadioButtons", &pixnlRadioButtons); - ThmExitOnFailure(hr, "Failed to select RadioButtons nodes."); - - while (S_OK == (hr = XmlNextElement(pixnlRadioButtons, &pixnRadioButtons, NULL))) - { - hr = XmlGetAttributeEx(pixnRadioButtons, L"Name", &sczName); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying RadioButtons Name."); - - hr = XmlSelectNodes(pixnRadioButtons, L"RadioButton", &pixnl); - ThmExitOnFailure(hr, "Failed to select RadioButton nodes."); - - hr = pixnl->get_length(reinterpret_cast(&cRadioButtons)); - ThmExitOnFailure(hr, "Failed to count the number of RadioButton nodes."); - - if (cRadioButtons) - { - if (pPage) - { - iPageControl = pPage->cControlIndices; - pPage->cControlIndices += cRadioButtons; - } - - hr = MemReAllocArray(reinterpret_cast(prgControls), *pcControls, sizeof(THEME_CONTROL), cRadioButtons); - ThmExitOnFailure(hr, "Failed to reallocate theme controls."); - - iControl = *pcControls; - *pcControls += cRadioButtons; - - fFirst = TRUE; - - while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) - { - pControl = *prgControls + iControl; - pControl->type = THEME_CONTROL_TYPE_RADIOBUTTON; - - hr = ParseControl(hModule, wzRelativePath, pixnChild, pTheme, pControl, FALSE, pPage); - ThmExitOnFailure(hr, "Failed to parse control."); - - if (fFirst) - { - pControl->dwStyle |= WS_GROUP; - fFirst = FALSE; - } - - hr = StrAllocString(&pControl->sczVariable, sczName, 0); - ThmExitOnFailure(hr, "Failed to copy radio button variable."); - - if (pPage) - { - pControl->wPageId = pPage->wId; - ++iPageControl; - } - - ++iControl; - } - - if (!fFirst) - { - pControl->fLastRadioButton = TRUE; - } - } - } - -LExit: - ReleaseStr(sczName); - ReleaseObject(pixnl); - ReleaseObject(pixnChild); - ReleaseObject(pixnlRadioButtons); - ReleaseObject(pixnRadioButtons); - - return hr; -} - - -static HRESULT ParseTabs( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixnChild = NULL; - BSTR bstrText = NULL; - - hr = XmlSelectNodes(pixn, L"Tab", &pixnl); - ThmExitOnFailure(hr, "Failed to select child tab nodes."); - - hr = pixnl->get_length(reinterpret_cast(&pControl->cTabs)); - ThmExitOnFailure(hr, "Failed to count the number of tabs."); - - if (0 < pControl->cTabs) - { - hr = MemAllocArray(reinterpret_cast(&pControl->pttTabs), sizeof(THEME_TAB), pControl->cTabs); - ThmExitOnFailure(hr, "Failed to allocate tab structs."); - - i = 0; - while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) - { - hr = XmlGetText(pixnChild, &bstrText); - ThmExitOnFailure(hr, "Failed to get inner text of tab element."); - - hr = StrAllocString(&(pControl->pttTabs[i].pszName), bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy tab name."); - - ++i; - ReleaseNullBSTR(bstrText); - } - } - -LExit: - ReleaseObject(pixnl); - ReleaseObject(pixnChild); - ReleaseBSTR(bstrText); - - return hr; -} - - -static HRESULT ParseText( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl, - __inout BOOL* pfAnyChildren - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixnChild = NULL; - BSTR bstrText = NULL; - - hr = XmlSelectNodes(pixn, L"Text", &pixnl); - ThmExitOnFailure(hr, "Failed to select child Text nodes."); - - hr = pixnl->get_length(reinterpret_cast(&pControl->cConditionalText)); - ThmExitOnFailure(hr, "Failed to count the number of Text nodes."); - - *pfAnyChildren |= 0 < pControl->cConditionalText; - - if (0 < pControl->cConditionalText) - { - MemAllocArray(reinterpret_cast(&pControl->rgConditionalText), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalText); - ThmExitOnNull(pControl->rgConditionalText, hr, E_OUTOFMEMORY, "Failed to allocate THEME_CONDITIONAL_TEXT structs."); - - i = 0; - while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) - { - THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + i; - - hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalText->sczCondition); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying Text/@Condition attribute."); - - hr = XmlGetText(pixnChild, &bstrText); - ThmExitOnFailure(hr, "Failed to get inner text of Text element."); - - if (S_OK == hr) - { - if (pConditionalText->sczCondition) - { - hr = StrAllocString(&pConditionalText->sczText, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy text to conditional text."); - - ++i; - } - else - { - if (pControl->sczText) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnFailure(hr, "Unconditional text for the '%ls' control is specified multiple times.", pControl->sczName); - } - - hr = StrAllocString(&pControl->sczText, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy text to control."); - - // Unconditional text entries aren't stored in the conditional text list. - --pControl->cConditionalText; - } - } - - ReleaseNullBSTR(bstrText); - } - } - -LExit: - ReleaseObject(pixnl); - ReleaseObject(pixnChild); - ReleaseBSTR(bstrText); - - return hr; -} - - -static HRESULT ParseTooltips( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl, - __inout BOOL* pfAnyChildren -) -{ - HRESULT hr = S_OK; - IXMLDOMNode* pixnChild = NULL; - BSTR bstrText = NULL; - - hr = XmlSelectSingleNode(pixn, L"Tooltip", &pixnChild); - ThmExitOnFailure(hr, "Failed to select child Tooltip node."); - - if (S_OK == hr) - { - *pfAnyChildren |= TRUE; - - hr = XmlGetText(pixnChild, &bstrText); - ThmExitOnFailure(hr, "Failed to get inner text of Tooltip element."); - - if (S_OK == hr) - { - hr = StrAllocString(&pControl->sczTooltip, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy tooltip text to control."); - } - } - -LExit: - ReleaseObject(pixnChild); - ReleaseBSTR(bstrText); - - return hr; -} - - -static HRESULT ParseNotes( - __in IXMLDOMNode* pixn, - __in THEME_CONTROL* pControl, - __out BOOL* pfAnyChildren - ) -{ - HRESULT hr = S_OK; - DWORD i = 0; - IXMLDOMNodeList* pixnl = NULL; - IXMLDOMNode* pixnChild = NULL; - BSTR bstrText = NULL; - - hr = XmlSelectNodes(pixn, L"Note", &pixnl); - ThmExitOnFailure(hr, "Failed to select child Note nodes."); - - hr = pixnl->get_length(reinterpret_cast(&pControl->cConditionalNotes)); - ThmExitOnFailure(hr, "Failed to count the number of Note nodes."); - - if (pfAnyChildren) - { - *pfAnyChildren = 0 < pControl->cConditionalNotes; - } - - if (0 < pControl->cConditionalNotes) - { - MemAllocArray(reinterpret_cast(&pControl->rgConditionalNotes), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalNotes); - ThmExitOnNull(pControl->rgConditionalNotes, hr, E_OUTOFMEMORY, "Failed to allocate note THEME_CONDITIONAL_TEXT structs."); - - i = 0; - while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) - { - THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + i; - - hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalNote->sczCondition); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed when querying Note/@Condition attribute."); - - hr = XmlGetText(pixnChild, &bstrText); - ThmExitOnFailure(hr, "Failed to get inner text of Note element."); - - if (S_OK == hr) - { - if (pConditionalNote->sczCondition) - { - hr = StrAllocString(&pConditionalNote->sczText, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy text to conditional note text."); - - ++i; - } - else - { - if (pControl->sczNote) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnFailure(hr, "Unconditional note text for the '%ls' control is specified multiple times.", pControl->sczName); - } - - hr = StrAllocString(&pControl->sczNote, bstrText, 0); - ThmExitOnFailure(hr, "Failed to copy text to command link control."); - - // Unconditional note entries aren't stored in the conditional notes list. - --pControl->cConditionalNotes; - } - } - - ReleaseNullBSTR(bstrText); - } - } - -LExit: - ReleaseObject(pixnl); - ReleaseObject(pixnChild); - ReleaseBSTR(bstrText); - - return hr; -} - - -static HRESULT StartBillboard( - __in THEME* pTheme, - __in DWORD dwControl - ) -{ - HRESULT hr = E_NOTFOUND; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - - if (hWnd) - { - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); - if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type) - { - // kick off - pControl->dwData = 0; - OnBillboardTimer(pTheme, pTheme->hwndParent, dwControl); - - if (!::SetTimer(pTheme->hwndParent, pControl->wId, pControl->wBillboardInterval, NULL)) - { - ThmExitWithLastError(hr, "Failed to start billboard."); - } - - hr = S_OK; - } - } - -LExit: - return hr; -} - - -static HRESULT StopBillboard( - __in THEME* pTheme, - __in DWORD dwControl - ) -{ - HRESULT hr = E_NOTFOUND; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - - if (hWnd) - { - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); - if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type) - { - ThemeControlEnable(pTheme, dwControl, FALSE); - - if (::KillTimer(pTheme->hwndParent, pControl->wId)) - { - hr = S_OK; - } - } - } - - return hr; -} - -static HRESULT EnsureFontInstance( - __in THEME* pTheme, - __in THEME_FONT* pFont, - __out THEME_FONT_INSTANCE** ppFontInstance - ) -{ - HRESULT hr = S_OK; - THEME_FONT_INSTANCE* pFontInstance = NULL; - LOGFONTW lf = { }; - - for (DWORD i = 0; i < pFont->cFontInstances; ++i) - { - pFontInstance = pFont->rgFontInstances + i; - if (pTheme->nDpi == pFontInstance->nDpi) - { - *ppFontInstance = pFontInstance; - ExitFunction(); - } - } - - hr = MemEnsureArraySize(reinterpret_cast(&pFont->rgFontInstances), pFont->cFontInstances, sizeof(THEME_FONT_INSTANCE), GROW_FONT_INSTANCES); - ThmExitOnFailure(hr, "Failed to allocate memory for font instances."); - - pFontInstance = pFont->rgFontInstances + pFont->cFontInstances; - pFontInstance->nDpi = pTheme->nDpi; - - lf.lfHeight = DpiuScaleValue(pFont->lfHeight, pFontInstance->nDpi); - lf.lfWeight = pFont->lfWeight; - lf.lfUnderline = pFont->lfUnderline; - lf.lfQuality = pFont->lfQuality; - - hr = ::StringCchCopyW(lf.lfFaceName, countof(lf.lfFaceName), pFont->sczFaceName); - ThmExitOnFailure(hr, "Failed to copy font name to create font."); - - pFontInstance->hFont = ::CreateFontIndirectW(&lf); - ThmExitOnNull(pFontInstance->hFont, hr, E_OUTOFMEMORY, "Failed to create DPI specific font."); - - ++pFont->cFontInstances; - *ppFontInstance = pFontInstance; - -LExit: - return hr; -} - - -static HRESULT FindImageList( - __in THEME* pTheme, - __in_z LPCWSTR wzImageListName, - __out HIMAGELIST *phImageList - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < pTheme->cImageLists; ++i) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pTheme->rgImageLists[i].sczName, -1, wzImageListName, -1)) - { - *phImageList = pTheme->rgImageLists[i].hImageList; - ExitFunction1(hr = S_OK); - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - - -static HRESULT DrawButton( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ) -{ - int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; - int nSourceY = pControl->hImage ? 0 : pControl->nSourceY; - DWORD dwSourceWidth = pControl->nDefaultDpiWidth; - DWORD dwSourceHeight = pControl->nDefaultDpiHeight; - - HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); - HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); - - DWORD_PTR dwStyle = ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE); - // "clicked" gets priority - if (ODS_SELECTED & pdis->itemState) - { - nSourceY += pControl->nDefaultDpiHeight * 2; - } - // then hover - else if (pControl->dwData & THEME_CONTROL_DATA_HOVER) - { - nSourceY += pControl->nDefaultDpiHeight; - } - // then focused - else if (WS_TABSTOP & dwStyle && ODS_FOCUS & pdis->itemState) - { - nSourceY += pControl->nDefaultDpiHeight * 3; - } - - ::StretchBlt(pdis->hDC, 0, 0, pControl->nWidth, pControl->nHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); - - ::SelectObject(hdcMem, hDefaultBitmap); - ::DeleteDC(hdcMem); - - DrawControlText(pTheme, pdis, pControl, TRUE, FALSE); - - return S_OK; -} - - -static HRESULT DrawHyperlink( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ) -{ - DrawControlText(pTheme, pdis, pControl, FALSE, TRUE); - return S_OK; -} - - -static void DrawControlText( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl, - __in BOOL fCentered, - __in BOOL fDrawFocusRect - ) -{ - HRESULT hr = S_OK; - WCHAR wzText[256] = { }; - DWORD cchText = 0; - THEME_FONT* pFont = NULL; - THEME_FONT_INSTANCE* pFontInstance = NULL; - HFONT hfPrev = NULL; - - if (0 == (cchText = ::GetWindowTextW(pdis->hwndItem, wzText, countof(wzText)))) - { - // nothing to do - return; - } - - if (ODS_SELECTED & pdis->itemState) - { - pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontSelectedId ? pControl->dwFontSelectedId : pControl->dwFontId); - } - else if (pControl->dwData & THEME_CONTROL_DATA_HOVER) - { - pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontHoverId ? pControl->dwFontHoverId : pControl->dwFontId); - } - else - { - pFont = pTheme->rgFonts + pControl->dwFontId; - } - - hr = EnsureFontInstance(pTheme, pFont, &pFontInstance); - if (SUCCEEDED(hr)) - { - hfPrev = SelectFont(pdis->hDC, pFontInstance->hFont); - } - - ::DrawTextExW(pdis->hDC, wzText, cchText, &pdis->rcItem, DT_SINGLELINE | (fCentered ? (DT_CENTER | DT_VCENTER) : 0), NULL); - - if (fDrawFocusRect && (WS_TABSTOP & ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE)) && (ODS_FOCUS & pdis->itemState)) - { - ::DrawFocusRect(pdis->hDC, &pdis->rcItem); - } - - if (hfPrev) - { - SelectFont(pdis->hDC, hfPrev); - } -} - - -static HRESULT DrawImage( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ) -{ - DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top; - DWORD dwWidth = pdis->rcItem.right - pdis->rcItem.left; - int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; - int nSourceY = pControl->hImage ? 0 : pControl->nSourceY; - DWORD dwSourceHeight = pControl->nDefaultDpiHeight; - DWORD dwSourceWidth = pControl->nDefaultDpiWidth; - - BLENDFUNCTION bf = { }; - bf.BlendOp = AC_SRC_OVER; - bf.SourceConstantAlpha = 255; - bf.AlphaFormat = AC_SRC_ALPHA; - - HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); - HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); - - // Try to draw the image with transparency and if that fails (usually because the image has no - // alpha channel) then draw the image as is. - if (!::AlphaBlend(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, bf)) - { - ::StretchBlt(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); - } - - ::SelectObject(hdcMem, hDefaultBitmap); - ::DeleteDC(hdcMem); - return S_OK; -} - - -static HRESULT DrawProgressBar( - __in THEME* pTheme, - __in DRAWITEMSTRUCT* pdis, - __in const THEME_CONTROL* pControl - ) -{ - DWORD dwProgressColor = HIWORD(pControl->dwData); - DWORD dwProgressPercentage = LOWORD(pControl->dwData); - DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top; - DWORD dwCenter = (pdis->rcItem.right - 2) * dwProgressPercentage / 100; - DWORD dwSourceHeight = pControl->nDefaultDpiHeight; - int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; - int nSourceY = (pControl->hImage ? 0 : pControl->nSourceY) + (dwProgressColor * dwSourceHeight); - - HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); - HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); - - // Draw the left side of the progress bar. - ::StretchBlt(pdis->hDC, 0, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwSourceHeight, SRCCOPY); - - // Draw the filled side of the progress bar, if there is any. - if (0 < dwCenter) - { - ::StretchBlt(pdis->hDC, 1, 0, dwCenter, dwHeight, hdcMem, nSourceX + 1, nSourceY, 1, dwSourceHeight, SRCCOPY); - } - - // Draw the unfilled side of the progress bar, if there is any. - if (dwCenter < static_cast(pdis->rcItem.right - 2)) - { - ::StretchBlt(pdis->hDC, 1 + dwCenter, 0, pdis->rcItem.right - dwCenter - 1, dwHeight, hdcMem, nSourceX + 2, nSourceY, 1, dwSourceHeight, SRCCOPY); - } - - // Draw the right side of the progress bar. - ::StretchBlt(pdis->hDC, pdis->rcItem.right - 1, 0, 1, dwHeight, hdcMem, nSourceX + 3, nSourceY, 1, dwSourceHeight, SRCCOPY); - - ::SelectObject(hdcMem, hDefaultBitmap); - ::DeleteDC(hdcMem); - return S_OK; -} - - -static BOOL DrawHoverControl( - __in THEME* pTheme, - __in BOOL fHover - ) -{ - BOOL fChangedHover = FALSE; - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, pTheme->hwndHover)); - - // Only hyperlinks and owner-drawn buttons have hover states. - if (pControl && (THEME_CONTROL_TYPE_HYPERLINK == pControl->type || - (THEME_CONTROL_TYPE_BUTTON == pControl->type && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW)))) - { - if (fHover) - { - pControl->dwData |= THEME_CONTROL_DATA_HOVER; - } - else - { - pControl->dwData &= ~THEME_CONTROL_DATA_HOVER; - } - - ::InvalidateRect(pControl->hWnd, NULL, FALSE); - fChangedHover = TRUE; - } - - return fChangedHover; -} - - -static void FreePage( - __in THEME_PAGE* pPage - ) -{ - if (pPage) - { - ReleaseStr(pPage->sczName); - - if (pPage->cSavedVariables) - { - for (DWORD i = 0; i < pPage->cSavedVariables; ++i) - { - ReleaseStr(pPage->rgSavedVariables[i].sczValue); - } - } - - ReleaseMem(pPage->rgSavedVariables); - } -} - - -static void FreeImageList( - __in THEME_IMAGELIST* pImageList - ) -{ - if (pImageList) - { - ReleaseStr(pImageList->sczName); - ImageList_Destroy(pImageList->hImageList); - } -} - -static void FreeControl( - __in THEME_CONTROL* pControl - ) -{ - if (pControl) - { - if (::IsWindow(pControl->hWnd)) - { - ::CloseWindow(pControl->hWnd); - pControl->hWnd = NULL; - } - - ReleaseStr(pControl->sczName); - ReleaseStr(pControl->sczText); - ReleaseStr(pControl->sczTooltip); - ReleaseStr(pControl->sczNote); - ReleaseStr(pControl->sczEnableCondition); - ReleaseStr(pControl->sczVisibleCondition); - ReleaseStr(pControl->sczValue); - ReleaseStr(pControl->sczVariable); - - if (pControl->hImage) - { - ::DeleteBitmap(pControl->hImage); - } - - for (DWORD i = 0; i < pControl->cControls; ++i) - { - FreeControl(pControl->rgControls + i); - } - - for (DWORD i = 0; i < pControl->cActions; ++i) - { - FreeAction(&(pControl->rgActions[i])); - } - - for (DWORD i = 0; i < pControl->cColumns; ++i) - { - FreeColumn(&(pControl->ptcColumns[i])); - } - - for (DWORD i = 0; i < pControl->cConditionalText; ++i) - { - FreeConditionalText(&(pControl->rgConditionalText[i])); - } - - for (DWORD i = 0; i < pControl->cConditionalNotes; ++i) - { - FreeConditionalText(&(pControl->rgConditionalNotes[i])); - } - - for (DWORD i = 0; i < pControl->cTabs; ++i) - { - FreeTab(&(pControl->pttTabs[i])); - } - - ReleaseMem(pControl->rgActions) - ReleaseMem(pControl->ptcColumns); - ReleaseMem(pControl->rgConditionalText); - ReleaseMem(pControl->rgConditionalNotes); - ReleaseMem(pControl->pttTabs); - } -} - - -static void FreeAction( - __in THEME_ACTION* pAction - ) -{ - switch (pAction->type) - { - case THEME_ACTION_TYPE_BROWSE_DIRECTORY: - ReleaseStr(pAction->BrowseDirectory.sczVariableName); - break; - case THEME_ACTION_TYPE_CHANGE_PAGE: - ReleaseStr(pAction->ChangePage.sczPageName); - break; - } - - ReleaseStr(pAction->sczCondition); -} - - -static void FreeColumn( - __in THEME_COLUMN* pColumn - ) -{ - ReleaseStr(pColumn->pszName); -} - - -static void FreeConditionalText( - __in THEME_CONDITIONAL_TEXT* pConditionalText - ) -{ - ReleaseStr(pConditionalText->sczCondition); - ReleaseStr(pConditionalText->sczText); -} - - -static void FreeTab( - __in THEME_TAB* pTab - ) -{ - ReleaseStr(pTab->pszName); -} - - -static void FreeFontInstance( - __in THEME_FONT_INSTANCE* pFontInstance - ) -{ - if (pFontInstance->hFont) - { - ::DeleteObject(pFontInstance->hFont); - pFontInstance->hFont = NULL; - } -} - - -static void FreeFont( - __in THEME_FONT* pFont - ) -{ - if (pFont) - { - if (pFont->hBackground) - { - ::DeleteObject(pFont->hBackground); - pFont->hBackground = NULL; - } - - if (pFont->hForeground) - { - ::DeleteObject(pFont->hForeground); - pFont->hForeground = NULL; - } - - for (DWORD i = 0; i < pFont->cFontInstances; ++i) - { - FreeFontInstance(&(pFont->rgFontInstances[i])); - } - - ReleaseMem(pFont->rgFontInstances); - ReleaseStr(pFont->sczFaceName); - } -} - - -static DWORD CALLBACK RichEditStreamFromFileHandleCallback( - __in DWORD_PTR dwCookie, - __in_bcount(cb) LPBYTE pbBuff, - __in LONG cb, - __in LONG* pcb - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = reinterpret_cast(dwCookie); - - if (!::ReadFile(hFile, pbBuff, cb, reinterpret_cast(pcb), NULL)) - { - ThmExitWithLastError(hr, "Failed to read file"); - } - -LExit: - return hr; -} - - -static DWORD CALLBACK RichEditStreamFromMemoryCallback( - __in DWORD_PTR dwCookie, - __in_bcount(cb) LPBYTE pbBuff, - __in LONG cb, - __in LONG* pcb - ) -{ - HRESULT hr = S_OK; - MEMBUFFER_FOR_RICHEDIT* pBuffer = reinterpret_cast(dwCookie); - DWORD cbCopy = 0; - - if (pBuffer->iData < pBuffer->cbData) - { - cbCopy = min(static_cast(cb), pBuffer->cbData - pBuffer->iData); - memcpy(pbBuff, pBuffer->rgbData + pBuffer->iData, cbCopy); - - pBuffer->iData += cbCopy; - Assert(pBuffer->iData <= pBuffer->cbData); - } - - *pcb = cbCopy; - return hr; -} - - -static void CALLBACK OnBillboardTimer( - __in THEME* pTheme, - __in HWND hwnd, - __in UINT_PTR idEvent - ) -{ - HWND hwndControl = ::GetDlgItem(hwnd, static_cast(idEvent)); - if (hwndControl) - { - THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hwndControl)); - AssertSz(pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type, "Only billboard controls should get billboard timer messages."); - - if (pControl) - { - if (pControl->dwData < pControl->cControls) - { - ThemeShowChild(pTheme, pControl, pControl->dwData); - } - else if (pControl->fBillboardLoops) - { - pControl->dwData = 0; - ThemeShowChild(pTheme, pControl, pControl->dwData); - } - else // no more looping - { - ::KillTimer(hwnd, idEvent); - } - - ++pControl->dwData; - } - } -} - -static void OnBrowseDirectory( - __in THEME* pTheme, - __in HWND hWnd, - __in const THEME_ACTION* pAction - ) -{ - HRESULT hr = S_OK; - WCHAR wzPath[MAX_PATH] = { }; - BROWSEINFOW browseInfo = { }; - PIDLIST_ABSOLUTE pidl = NULL; - - browseInfo.hwndOwner = hWnd; - browseInfo.pszDisplayName = wzPath; - browseInfo.lpszTitle = pTheme->sczCaption; - browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI; - pidl = ::SHBrowseForFolderW(&browseInfo); - if (pidl && ::SHGetPathFromIDListW(pidl, wzPath)) - { - // Since editbox changes aren't immediately saved off, we have to treat them differently. - THEME_CONTROL* pTargetControl = NULL; - - for (DWORD i = 0; i < pTheme->cControls; ++i) - { - THEME_CONTROL* pControl = pTheme->rgControls + i; - - if ((!pControl->wPageId || pControl->wPageId == pTheme->dwCurrentPageId) && - CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pControl->sczName, -1, pAction->BrowseDirectory.sczVariableName, -1)) - { - pTargetControl = pControl; - break; - } - } - - if (pTargetControl && THEME_CONTROL_TYPE_EDITBOX == pTargetControl->type && !pTargetControl->fDisableVariableFunctionality) - { - hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath); - ThmExitOnFailure(hr, "Failed to set text on editbox: %ls", pTargetControl->sczName); - } - else if (pTheme->pfnSetStringVariable) - { - hr = pTheme->pfnSetStringVariable(pAction->BrowseDirectory.sczVariableName, wzPath, FALSE, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to set variable: %ls", pAction->BrowseDirectory.sczVariableName); - } - else if (pTargetControl) - { - hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath); - ThmExitOnFailure(hr, "Failed to set text on control: %ls", pTargetControl->sczName); - } - - ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH); - } - -LExit: - if (pidl) - { - ::CoTaskMemFree(pidl); - } -} - -static BOOL OnButtonClicked( - __in THEME* pTheme, - __in HWND hWnd, - __in const THEME_CONTROL* pControl - ) -{ - HRESULT hr = S_OK; - BOOL fHandled = FALSE; - - if (THEME_CONTROL_TYPE_BUTTON == pControl->type || THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) - { - if (pControl->cActions) - { - fHandled = TRUE; - THEME_ACTION* pChosenAction = pControl->pDefaultAction; - - if (pTheme->pfnEvaluateCondition) - { - // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined. - // This is the current implementation and can change at any time. - for (DWORD j = 0; j < pControl->cActions; ++j) - { - THEME_ACTION* pAction = pControl->rgActions + j; - - if (pAction->sczCondition) - { - BOOL fCondition = FALSE; - - hr = pTheme->pfnEvaluateCondition(pAction->sczCondition, &fCondition, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate condition: %ls", pAction->sczCondition); - - if (fCondition) - { - pChosenAction = pAction; - break; - } - } - } - } - - if (pChosenAction) - { - switch (pChosenAction->type) - { - case THEME_ACTION_TYPE_BROWSE_DIRECTORY: - OnBrowseDirectory(pTheme, hWnd, pChosenAction); - break; - - case THEME_ACTION_TYPE_CLOSE_WINDOW: - ::SendMessageW(hWnd, WM_CLOSE, 0, 0); - break; - - case THEME_ACTION_TYPE_CHANGE_PAGE: - DWORD dwPageId = 0; - LPCWSTR pPageNames = pChosenAction->ChangePage.sczPageName; - ThemeGetPageIds(pTheme, &pPageNames, &dwPageId, 1); - - if (!dwPageId) - { - ThmExitOnFailure(E_INVALIDDATA, "Unknown page: %ls", pChosenAction->ChangePage.sczPageName); - } - - ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_HIDE, pChosenAction->ChangePage.fCancel ? THEME_SHOW_PAGE_REASON_CANCEL : THEME_SHOW_PAGE_REASON_DEFAULT); - ThemeShowPage(pTheme, dwPageId, SW_SHOW); - break; - } - } - } - } - else if (!pControl->fDisableVariableFunctionality && (pTheme->pfnSetNumericVariable || pTheme->pfnSetStringVariable)) - { - BOOL fRefresh = FALSE; - - switch (pControl->type) - { - case THEME_CONTROL_TYPE_CHECKBOX: - if (pTheme->pfnSetNumericVariable && pControl->sczName && *pControl->sczName) - { - BOOL fChecked = ThemeIsControlChecked(pTheme, pControl->wId); - pTheme->pfnSetNumericVariable(pControl->sczName, fChecked ? 1 : 0, pTheme->pvVariableContext); - fRefresh = TRUE; - } - break; - case THEME_CONTROL_TYPE_RADIOBUTTON: - if (pTheme->pfnSetStringVariable && pControl->sczVariable && *pControl->sczVariable && ThemeIsControlChecked(pTheme, pControl->wId)) - { - pTheme->pfnSetStringVariable(pControl->sczVariable, pControl->sczValue, FALSE, pTheme->pvVariableContext); - fRefresh = TRUE; - } - break; - } - - if (fRefresh) - { - ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH); - fHandled = TRUE; - } - } - -LExit: - return fHandled; -} - -static BOOL OnDpiChanged( - __in THEME* pTheme, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - UINT nDpi = HIWORD(wParam); - RECT* pRect = reinterpret_cast(lParam); - BOOL fIgnored = pTheme->nDpi == nDpi; - - if (fIgnored) - { - ExitFunction(); - } - - - pTheme->fForceResize = !pTheme->fAutoResize; - ScaleThemeFromWindow(pTheme, nDpi, pRect->left, pRect->top); - -LExit: - return !fIgnored; -} - -static void OnNcCreate( - __in THEME* pTheme, - __in HWND hWnd, - __in LPARAM lParam - ) -{ - DPIU_WINDOW_CONTEXT windowContext = { }; - CREATESTRUCTW* pCreateStruct = reinterpret_cast(lParam); - - pTheme->hwndParent = hWnd; - - DpiuGetWindowContext(pTheme->hwndParent, &windowContext); - - if (windowContext.nDpi != pTheme->nDpi) - { - ScaleTheme(pTheme, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y, pCreateStruct->style, NULL != pCreateStruct->hMenu, pCreateStruct->dwExStyle); - } -} - -static HRESULT OnRichEditEnLink( - __in LPARAM lParam, - __in HWND hWndRichEdit, - __in HWND hWnd - ) -{ - HRESULT hr = S_OK; - LPWSTR sczLink = NULL; - ENLINK* link = reinterpret_cast(lParam); - - switch (link->msg) - { - case WM_LBUTTONDOWN: - { - hr = StrAlloc(&sczLink, link->chrg.cpMax - link->chrg.cpMin + 2); - ThmExitOnFailure(hr, "Failed to allocate string for link."); - - TEXTRANGEW tr; - tr.chrg.cpMin = link->chrg.cpMin; - tr.chrg.cpMax = link->chrg.cpMax; - tr.lpstrText = sczLink; - - if (0 < ::SendMessageW(hWndRichEdit, EM_GETTEXTRANGE, 0, reinterpret_cast(&tr))) - { - hr = ShelExec(sczLink, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL); - ThmExitOnFailure(hr, "Failed to launch link: %ls", sczLink); - } - - break; - } - - case WM_SETCURSOR: - ::SetCursor(vhCursorHand); - break; - } - -LExit: - ReleaseStr(sczLink); - - return hr; -} - -static BOOL ControlIsType( - __in const THEME* pTheme, - __in DWORD dwControl, - __in const THEME_CONTROL_TYPE type - ) -{ - BOOL fIsType = FALSE; - HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); - if (hWnd) - { - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); - fIsType = (pControl && type == pControl->type); - } - - return fIsType; -} - -static const THEME_CONTROL* FindControlFromHWnd( - __in const THEME* pTheme, - __in HWND hWnd, - __in_opt const THEME_CONTROL* pParentControl - ) -{ - DWORD cControls = 0; - THEME_CONTROL* rgControls = NULL; - - GetControls(pTheme, pParentControl, cControls, rgControls); - - // As we can't use GWLP_USERDATA (SysLink controls on Windows XP uses it too)... - for (DWORD i = 0; i < cControls; ++i) - { - if (hWnd == rgControls[i].hWnd) - { - return rgControls + i; - } - else if (0 < rgControls[i].cControls) - { - const THEME_CONTROL* pChildControl = FindControlFromHWnd(pTheme, hWnd, rgControls + i); - if (pChildControl) - { - return pChildControl; - } - } - } - - return NULL; -} - -static void GetControlDimensions( - __in const THEME_CONTROL* pControl, - __in const RECT* prcParent, - __out int* piWidth, - __out int* piHeight, - __out int* piX, - __out int* piY - ) -{ - *piWidth = pControl->nWidth + (0 < pControl->nWidth ? 0 : prcParent->right - max(0, pControl->nX)); - *piHeight = pControl->nHeight + (0 < pControl->nHeight ? 0 : prcParent->bottom - max(0, pControl->nY)); - *piX = pControl->nX + (-1 < pControl->nX ? 0 : prcParent->right - *piWidth); - *piY = pControl->nY + (-1 < pControl->nY ? 0 : prcParent->bottom - *piHeight); -} - -static HRESULT SizeListViewColumns( - __inout THEME_CONTROL* pControl - ) -{ - HRESULT hr = S_OK; - RECT rcParent = { }; - int cNumExpandingColumns = 0; - int iExtraAvailableSize; - - if (!::GetWindowRect(pControl->hWnd, &rcParent)) - { - ThmExitWithLastError(hr, "Failed to get window rect of listview control."); - } - - iExtraAvailableSize = rcParent.right - rcParent.left; - - for (DWORD i = 0; i < pControl->cColumns; ++i) - { - if (pControl->ptcColumns[i].fExpands) - { - ++cNumExpandingColumns; - } - - iExtraAvailableSize -= pControl->ptcColumns[i].nBaseWidth; - } - - // Leave room for a vertical scroll bar just in case. - iExtraAvailableSize -= ::GetSystemMetrics(SM_CXVSCROLL); - - for (DWORD i = 0; i < pControl->cColumns; ++i) - { - if (pControl->ptcColumns[i].fExpands) - { - pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth + (iExtraAvailableSize / cNumExpandingColumns); - // In case there is any remainder, use it up the first chance we get. - pControl->ptcColumns[i].nWidth += iExtraAvailableSize % cNumExpandingColumns; - iExtraAvailableSize -= iExtraAvailableSize % cNumExpandingColumns; - } - else - { - pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth; - } - } - -LExit: - return hr; -} - - -static HRESULT ShowControl( - __in THEME* pTheme, - __in THEME_CONTROL* pControl, - __in int nCmdShow, - __in BOOL fSaveEditboxes, - __in THEME_SHOW_PAGE_REASON reason, - __in DWORD dwPageId, - __out_opt HWND* phwndFocus - ) -{ - HRESULT hr = S_OK; - DWORD iPageControl = 0; - HWND hwndFocus = NULL; - LPWSTR sczFormatString = NULL; - LPWSTR sczText = NULL; - THEME_SAVEDVARIABLE* pSavedVariable = NULL; - BOOL fHide = SW_HIDE == nCmdShow; - THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPageId); - - // Save the editbox value if necessary (other control types save their values immediately). - if (pTheme->pfnSetStringVariable && !pControl->fDisableVariableFunctionality && - fSaveEditboxes && THEME_CONTROL_TYPE_EDITBOX == pControl->type && pControl->sczName && *pControl->sczName) - { - hr = ThemeGetTextControl(pTheme, pControl->wId, &sczText); - ThmExitOnFailure(hr, "Failed to get the text for control: %ls", pControl->sczName); - - hr = pTheme->pfnSetStringVariable(pControl->sczName, sczText, FALSE, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to set the variable '%ls' to '%ls'", pControl->sczName, sczText); - } - - HWND hWnd = pControl->hWnd; - - if (fHide && pControl->wPageId) - { - ::ShowWindow(hWnd, SW_HIDE); - - if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type) - { - StopBillboard(pTheme, pControl->wId); - } - - ExitFunction(); - } - - BOOL fEnabled = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED); - BOOL fVisible = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDDEN); - - if (!pControl->fDisableVariableFunctionality) - { - if (pTheme->pfnEvaluateCondition) - { - // If the control has a VisibleCondition, check if it's true. - if (pControl->sczVisibleCondition) - { - hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); - } - - // If the control has an EnableCondition, check if it's true. - if (pControl->sczEnableCondition) - { - hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnabled, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); - } - } - - // Try to format each control's text based on context, except for editboxes since their text comes from the user. - if (pTheme->pfnFormatString && ((pControl->sczText && *pControl->sczText) || pControl->cConditionalText) && THEME_CONTROL_TYPE_EDITBOX != pControl->type) - { - LPWSTR wzText = pControl->sczText; - LPWSTR wzNote = pControl->sczNote; - - if (pTheme->pfnEvaluateCondition) - { - // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined. - // This is the current implementation and can change at any time. - for (DWORD j = 0; j < pControl->cConditionalText; ++j) - { - THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + j; - wzText = pConditionalText->sczText; - - if (pConditionalText->sczCondition) - { - BOOL fCondition = FALSE; - - hr = pTheme->pfnEvaluateCondition(pConditionalText->sczCondition, &fCondition, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate condition: %ls", pConditionalText->sczCondition); - - if (fCondition) - { - wzText = pConditionalText->sczText; - break; - } - } - } - - for (DWORD j = 0; j < pControl->cConditionalNotes; ++j) - { - THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + j; - wzNote = pConditionalNote->sczText; - - if (pConditionalNote->sczCondition) - { - BOOL fCondition = FALSE; - - hr = pTheme->pfnEvaluateCondition(pConditionalNote->sczCondition, &fCondition, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate note condition: %ls", pConditionalNote->sczCondition); - - if (fCondition) - { - wzNote = pConditionalNote->sczText; - break; - } - } - } - } - - if (wzText && *wzText) - { - hr = pTheme->pfnFormatString(wzText, &sczText, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to format string: %ls", wzText); - } - else - { - ReleaseNullStr(sczText); - } - - ThemeSetTextControl(pTheme, pControl->wId, sczText); - - if (wzNote && *wzNote) - { - hr = pTheme->pfnFormatString(wzNote, &sczText, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to format note: %ls", wzNote); - } - else - { - ReleaseNullStr(sczText); - } - - ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast(sczText)); - } - - // If this is a named control, do variable magic. - if (pControl->sczName && *pControl->sczName) - { - // If this is a checkbox control, - // try to set its default state to the state of a matching named variable. - if (pTheme->pfnGetNumericVariable && THEME_CONTROL_TYPE_CHECKBOX == pControl->type) - { - LONGLONG llValue = 0; - hr = pTheme->pfnGetNumericVariable(pControl->sczName, &llValue, pTheme->pvVariableContext); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ThmExitOnFailure(hr, "Failed to get numeric variable: %ls", pControl->sczName); - - if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId) - { - pSavedVariable = pPage->rgSavedVariables + iPageControl; - pSavedVariable->wzName = pControl->sczName; - - if (SUCCEEDED(hr)) - { - hr = StrAllocFormattedSecure(&pSavedVariable->sczValue, L"%lld", llValue); - ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); - } - - ++iPageControl; - } - - ThemeSendControlMessage(pTheme, pControl->wId, BM_SETCHECK, SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED, 0); - } - - // If this is an editbox control, - // try to set its default state to the state of a matching named variable. - if (pTheme->pfnFormatString && THEME_CONTROL_TYPE_EDITBOX == pControl->type) - { - hr = StrAllocFormatted(&sczFormatString, L"[%ls]", pControl->sczName); - ThmExitOnFailure(hr, "Failed to create format string: '%ls'", pControl->sczName); - - hr = pTheme->pfnFormatString(sczFormatString, &sczText, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to format string: '%ls'", sczFormatString); - - if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId) - { - pSavedVariable = pPage->rgSavedVariables + iPageControl; - pSavedVariable->wzName = pControl->sczName; - - if (SUCCEEDED(hr)) - { - hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0); - ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); - } - - ++iPageControl; - } - - ThemeSetTextControl(pTheme, pControl->wId, sczText); - } - } - - // If this is a radio button associated with a variable, - // try to set its default state to the state of the variable. - if (pTheme->pfnGetStringVariable && THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type && pControl->sczVariable && *pControl->sczVariable) - { - hr = pTheme->pfnGetStringVariable(pControl->sczVariable, &sczText, pTheme->pvVariableContext); - if (E_NOTFOUND == hr) - { - ReleaseNullStr(sczText); - } - else - { - ThmExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczVariable); - } - - if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId && pControl->fLastRadioButton) - { - pSavedVariable = pPage->rgSavedVariables + iPageControl; - pSavedVariable->wzName = pControl->sczVariable; - - if (SUCCEEDED(hr)) - { - hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0); - ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczVariable); - } - - ++iPageControl; - } - - hr = S_OK; - - Button_SetCheck(hWnd, (!sczText && !pControl->sczValue) || (sczText && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczText, -1, pControl->sczValue, -1))); - } - } - - if (!fVisible || (!fEnabled && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED))) - { - ::ShowWindow(hWnd, SW_HIDE); - } - else - { - ::EnableWindow(hWnd, !fHide && fEnabled); - - if (!hwndFocus && pControl->wPageId && (pControl->dwStyle & WS_TABSTOP)) - { - hwndFocus = hWnd; - } - - ::ShowWindow(hWnd, nCmdShow); - } - - if (0 < pControl->cControls) - { - ShowControls(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId); - } - - if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type && pControl->wPageId) - { - if (fEnabled) - { - StartBillboard(pTheme, pControl->wId); - } - else - { - StopBillboard(pTheme, pControl->wId); - } - } - - if (phwndFocus) - { - *phwndFocus = hwndFocus; - } - -LExit: - ReleaseStr(sczFormatString); - ReleaseStr(sczText); - - return hr; -} - -static HRESULT ShowControls( - __in THEME* pTheme, - __in_opt const THEME_CONTROL* pParentControl, - __in int nCmdShow, - __in BOOL fSaveEditboxes, - __in THEME_SHOW_PAGE_REASON reason, - __in DWORD dwPageId - ) -{ - HRESULT hr = S_OK; - HWND hwndFocus = NULL; - DWORD cControls = 0; - THEME_CONTROL* rgControls = NULL; - - GetControls(pTheme, pParentControl, cControls, rgControls); - - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - - // Only look at non-page controls and the specified page's controls. - if (!pControl->wPageId || pControl->wPageId == dwPageId) - { - hr = ShowControl(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId, &hwndFocus); - ThmExitOnFailure(hr, "Failed to show control '%ls' at index %d.", pControl->sczName, i); - } - } - - if (hwndFocus) - { - ::SetFocus(hwndFocus); - } - -LExit: - return hr; -} - - -static LRESULT CALLBACK ControlGroupDefWindowProc( - __in_opt THEME* pTheme, - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - if (pTheme) - { - switch (uMsg) - { - case WM_DRAWITEM: - ThemeDrawControl(pTheme, reinterpret_cast(lParam)); - return TRUE; - - case WM_CTLCOLORBTN: __fallthrough; - case WM_CTLCOLORSTATIC: - { - HBRUSH hBrush = NULL; - if (ThemeSetControlColor(pTheme, reinterpret_cast(wParam), reinterpret_cast(lParam), &hBrush)) - { - return reinterpret_cast(hBrush); - } - } - break; - - case WM_SETCURSOR: - if (ThemeHoverControl(pTheme, hWnd, reinterpret_cast(wParam))) - { - return TRUE; - } - break; - - case WM_PAINT: - if (::GetUpdateRect(hWnd, NULL, FALSE)) - { - PAINTSTRUCT ps; - ::BeginPaint(hWnd, &ps); - if (hWnd == pTheme->hwndParent) - { - ThemeDrawBackground(pTheme, &ps); - } - ::EndPaint(hWnd, &ps); - } - return 0; - - case WM_TIMER: - OnBillboardTimer(pTheme, hWnd, wParam); - break; - - case WM_NOTIFY: - if (lParam) - { - LPNMHDR pnmhdr = reinterpret_cast(lParam); - switch (pnmhdr->code) - { - // Tab/Shift+Tab support for rich-edit control. - case EN_MSGFILTER: - { - MSGFILTER* msgFilter = reinterpret_cast(lParam); - if (WM_KEYDOWN == msgFilter->msg && VK_TAB == msgFilter->wParam) - { - BOOL fShift = 0x8000 & ::GetKeyState(VK_SHIFT); - HWND hwndFocus = ::GetNextDlgTabItem(hWnd, msgFilter->nmhdr.hwndFrom, fShift); - ::SetFocus(hwndFocus); - return 1; - } - break; - } - - // Hyperlink clicks from rich-edit control. - case EN_LINK: - return SUCCEEDED(OnRichEditEnLink(lParam, pnmhdr->hwndFrom, hWnd)); - - // Clicks on a hypertext/syslink control. - case NM_CLICK: __fallthrough; - case NM_RETURN: - if (ControlIsType(pTheme, static_cast(pnmhdr->idFrom), THEME_CONTROL_TYPE_HYPERTEXT)) - { - PNMLINK pnmlink = reinterpret_cast(lParam); - LITEM litem = pnmlink->item; - ShelExec(litem.szUrl, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL); - return 1; - } - - return 0; - } - } - break; - - case WM_COMMAND: - switch (HIWORD(wParam)) - { - case BN_CLICKED: - if (lParam) - { - const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, (HWND)lParam); - if (pControl && OnButtonClicked(pTheme, hWnd, pControl)) - { - return 0; - } - } - break; - } - break; - } - } - - return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); -} - - -static LRESULT CALLBACK PanelWndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - LRESULT lres = 0; - THEME* pTheme = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); - - switch (uMsg) - { - case WM_NCCREATE: - { - LPCREATESTRUCTW lpcs = reinterpret_cast(lParam); - pTheme = reinterpret_cast(lpcs->lpCreateParams); - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pTheme)); - } - break; - - case WM_NCDESTROY: - lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); - return lres; - - case WM_NCHITTEST: - return HTCLIENT; - break; - } - - return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); -} - -static LRESULT CALLBACK StaticOwnerDrawWndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_UPDATEUISTATE: - return ::DefWindowProc(hWnd, uMsg, wParam, lParam); - default: - return (*vpfnStaticOwnerDrawBaseWndProc)(hWnd, uMsg, wParam, lParam); - } -} - -static HRESULT LoadControls( - __in THEME* pTheme, - __in_opt THEME_CONTROL* pParentControl, - __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, - __in DWORD cAssignControlIds - ) -{ - HRESULT hr = S_OK; - RECT rcParent = { }; - LPWSTR sczText = NULL; - BOOL fStartNewGroup = FALSE; - DWORD cControls = 0; - THEME_CONTROL* rgControls = NULL; - HWND hwndParent = pParentControl ? pParentControl->hWnd : pTheme->hwndParent; - int w = 0; - int h = 0; - int x = 0; - int y = 0; - - GetControls(pTheme, pParentControl, cControls, rgControls); - ::GetClientRect(hwndParent, &rcParent); - - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL; - THEME_FONT_INSTANCE* pControlFontInstance = NULL; - LPCWSTR wzWindowClass = NULL; - DWORD dwWindowBits = WS_CHILD; - DWORD dwWindowExBits = 0; - - if (fStartNewGroup) - { - dwWindowBits |= WS_GROUP; - fStartNewGroup = FALSE; - } - - switch (pControl->type) - { - case THEME_CONTROL_TYPE_BILLBOARD: - __fallthrough; - case THEME_CONTROL_TYPE_PANEL: - wzWindowClass = THEME_WC_PANEL; - dwWindowBits |= WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; - dwWindowExBits |= WS_EX_TRANSPARENT | WS_EX_CONTROLPARENT; -#ifdef DEBUG - StrAllocFormatted(&pControl->sczText, L"Panel '%ls', id: %d", pControl->sczName, pControl->wId); -#endif - break; - - case THEME_CONTROL_TYPE_CHECKBOX: - dwWindowBits |= BS_AUTOCHECKBOX | BS_MULTILINE; // checkboxes are basically buttons with an extra bit tossed in. - __fallthrough; - case THEME_CONTROL_TYPE_BUTTON: - wzWindowClass = WC_BUTTONW; - if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) - { - dwWindowBits |= BS_OWNERDRAW; - pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; - } - break; - - case THEME_CONTROL_TYPE_COMBOBOX: - wzWindowClass = WC_COMBOBOXW; - dwWindowBits |= CBS_DROPDOWNLIST | CBS_HASSTRINGS; - break; - - case THEME_CONTROL_TYPE_COMMANDLINK: - wzWindowClass = WC_BUTTONW; - dwWindowBits |= BS_COMMANDLINK; - break; - - case THEME_CONTROL_TYPE_EDITBOX: - wzWindowClass = WC_EDITW; - dwWindowBits |= ES_LEFT | ES_AUTOHSCROLL; - dwWindowExBits = WS_EX_CLIENTEDGE; - break; - - case THEME_CONTROL_TYPE_HYPERLINK: // hyperlinks are basically just owner drawn buttons. - wzWindowClass = THEME_WC_HYPERLINK; - dwWindowBits |= BS_OWNERDRAW | BTNS_NOPREFIX; - break; - - case THEME_CONTROL_TYPE_HYPERTEXT: - wzWindowClass = WC_LINK; - dwWindowBits |= LWS_NOPREFIX; - break; - - case THEME_CONTROL_TYPE_IMAGE: // images are basically just owner drawn static controls (so we can draw .jpgs and .pngs instead of just bitmaps). - if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) - { - wzWindowClass = THEME_WC_STATICOWNERDRAW; - dwWindowBits |= SS_OWNERDRAW; - pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ThmExitOnRootFailure(hr, "Invalid image or image list coordinates."); - } - break; - - case THEME_CONTROL_TYPE_LABEL: - wzWindowClass = WC_STATICW; - break; - - case THEME_CONTROL_TYPE_LISTVIEW: - // If thmutil is handling the image list for this listview, tell Windows not to free it when the control is destroyed. - if (pControl->rghImageList[0] || pControl->rghImageList[1] || pControl->rghImageList[2] || pControl->rghImageList[3]) - { - pControl->dwStyle |= LVS_SHAREIMAGELISTS; - } - wzWindowClass = WC_LISTVIEWW; - break; - - case THEME_CONTROL_TYPE_PROGRESSBAR: - if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) - { - wzWindowClass = THEME_WC_STATICOWNERDRAW; // no such thing as an owner drawn progress bar so we'll make our own out of a static control. - dwWindowBits |= SS_OWNERDRAW; - pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; - } - else - { - wzWindowClass = PROGRESS_CLASSW; - } - break; - - case THEME_CONTROL_TYPE_RADIOBUTTON: - dwWindowBits |= BS_AUTORADIOBUTTON | BS_MULTILINE; - wzWindowClass = WC_BUTTONW; - - if (pControl->fLastRadioButton) - { - fStartNewGroup = TRUE; - } - break; - - case THEME_CONTROL_TYPE_RICHEDIT: - if (!vhModuleMsftEdit && !vhModuleRichEd) - { - hr = LoadSystemLibrary(L"Msftedit.dll", &vhModuleMsftEdit); - if (FAILED(hr)) - { - hr = LoadSystemLibrary(L"Riched20.dll", &vhModuleRichEd); - ThmExitOnFailure(hr, "Failed to load Rich Edit control library."); - } - } - - wzWindowClass = vhModuleMsftEdit ? MSFTEDIT_CLASS : RICHEDIT_CLASSW; - dwWindowBits |= ES_AUTOVSCROLL | ES_MULTILINE | WS_VSCROLL | ES_READONLY; - break; - - case THEME_CONTROL_TYPE_STATIC: - wzWindowClass = WC_STATICW; - dwWindowBits |= SS_ETCHEDHORZ; - break; - - case THEME_CONTROL_TYPE_TAB: - wzWindowClass = WC_TABCONTROLW; - break; - - case THEME_CONTROL_TYPE_TREEVIEW: - wzWindowClass = WC_TREEVIEWW; - break; - } - ThmExitOnNull(wzWindowClass, hr, E_INVALIDDATA, "Failed to configure control %u because of unknown type: %u", i, pControl->type); - - // Default control ids to the theme id and its index in the control array, unless there - // is a specific id to assign to a named control. - WORD wControlId = MAKEWORD(i, pTheme->wId); - for (DWORD iAssignControl = 0; pControl->sczName && iAssignControl < cAssignControlIds; ++iAssignControl) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pControl->sczName, -1, rgAssignControlIds[iAssignControl].wzName, -1)) - { - wControlId = rgAssignControlIds[iAssignControl].wId; - break; - } - } - - pControl->wId = wControlId; - - GetControlDimensions(pControl, &rcParent, &w, &h, &x, &y); - - BOOL fVisible = pControl->dwStyle & WS_VISIBLE; - BOOL fDisabled = pControl->dwStyle & WS_DISABLED; - - // If the control is supposed to be initially visible and it has a VisibleCondition, check if it's true. - if (fVisible && pControl->sczVisibleCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality) - { - hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); - - if (!fVisible) - { - pControl->dwStyle &= ~WS_VISIBLE; - } - } - - // Disable controls that aren't visible so their shortcut keys don't trigger. - if (!fVisible) - { - dwWindowBits |= WS_DISABLED; - fDisabled = TRUE; - } - - // If the control is supposed to be initially enabled and it has an EnableCondition, check if it's true. - if (!fDisabled && pControl->sczEnableCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality) - { - BOOL fEnable = TRUE; - - hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnable, pTheme->pvVariableContext); - ThmExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); - - fDisabled = !fEnable; - dwWindowBits |= fDisabled ? WS_DISABLED : 0; - } - - // Honor the HideWhenDisabled option. - if ((pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED) && fVisible && fDisabled) - { - fVisible = FALSE; - pControl->dwStyle &= ~WS_VISIBLE; - } - - pControl->hWnd = ::CreateWindowExW(dwWindowExBits, wzWindowClass, pControl->sczText, pControl->dwStyle | dwWindowBits, x, y, w, h, hwndParent, reinterpret_cast(wControlId), NULL, pTheme); - ThmExitOnNullWithLastError(pControl->hWnd, hr, "Failed to create window."); - - if (pControl->sczTooltip) - { - if (!pTheme->hwndTooltip) - { - pTheme->hwndTooltip = ::CreateWindowExW(WS_EX_TOOLWINDOW, TOOLTIPS_CLASSW, NULL, WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON | TTS_NOPREFIX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndParent, NULL, NULL, NULL); - } - - if (pTheme->hwndTooltip) - { - TOOLINFOW toolinfo = {}; - toolinfo.cbSize = sizeof(toolinfo); - toolinfo.hwnd = hwndParent; - toolinfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; - toolinfo.uId = reinterpret_cast(pControl->hWnd); - toolinfo.lpszText = pControl->sczTooltip; - ::SendMessageW(pTheme->hwndTooltip, TTM_ADDTOOLW, 0, reinterpret_cast(&toolinfo)); - } - } - - if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) - { - if (pControl->sczNote) - { - ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast(pControl->sczNote)); - } - - if (pControl->hImage) - { - ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_BITMAP, reinterpret_cast(pControl->hImage)); - } - else if (pControl->hIcon) - { - ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_ICON, reinterpret_cast(pControl->hIcon)); - } - } - else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type) - { - if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE) - { - hr = ::SHAutoComplete(pControl->hWnd, SHACF_FILESYS_ONLY); - } - } - else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) - { - ::SendMessageW(pControl->hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, pControl->dwExtendedStyle); - - hr = SizeListViewColumns(pControl); - ThmExitOnFailure(hr, "Failed to get size of list view columns."); - - for (DWORD j = 0; j < pControl->cColumns; ++j) - { - LVCOLUMNW lvc = { }; - lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; - lvc.cx = pControl->ptcColumns[j].nWidth; - lvc.iSubItem = j; - lvc.pszText = pControl->ptcColumns[j].pszName; - lvc.fmt = LVCFMT_LEFT; - lvc.cchTextMax = 4; - - if (-1 == ::SendMessageW(pControl->hWnd, LVM_INSERTCOLUMNW, (WPARAM) (int) (j), (LPARAM) (const LV_COLUMNW *) (&lvc))) - { - ThmExitWithLastError(hr, "Failed to insert listview column %u into tab control.", j); - } - - // Return value tells us the old image list, we don't care. - if (pControl->rghImageList[0]) - { - ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_NORMAL), reinterpret_cast(pControl->rghImageList[0])); - } - else if (pControl->rghImageList[1]) - { - ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_SMALL), reinterpret_cast(pControl->rghImageList[1])); - } - else if (pControl->rghImageList[2]) - { - ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_STATE), reinterpret_cast(pControl->rghImageList[2])); - } - else if (pControl->rghImageList[3]) - { - ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_GROUPHEADER), reinterpret_cast(pControl->rghImageList[3])); - } - } - } - else if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type) - { - ::SendMessageW(pControl->hWnd, EM_AUTOURLDETECT, static_cast(TRUE), 0); - ::SendMessageW(pControl->hWnd, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_LINK); - } - else if (THEME_CONTROL_TYPE_TAB == pControl->type) - { - ULONG_PTR hbrBackground = 0; - if (THEME_INVALID_ID != pControl->dwFontId) - { - hbrBackground = reinterpret_cast(pTheme->rgFonts[pControl->dwFontId].hBackground); - } - else - { - hbrBackground = ::GetClassLongPtr(pTheme->hwndParent, GCLP_HBRBACKGROUND); - } - ::SetClassLongPtr(pControl->hWnd, GCLP_HBRBACKGROUND, hbrBackground); - - for (DWORD j = 0; j < pControl->cTabs; ++j) - { - TCITEMW tci = { }; - tci.mask = TCIF_TEXT | TCIF_IMAGE; - tci.iImage = -1; - tci.pszText = pControl->pttTabs[j].pszName; - - if (-1 == ::SendMessageW(pControl->hWnd, TCM_INSERTITEMW, (WPARAM) (int) (j), (LPARAM) (const TC_ITEMW *) (&tci))) - { - ThmExitWithLastError(hr, "Failed to insert tab %u into tab control.", j); - } - } - } - - if (pControlFont) - { - hr = EnsureFontInstance(pTheme, pControlFont, &pControlFontInstance); - ThmExitOnFailure(hr, "Failed to get DPI specific font."); - - ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM) pControlFontInstance->hFont, FALSE); - } - - // Initialize the text on all "application" (non-page) controls, best effort only. - if (pTheme->pfnFormatString && !pControl->wPageId && pControl->sczText && *pControl->sczText) - { - HRESULT hrFormat = pTheme->pfnFormatString(pControl->sczText, &sczText, pTheme->pvVariableContext); - if (SUCCEEDED(hrFormat)) - { - ThemeSetTextControl(pTheme, pControl->wId, sczText); - } - } - - if (pControl->cControls) - { - hr = LoadControls(pTheme, pControl, rgAssignControlIds, cAssignControlIds); - ThmExitOnFailure(hr, "Failed to load child controls."); - } - } - -LExit: - ReleaseStr(sczText); - - return hr; -} - -static HRESULT LocalizeControls( - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in const WIX_LOCALIZATION *pWixLoc - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - hr = LocalizeControl(pControl, pWixLoc); - ThmExitOnFailure(hr, "Failed to localize control: %ls", pControl->sczName); - } - -LExit: - return hr; -} - -static HRESULT LocalizeControl( - __in THEME_CONTROL* pControl, - __in const WIX_LOCALIZATION *pWixLoc - ) -{ - HRESULT hr = S_OK; - LOC_CONTROL* pLocControl = NULL; - LPWSTR sczLocStringId = NULL; - - if (pControl->sczText && *pControl->sczText) - { - hr = LocLocalizeString(pWixLoc, &pControl->sczText); - ThmExitOnFailure(hr, "Failed to localize control text."); - } - else if (pControl->sczName) - { - LOC_STRING* plocString = NULL; - - hr = StrAllocFormatted(&sczLocStringId, L"#(loc.%ls)", pControl->sczName); - ThmExitOnFailure(hr, "Failed to format loc string id: %ls", pControl->sczName); - - hr = LocGetString(pWixLoc, sczLocStringId, &plocString); - if (E_NOTFOUND != hr) - { - ThmExitOnFailure(hr, "Failed to get loc string: %ls", pControl->sczName); - - hr = StrAllocString(&pControl->sczText, plocString->wzText, 0); - ThmExitOnFailure(hr, "Failed to copy loc string to control: %ls", plocString->wzText); - } - } - - if (pControl->sczTooltip && *pControl->sczTooltip) - { - hr = LocLocalizeString(pWixLoc, &pControl->sczTooltip); - ThmExitOnFailure(hr, "Failed to localize control tooltip text."); - } - - if (pControl->sczNote && *pControl->sczNote) - { - hr = LocLocalizeString(pWixLoc, &pControl->sczNote); - ThmExitOnFailure(hr, "Failed to localize control note text."); - } - - for (DWORD j = 0; j < pControl->cConditionalText; ++j) - { - hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalText[j].sczText); - ThmExitOnFailure(hr, "Failed to localize conditional text."); - } - - for (DWORD j = 0; j < pControl->cConditionalNotes; ++j) - { - hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalNotes[j].sczText); - ThmExitOnFailure(hr, "Failed to localize conditional note."); - } - - for (DWORD j = 0; j < pControl->cColumns; ++j) - { - hr = LocLocalizeString(pWixLoc, &pControl->ptcColumns[j].pszName); - ThmExitOnFailure(hr, "Failed to localize column text."); - } - - for (DWORD j = 0; j < pControl->cTabs; ++j) - { - hr = LocLocalizeString(pWixLoc, &pControl->pttTabs[j].pszName); - ThmExitOnFailure(hr, "Failed to localize tab text."); - } - - // Localize control's size, location, and text. - if (pControl->sczName) - { - hr = LocGetControl(pWixLoc, pControl->sczName, &pLocControl); - if (E_NOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - ThmExitOnFailure(hr, "Failed to localize control."); - - if (LOC_CONTROL_NOT_SET != pLocControl->nX) - { - pControl->nDefaultDpiX = pLocControl->nX; - } - - if (LOC_CONTROL_NOT_SET != pLocControl->nY) - { - pControl->nDefaultDpiY = pLocControl->nY; - } - - if (LOC_CONTROL_NOT_SET != pLocControl->nWidth) - { - pControl->nDefaultDpiWidth = pLocControl->nWidth; - } - - if (LOC_CONTROL_NOT_SET != pLocControl->nHeight) - { - pControl->nDefaultDpiHeight = pLocControl->nHeight; - } - - if (pLocControl->wzText && *pLocControl->wzText) - { - hr = StrAllocString(&pControl->sczText, pLocControl->wzText, 0); - ThmExitOnFailure(hr, "Failed to localize control text."); - } - } - - hr = LocalizeControls(pControl->cControls, pControl->rgControls, pWixLoc); - -LExit: - ReleaseStr(sczLocStringId); - - return hr; -} - -static HRESULT LoadControlsString( - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in HMODULE hResModule - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - hr = LoadControlString(pControl, hResModule); - ThmExitOnFailure(hr, "Failed to load string for control: %ls", pControl->sczName); - } - -LExit: - return hr; -} - -static HRESULT LoadControlString( - __in THEME_CONTROL* pControl, - __in HMODULE hResModule - ) -{ - HRESULT hr = S_OK; - if (UINT_MAX != pControl->uStringId) - { - hr = ResReadString(hResModule, pControl->uStringId, &pControl->sczText); - ThmExitOnFailure(hr, "Failed to load control text."); - - for (DWORD j = 0; j < pControl->cColumns; ++j) - { - if (UINT_MAX != pControl->ptcColumns[j].uStringId) - { - hr = ResReadString(hResModule, pControl->ptcColumns[j].uStringId, &pControl->ptcColumns[j].pszName); - ThmExitOnFailure(hr, "Failed to load column text."); - } - } - - for (DWORD j = 0; j < pControl->cTabs; ++j) - { - if (UINT_MAX != pControl->pttTabs[j].uStringId) - { - hr = ResReadString(hResModule, pControl->pttTabs[j].uStringId, &pControl->pttTabs[j].pszName); - ThmExitOnFailure(hr, "Failed to load tab text."); - } - } - } - - hr = LoadControlsString(pControl->cControls, pControl->rgControls, hResModule); - -LExit: - return hr; -} - -static void ResizeControls( - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in const RECT* prcParent - ) -{ - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - ResizeControl(pControl, prcParent); - } -} - -static void ResizeControl( - __in THEME_CONTROL* pControl, - __in const RECT* prcParent - ) -{ - int w = 0; - int h = 0; - int x = 0; - int y = 0; - RECT rcControl = { }; - - GetControlDimensions(pControl, prcParent, &w, &h, &x, &y); - ::SetWindowPos(pControl->hWnd, NULL, x, y, w, h, SWP_NOACTIVATE | SWP_NOZORDER); - -#ifdef DEBUG - if (THEME_CONTROL_TYPE_BUTTON == pControl->type) - { - Trace(REPORT_STANDARD, "Resizing button (%ls/%ls) to (%d,%d)+(%d,%d) for parent (%d,%d)-(%d,%d)", - pControl->sczName, pControl->sczText, x, y, w, h, prcParent->left, prcParent->top, prcParent->right, prcParent->bottom); - } -#endif - - if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) - { - SizeListViewColumns(pControl); - - for (DWORD j = 0; j < pControl->cColumns; ++j) - { - if (-1 == ::SendMessageW(pControl->hWnd, LVM_SETCOLUMNWIDTH, (WPARAM) (int) (j), (LPARAM) (pControl->ptcColumns[j].nWidth))) - { - Trace(REPORT_DEBUG, "Failed to resize listview column %u with error %u", j, ::GetLastError()); - return; - } - } - } - - if (pControl->cControls) - { - ::GetClientRect(pControl->hWnd, &rcControl); - ResizeControls(pControl->cControls, pControl->rgControls, &rcControl); - } -} - -static void ScaleThemeFromWindow( - __in THEME* pTheme, - __in UINT nDpi, - __in int x, - __in int y - ) -{ - DWORD dwStyle = GetWindowStyle(pTheme->hwndParent); - BOOL fMenu = NULL != ::GetMenu(pTheme->hwndParent); - DWORD dwExStyle = GetWindowExStyle(pTheme->hwndParent); - - ScaleTheme(pTheme, nDpi, x, y, dwStyle, fMenu, dwExStyle); -} - -static void ScaleTheme( - __in THEME* pTheme, - __in UINT nDpi, - __in int x, - __in int y, - __in DWORD dwStyle, - __in BOOL fMenu, - __in DWORD dwExStyle - ) -{ - RECT rect = { }; - - pTheme->nDpi = nDpi; - - pTheme->nHeight = DpiuScaleValue(pTheme->nDefaultDpiHeight, pTheme->nDpi); - pTheme->nWidth = DpiuScaleValue(pTheme->nDefaultDpiWidth, pTheme->nDpi); - pTheme->nMinimumHeight = DpiuScaleValue(pTheme->nDefaultDpiMinimumHeight, pTheme->nDpi); - pTheme->nMinimumWidth = DpiuScaleValue(pTheme->nDefaultDpiMinimumWidth, pTheme->nDpi); - - rect.left = x; - rect.top = y; - rect.right = x + pTheme->nWidth; - rect.bottom = y + pTheme->nHeight; - DpiuAdjustWindowRect(&rect, dwStyle, fMenu, dwExStyle, pTheme->nDpi); - pTheme->nWindowWidth = rect.right - rect.left; - pTheme->nWindowHeight = rect.bottom - rect.top; - - ScaleControls(pTheme, pTheme->cControls, pTheme->rgControls, pTheme->nDpi); - - if (pTheme->hwndParent) - { - ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWindowWidth, pTheme->nWindowHeight, SWP_NOACTIVATE | SWP_NOZORDER); - } -} - -static void ScaleControls( - __in THEME* pTheme, - __in DWORD cControls, - __in THEME_CONTROL* rgControls, - __in UINT nDpi - ) -{ - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - ScaleControl(pTheme, pControl, nDpi); - } -} - -static void ScaleControl( - __in THEME* pTheme, - __in THEME_CONTROL* pControl, - __in UINT nDpi - ) -{ - HRESULT hr = S_OK; - THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL; - THEME_FONT_INSTANCE* pControlFontInstance = NULL; - - if (pControlFont) - { - hr = EnsureFontInstance(pTheme, pControlFont, &pControlFontInstance); - if (SUCCEEDED(hr) && pControl->hWnd) - { - ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM)pControlFontInstance->hFont, FALSE); - } - } - - if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) - { - for (DWORD i = 0; i < pControl->cColumns; ++i) - { - THEME_COLUMN* pColumn = pControl->ptcColumns + i; - - pColumn->nBaseWidth = DpiuScaleValue(pColumn->nDefaultDpiBaseWidth, nDpi); - } - } - - pControl->nWidth = DpiuScaleValue(pControl->nDefaultDpiWidth, nDpi); - pControl->nHeight = DpiuScaleValue(pControl->nDefaultDpiHeight, nDpi); - pControl->nX = DpiuScaleValue(pControl->nDefaultDpiX, nDpi); - pControl->nY = DpiuScaleValue(pControl->nDefaultDpiY, nDpi); - - if (pControl->cControls) - { - ScaleControls(pTheme, pControl->cControls, pControl->rgControls, nDpi); - } -} - -static void UnloadControls( - __in DWORD cControls, - __in THEME_CONTROL* rgControls - ) -{ - for (DWORD i = 0; i < cControls; ++i) - { - THEME_CONTROL* pControl = rgControls + i; - pControl->hWnd = NULL; - - UnloadControls(pControl->cControls, pControl->rgControls); - } -} - diff --git a/src/dutil/timeutil.cpp b/src/dutil/timeutil.cpp deleted file mode 100644 index b7953c94..00000000 --- a/src/dutil/timeutil.cpp +++ /dev/null @@ -1,385 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define TimeExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) -#define TimeExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) -#define TimeExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) -#define TimeExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) -#define TimeExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) -#define TimeExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) -#define TimeExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_TIMEUTIL, p, x, e, s, __VA_ARGS__) -#define TimeExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, p, x, s, __VA_ARGS__) -#define TimeExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, p, x, e, s, __VA_ARGS__) -#define TimeExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, p, x, s, __VA_ARGS__) -#define TimeExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_TIMEUTIL, e, x, s, __VA_ARGS__) -#define TimeExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_TIMEUTIL, g, x, s, __VA_ARGS__) - -const LPCWSTR DAY_OF_WEEK[] = { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" }; -const LPCWSTR MONTH_OF_YEAR[] = { L"None", L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec" }; -enum TIME_PARSER { DayOfWeek, DayOfMonth, MonthOfYear, Year, Hours, Minutes, Seconds, TimeZone }; -enum TIME_PARSERRFC3339 { RFC3339_Year, RFC3339_Month, RFC3339_Day, RFC3339_Hours, RFC3339_Minutes, RFC3339_Seconds, RFC3339_TimeZone }; - -// prototypes -static HRESULT DayFromString( - __in_z LPCWSTR wzDay, - __out WORD* pwDayOfWeek - ); -static HRESULT MonthFromString( - __in_z LPCWSTR wzMonth, - __out WORD* pwMonthOfYear - ); - - -/******************************************************************** - TimeFromString - converts string to FILETIME - -*******************************************************************/ -extern "C" HRESULT DAPI TimeFromString( - __in_z LPCWSTR wzTime, - __out FILETIME* pFileTime - ) -{ - Assert(wzTime && pFileTime); - - HRESULT hr = S_OK; - LPWSTR pwzTime = NULL; - - SYSTEMTIME sysTime = { }; - TIME_PARSER timeParser = DayOfWeek; - - LPCWSTR pwzStart = NULL; - LPWSTR pwzEnd = NULL; - - hr = StrAllocString(&pwzTime, wzTime, 0); - TimeExitOnFailure(hr, "Failed to copy time."); - - pwzStart = pwzEnd = pwzTime; - while (pwzEnd && *pwzEnd) - { - if (L',' == *pwzEnd || L' ' == *pwzEnd || L':' == *pwzEnd) - { - *pwzEnd = L'\0'; // null terminate - ++pwzEnd; - - while (L' ' == *pwzEnd) - { - ++pwzEnd; // and skip past the blank space - } - - switch (timeParser) - { - case DayOfWeek: - hr = DayFromString(pwzStart, &sysTime.wDayOfWeek); - TimeExitOnFailure(hr, "Failed to convert string to day: %ls", pwzStart); - break; - - case DayOfMonth: - sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case MonthOfYear: - hr = MonthFromString(pwzStart, &sysTime.wMonth); - TimeExitOnFailure(hr, "Failed to convert to month: %ls", pwzStart); - break; - - case Year: - sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case Hours: - sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case Minutes: - sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case Seconds: - sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case TimeZone: - // TODO: do something with this in the future, but this should only hit outside of the while loop. - break; - - default: - break; - } - - pwzStart = pwzEnd; - timeParser = (TIME_PARSER)((int)timeParser + 1); - } - - ++pwzEnd; - } - - - if (!::SystemTimeToFileTime(&sysTime, pFileTime)) - { - TimeExitWithLastError(hr, "Failed to convert system time to file time."); - } - -LExit: - ReleaseStr(pwzTime); - - return hr; -} - -/******************************************************************** - TimeFromString3339 - converts string formated in accorance with RFC3339 to FILETIME - http://tools.ietf.org/html/rfc3339 -*******************************************************************/ -extern "C" HRESULT DAPI TimeFromString3339( - __in_z LPCWSTR wzTime, - __out FILETIME* pFileTime - ) -{ - Assert(wzTime && pFileTime); - - HRESULT hr = S_OK; - LPWSTR pwzTime = NULL; - - SYSTEMTIME sysTime = { }; - TIME_PARSERRFC3339 timeParser = RFC3339_Year; - - LPCWSTR pwzStart = NULL; - LPWSTR pwzEnd = NULL; - - hr = StrAllocString(&pwzTime, wzTime, 0); - TimeExitOnFailure(hr, "Failed to copy time."); - - pwzStart = pwzEnd = pwzTime; - while (pwzEnd && *pwzEnd) - { - if (L'T' == *pwzEnd || L':' == *pwzEnd || L'-' == *pwzEnd) - { - *pwzEnd = L'\0'; // null terminate - ++pwzEnd; - - switch (timeParser) - { - case RFC3339_Year: - sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case RFC3339_Month: - sysTime.wMonth = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case RFC3339_Day: - sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case RFC3339_Hours: - sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case RFC3339_Minutes: - sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case RFC3339_Seconds: - sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10); - break; - - case RFC3339_TimeZone: - // TODO: do something with this in the future, but this should only hit outside of the while loop. - break; - - default: - break; - } - - pwzStart = pwzEnd; - timeParser = (TIME_PARSERRFC3339)((int)timeParser + 1); - } - - ++pwzEnd; - } - - - if (!::SystemTimeToFileTime(&sysTime, pFileTime)) - { - TimeExitWithLastError(hr, "Failed to convert system time to file time."); - } - -LExit: - ReleaseStr(pwzTime); - - return hr; -} -/**************************************************************************** -TimeCurrentTime - gets the current time in string format - -****************************************************************************/ -extern "C" HRESULT DAPI TimeCurrentTime( - __deref_out_z LPWSTR* ppwz, - __in BOOL fGMT - ) -{ - SYSTEMTIME st; - - if (fGMT) - { - ::GetSystemTime(&st); - } - else - { - SYSTEMTIME stGMT; - TIME_ZONE_INFORMATION tzi; - - ::GetTimeZoneInformation(&tzi); - ::GetSystemTime(&stGMT); - ::SystemTimeToTzSpecificLocalTime(&tzi, &stGMT, &st); - } - - return StrAllocFormatted(ppwz, L"%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond); -} - - -/**************************************************************************** -TimeCurrentDateTime - gets the current date and time in string format, - per format described in RFC 3339 -****************************************************************************/ -extern "C" HRESULT DAPI TimeCurrentDateTime( - __deref_out_z LPWSTR* ppwz, - __in BOOL fGMT - ) -{ - SYSTEMTIME st; - - ::GetSystemTime(&st); - - return TimeSystemDateTime(ppwz, &st, fGMT); -} - - -/**************************************************************************** -TimeSystemDateTime - converts the provided system time struct to string format, - per format described in RFC 3339 -****************************************************************************/ -extern "C" HRESULT DAPI TimeSystemDateTime( - __deref_out_z LPWSTR* ppwz, - __in const SYSTEMTIME *pst, - __in BOOL fGMT - ) -{ - DWORD dwAbsBias = 0; - - if (fGMT) - { - return StrAllocFormatted(ppwz, L"%04hu-%02hu-%02huT%02hu:%02hu:%02huZ", pst->wYear, pst->wMonth, pst->wDay, pst->wHour, pst->wMinute, pst->wSecond); - } - else - { - SYSTEMTIME st; - TIME_ZONE_INFORMATION tzi; - - ::GetTimeZoneInformation(&tzi); - ::SystemTimeToTzSpecificLocalTime(&tzi, pst, &st); - dwAbsBias = abs(tzi.Bias); - - return StrAllocFormatted(ppwz, L"%04hu-%02hu-%02huT%02hu:%02hu:%02hu%c%02u:%02u", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, 0 >= tzi.Bias ? L'+' : L'-', dwAbsBias / 60, dwAbsBias % 60); - } -} - - -/**************************************************************************** -TimeSystemToDateTimeString - converts the provided system time struct to - string format representing date and time for the specified locale -****************************************************************************/ -HRESULT DAPI TimeSystemToDateTimeString( - __deref_out_z LPWSTR* ppwz, - __in const SYSTEMTIME* pst, - __in LCID locale - ) -{ - HRESULT hr = S_OK; - const WCHAR * DATE_FORMAT = L"MMM dd',' yyyy',' "; - const WCHAR * TIME_FORMAT = L"hh':'mm':'ss tt"; - int iLenDate = 0; - int iLenTime = 0; - - iLenDate = ::GetDateFormatW(locale, 0, pst, DATE_FORMAT, NULL, 0); - if (0 >= iLenDate) - { - TimeExitWithLastError(hr, "Failed to get date format with NULL"); - } - - iLenTime = ::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, NULL, 0); - if (0 >= iLenTime) - { - TimeExitWithLastError(hr, "Failed to get time format with NULL"); - } - - // Between both lengths we account for 2 null terminators, and only need one, so we subtract one - hr = StrAlloc(ppwz, iLenDate + iLenTime - 1); - TimeExitOnFailure(hr, "Failed to allocate string"); - - if (!::GetDateFormatW(locale, 0, pst, DATE_FORMAT, *ppwz, iLenDate)) - { - TimeExitWithLastError(hr, "Failed to get date format with buffer"); - } - // Space to separate them - (*ppwz)[iLenDate - 1] = ' '; - - if (!::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, (*ppwz) + iLenDate - 1, iLenTime)) - { - TimeExitWithLastError(hr, "Failed to get time format with buffer"); - } - -LExit: - return hr; -} - -/******************************************************************** - DayFromString - converts string to day - -*******************************************************************/ -static HRESULT DayFromString( - __in_z LPCWSTR wzDay, - __out WORD* pwDayOfWeek - ) -{ - HRESULT hr = E_INVALIDARG; // assume we won't find a matching name - - for (WORD i = 0; i < countof(DAY_OF_WEEK); ++i) - { - if (0 == lstrcmpW(wzDay, DAY_OF_WEEK[i])) - { - *pwDayOfWeek = i; - hr = S_OK; - break; - } - } - - return hr; -} - - -/******************************************************************** - MonthFromString - converts string to month - -*******************************************************************/ -static HRESULT MonthFromString( - __in_z LPCWSTR wzMonth, - __out WORD* pwMonthOfYear - ) -{ - HRESULT hr = E_INVALIDARG; // assume we won't find a matching name - - for (WORD i = 0; i < countof(MONTH_OF_YEAR); ++i) - { - if (0 == lstrcmpW(wzMonth, MONTH_OF_YEAR[i])) - { - *pwMonthOfYear = i; - hr = S_OK; - break; - } - } - - return hr; -} diff --git a/src/dutil/uncutil.cpp b/src/dutil/uncutil.cpp deleted file mode 100644 index 415ea198..00000000 --- a/src/dutil/uncutil.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define UncExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) -#define UncExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) -#define UncExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) -#define UncExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) -#define UncExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) -#define UncExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) -#define UncExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_UNCUTIL, p, x, e, s, __VA_ARGS__) -#define UncExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, p, x, s, __VA_ARGS__) -#define UncExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_UNCUTIL, p, x, e, s, __VA_ARGS__) -#define UncExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, p, x, s, __VA_ARGS__) -#define UncExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_UNCUTIL, e, x, s, __VA_ARGS__) -#define UncExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_UNCUTIL, g, x, s, __VA_ARGS__) - -DAPI_(HRESULT) UncConvertFromMountedDrive( - __inout LPWSTR *psczUNCPath, - __in LPCWSTR sczMountedDrivePath - ) -{ - HRESULT hr = S_OK; - DWORD dwLength = 0; - DWORD er = ERROR_SUCCESS; - LPWSTR sczDrive = NULL; - - // Only copy drive letter and colon - hr = StrAllocString(&sczDrive, sczMountedDrivePath, 2); - UncExitOnFailure(hr, "Failed to copy drive"); - - // ERROR_NOT_CONNECTED means it's not a mapped drive - er = ::WNetGetConnectionW(sczDrive, NULL, &dwLength); - if (ERROR_MORE_DATA == er) - { - er = ERROR_SUCCESS; - - hr = StrAlloc(psczUNCPath, dwLength); - UncExitOnFailure(hr, "Failed to allocate string to get raw UNC path of length %u", dwLength); - - er = ::WNetGetConnectionW(sczDrive, *psczUNCPath, &dwLength); - if (ERROR_CONNECTION_UNAVAIL == er) - { - // This means the drive is remembered but not currently connected, this can mean the location is accessible via UNC path but not via mounted drive path - er = ERROR_SUCCESS; - } - UncExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed with buffer provided on drive %ls", sczDrive); - - // Skip drive letter and colon - hr = StrAllocConcat(psczUNCPath, sczMountedDrivePath + 2, 0); - UncExitOnFailure(hr, "Failed to copy rest of database path"); - } - else - { - if (ERROR_SUCCESS == er) - { - er = ERROR_NO_DATA; - } - - UncExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed on drive %ls", sczDrive); - } - -LExit: - ReleaseStr(sczDrive); - - return hr; -} diff --git a/src/dutil/uriutil.cpp b/src/dutil/uriutil.cpp deleted file mode 100644 index 7913c22e..00000000 --- a/src/dutil/uriutil.cpp +++ /dev/null @@ -1,553 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define UriExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) -#define UriExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) -#define UriExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) -#define UriExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) -#define UriExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) -#define UriExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) -#define UriExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__) -#define UriExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__) -#define UriExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__) -#define UriExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__) -#define UriExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_URIUTIL, e, x, s, __VA_ARGS__) -#define UriExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_URIUTIL, g, x, s, __VA_ARGS__) - - -// -// UriCanonicalize - canonicalizes a URI. -// -extern "C" HRESULT DAPI UriCanonicalize( - __inout_z LPWSTR* psczUri - ) -{ - HRESULT hr = S_OK; - WCHAR wz[INTERNET_MAX_URL_LENGTH] = { }; - DWORD cch = countof(wz); - - if (!::InternetCanonicalizeUrlW(*psczUri, wz, &cch, ICU_DECODE)) - { - UriExitWithLastError(hr, "Failed to canonicalize URI."); - } - - hr = StrAllocString(psczUri, wz, cch); - UriExitOnFailure(hr, "Failed copy canonicalized URI."); - -LExit: - return hr; -} - - -// -// UriCrack - cracks a URI into constituent parts. -// -extern "C" HRESULT DAPI UriCrack( - __in_z LPCWSTR wzUri, - __out_opt INTERNET_SCHEME* pScheme, - __deref_opt_out_z LPWSTR* psczHostName, - __out_opt INTERNET_PORT* pPort, - __deref_opt_out_z LPWSTR* psczUser, - __deref_opt_out_z LPWSTR* psczPassword, - __deref_opt_out_z LPWSTR* psczPath, - __deref_opt_out_z LPWSTR* psczQueryString - ) -{ - HRESULT hr = S_OK; - URL_COMPONENTSW components = { }; - WCHAR wzHostName[INTERNET_MAX_HOST_NAME_LENGTH + 1]; - WCHAR wzUserName[INTERNET_MAX_USER_NAME_LENGTH + 1]; - WCHAR wzPassword[INTERNET_MAX_PASSWORD_LENGTH + 1]; - WCHAR wzPath[INTERNET_MAX_PATH_LENGTH + 1]; - WCHAR wzQueryString[INTERNET_MAX_PATH_LENGTH + 1]; - - components.dwStructSize = sizeof(URL_COMPONENTSW); - - if (psczHostName) - { - components.lpszHostName = wzHostName; - components.dwHostNameLength = countof(wzHostName); - } - - if (psczUser) - { - components.lpszUserName = wzUserName; - components.dwUserNameLength = countof(wzUserName); - } - - if (psczPassword) - { - components.lpszPassword = wzPassword; - components.dwPasswordLength = countof(wzPassword); - } - - if (psczPath) - { - components.lpszUrlPath = wzPath; - components.dwUrlPathLength = countof(wzPath); - } - - if (psczQueryString) - { - components.lpszExtraInfo = wzQueryString; - components.dwExtraInfoLength = countof(wzQueryString); - } - - if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &components)) - { - UriExitWithLastError(hr, "Failed to crack URI."); - } - - if (pScheme) - { - *pScheme = components.nScheme; - } - - if (psczHostName) - { - hr = StrAllocString(psczHostName, components.lpszHostName, components.dwHostNameLength); - UriExitOnFailure(hr, "Failed to copy host name."); - } - - if (pPort) - { - *pPort = components.nPort; - } - - if (psczUser) - { - hr = StrAllocString(psczUser, components.lpszUserName, components.dwUserNameLength); - UriExitOnFailure(hr, "Failed to copy user name."); - } - - if (psczPassword) - { - hr = StrAllocString(psczPassword, components.lpszPassword, components.dwPasswordLength); - UriExitOnFailure(hr, "Failed to copy password."); - } - - if (psczPath) - { - hr = StrAllocString(psczPath, components.lpszUrlPath, components.dwUrlPathLength); - UriExitOnFailure(hr, "Failed to copy path."); - } - - if (psczQueryString) - { - hr = StrAllocString(psczQueryString, components.lpszExtraInfo, components.dwExtraInfoLength); - UriExitOnFailure(hr, "Failed to copy query string."); - } - -LExit: - return hr; -} - - -// -// UriCrackEx - cracks a URI into URI_INFO. -// -extern "C" HRESULT DAPI UriCrackEx( - __in_z LPCWSTR wzUri, - __in URI_INFO* pUriInfo - ) -{ - HRESULT hr = S_OK; - - hr = UriCrack(wzUri, &pUriInfo->scheme, &pUriInfo->sczHostName, &pUriInfo->port, &pUriInfo->sczUser, &pUriInfo->sczPassword, &pUriInfo->sczPath, &pUriInfo->sczQueryString); - UriExitOnFailure(hr, "Failed to crack URI."); - -LExit: - return hr; -} - - -// -// UriInfoUninitialize - frees the memory in a URI_INFO struct. -// -extern "C" void DAPI UriInfoUninitialize( - __in URI_INFO* pUriInfo - ) -{ - ReleaseStr(pUriInfo->sczHostName); - ReleaseStr(pUriInfo->sczUser); - ReleaseStr(pUriInfo->sczPassword); - ReleaseStr(pUriInfo->sczPath); - ReleaseStr(pUriInfo->sczQueryString); - memset(pUriInfo, 0, sizeof(URI_INFO)); -} - - -// -// UriCreate - creates a URI from constituent parts. -// -extern "C" HRESULT DAPI UriCreate( - __inout_z LPWSTR* psczUri, - __in INTERNET_SCHEME scheme, - __in_z_opt LPWSTR wzHostName, - __in INTERNET_PORT port, - __in_z_opt LPWSTR wzUser, - __in_z_opt LPWSTR wzPassword, - __in_z_opt LPWSTR wzPath, - __in_z_opt LPWSTR wzQueryString - ) -{ - HRESULT hr = S_OK; - WCHAR wz[INTERNET_MAX_URL_LENGTH] = { }; - DWORD cch = countof(wz); - URL_COMPONENTSW components = { }; - - components.dwStructSize = sizeof(URL_COMPONENTSW); - components.nScheme = scheme; - components.lpszHostName = wzHostName; - components.nPort = port; - components.lpszUserName = wzUser; - components.lpszPassword = wzPassword; - components.lpszUrlPath = wzPath; - components.lpszExtraInfo = wzQueryString; - - if (!::InternetCreateUrlW(&components, ICU_ESCAPE, wz, &cch)) - { - UriExitWithLastError(hr, "Failed to create URI."); - } - - hr = StrAllocString(psczUri, wz, cch); - UriExitOnFailure(hr, "Failed copy created URI."); - -LExit: - return hr; -} - - -// -// UriGetServerAndResource - gets the server and resource as independent strings from a URI. -// -// NOTE: This function is useful for the InternetConnect/HttpRequest APIs. -// -extern "C" HRESULT DAPI UriGetServerAndResource( - __in_z LPCWSTR wzUri, - __out_z LPWSTR* psczServer, - __out_z LPWSTR* psczResource - ) -{ - HRESULT hr = S_OK; - INTERNET_SCHEME scheme = INTERNET_SCHEME_UNKNOWN; - LPWSTR sczHostName = NULL; - INTERNET_PORT port = INTERNET_INVALID_PORT_NUMBER; - LPWSTR sczUser = NULL; - LPWSTR sczPassword = NULL; - LPWSTR sczPath = NULL; - LPWSTR sczQueryString = NULL; - - hr = UriCrack(wzUri, &scheme, &sczHostName, &port, &sczUser, &sczPassword, &sczPath, &sczQueryString); - UriExitOnFailure(hr, "Failed to crack URI."); - - hr = UriCreate(psczServer, scheme, sczHostName, port, sczUser, sczPassword, NULL, NULL); - UriExitOnFailure(hr, "Failed to allocate server URI."); - - hr = UriCreate(psczResource, INTERNET_SCHEME_UNKNOWN, NULL, INTERNET_INVALID_PORT_NUMBER, NULL, NULL, sczPath, sczQueryString); - UriExitOnFailure(hr, "Failed to allocate resource URI."); - -LExit: - ReleaseStr(sczQueryString); - ReleaseStr(sczPath); - ReleaseStr(sczPassword); - ReleaseStr(sczUser); - ReleaseStr(sczHostName); - - return hr; -} - - -// -// UriFile - returns the file part of the URI. -// -extern "C" HRESULT DAPI UriFile( - __deref_out_z LPWSTR* psczFile, - __in_z LPCWSTR wzUri - ) -{ - HRESULT hr = S_OK; - WCHAR wz[MAX_PATH + 1]; - DWORD cch = countof(wz); - URL_COMPONENTSW uc = { }; - - uc.dwStructSize = sizeof(uc); - uc.lpszUrlPath = wz; - uc.dwUrlPathLength = cch; - - if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &uc)) - { - UriExitWithLastError(hr, "Failed to crack URI."); - } - - // Copy only the file name. Fortunately, PathFile() understands that - // forward slashes can be directory separators like backslashes. - hr = StrAllocString(psczFile, PathFile(wz), 0); - UriExitOnFailure(hr, "Failed to copy file name"); - -LExit: - return hr; -} - - -/******************************************************************* - UriProtocol - determines the protocol of a URI. - -********************************************************************/ -extern "C" HRESULT DAPI UriProtocol( - __in_z LPCWSTR wzUri, - __out URI_PROTOCOL* pProtocol - ) -{ - Assert(wzUri && *wzUri); - Assert(pProtocol); - - HRESULT hr = S_OK; - - if ((L'h' == wzUri[0] || L'H' == wzUri[0]) && - (L't' == wzUri[1] || L'T' == wzUri[1]) && - (L't' == wzUri[2] || L'T' == wzUri[2]) && - (L'p' == wzUri[3] || L'P' == wzUri[3]) && - (L's' == wzUri[4] || L'S' == wzUri[4]) && - L':' == wzUri[5] && - L'/' == wzUri[6] && - L'/' == wzUri[7]) - { - *pProtocol = URI_PROTOCOL_HTTPS; - } - else if ((L'h' == wzUri[0] || L'H' == wzUri[0]) && - (L't' == wzUri[1] || L'T' == wzUri[1]) && - (L't' == wzUri[2] || L'T' == wzUri[2]) && - (L'p' == wzUri[3] || L'P' == wzUri[3]) && - L':' == wzUri[4] && - L'/' == wzUri[5] && - L'/' == wzUri[6]) - { - *pProtocol = URI_PROTOCOL_HTTP; - } - else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) && - (L't' == wzUri[1] || L'T' == wzUri[1]) && - (L'p' == wzUri[2] || L'P' == wzUri[2]) && - L':' == wzUri[3] && - L'/' == wzUri[4] && - L'/' == wzUri[5]) - { - *pProtocol = URI_PROTOCOL_FTP; - } - else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) && - (L'i' == wzUri[1] || L'I' == wzUri[1]) && - (L'l' == wzUri[2] || L'L' == wzUri[2]) && - (L'e' == wzUri[3] || L'E' == wzUri[3]) && - L':' == wzUri[4] && - L'/' == wzUri[5] && - L'/' == wzUri[6]) - { - *pProtocol = URI_PROTOCOL_FILE; - } - else - { - *pProtocol = URI_PROTOCOL_UNKNOWN; - } - - return hr; -} - - -/******************************************************************* - UriRoot - returns the root of the path specified in the URI. - - examples: - file:///C:\path\path -> C:\ - file://server/share/path/path -> \\server\share - http://www.example.com/path/path -> http://www.example.com/ - ftp://ftp.example.com/path/path -> ftp://www.example.com/ - - NOTE: This function should only be used on cannonicalized URIs. - It does not cannonicalize itself. -********************************************************************/ -extern "C" HRESULT DAPI UriRoot( - __in_z LPCWSTR wzUri, - __out LPWSTR* ppwzRoot, - __out_opt URI_PROTOCOL* pProtocol - ) -{ - Assert(wzUri && *wzUri); - Assert(ppwzRoot); - - HRESULT hr = S_OK; - URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; - LPCWSTR pwcSlash = NULL; - - hr = UriProtocol(wzUri, &protocol); - UriExitOnFailure(hr, "Invalid URI."); - - switch (protocol) - { - case URI_PROTOCOL_FILE: - if (L'/' == wzUri[7]) // file path - { - if (((L'a' <= wzUri[8] && L'z' >= wzUri[8]) || (L'A' <= wzUri[8] && L'Z' >= wzUri[8])) && L':' == wzUri[9]) - { - hr = StrAlloc(ppwzRoot, 4); - UriExitOnFailure(hr, "Failed to allocate string for root of URI."); - *ppwzRoot[0] = wzUri[8]; - *ppwzRoot[1] = L':'; - *ppwzRoot[2] = L'\\'; - *ppwzRoot[3] = L'\0'; - } - else - { - hr = E_INVALIDARG; - UriExitOnFailure(hr, "Invalid file path in URI."); - } - } - else // UNC share - { - pwcSlash = wcschr(wzUri + 8, L'/'); - if (!pwcSlash) - { - hr = E_INVALIDARG; - UriExitOnFailure(hr, "Invalid server name in URI."); - } - else - { - hr = StrAllocString(ppwzRoot, L"\\\\", 64); - UriExitOnFailure(hr, "Failed to allocate string for root of URI."); - - pwcSlash = wcschr(pwcSlash + 1, L'/'); - if (pwcSlash) - { - hr = StrAllocConcat(ppwzRoot, wzUri + 8, pwcSlash - wzUri - 8); - UriExitOnFailure(hr, "Failed to add server/share to root of URI."); - } - else - { - hr = StrAllocConcat(ppwzRoot, wzUri + 8, 0); - UriExitOnFailure(hr, "Failed to add server/share to root of URI."); - } - - // replace all slashes with backslashes to be truly UNC. - for (LPWSTR pwc = *ppwzRoot; pwc && *pwc; ++pwc) - { - if (L'/' == *pwc) - { - *pwc = L'\\'; - } - } - } - } - break; - - case URI_PROTOCOL_FTP: - pwcSlash = wcschr(wzUri + 6, L'/'); - if (pwcSlash) - { - hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); - UriExitOnFailure(hr, "Failed allocate root from URI."); - } - else - { - hr = StrAllocString(ppwzRoot, wzUri, 0); - UriExitOnFailure(hr, "Failed allocate root from URI."); - } - break; - - case URI_PROTOCOL_HTTP: - pwcSlash = wcschr(wzUri + 7, L'/'); - if (pwcSlash) - { - hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); - UriExitOnFailure(hr, "Failed allocate root from URI."); - } - else - { - hr = StrAllocString(ppwzRoot, wzUri, 0); - UriExitOnFailure(hr, "Failed allocate root from URI."); - } - break; - - default: - hr = E_INVALIDARG; - UriExitOnFailure(hr, "Unknown URI protocol."); - } - - if (pProtocol) - { - *pProtocol = protocol; - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI UriResolve( - __in_z LPCWSTR wzUri, - __in_opt LPCWSTR wzBaseUri, - __out LPWSTR* ppwzResolvedUri, - __out_opt URI_PROTOCOL* pResolvedProtocol - ) -{ - UNREFERENCED_PARAMETER(wzUri); - UNREFERENCED_PARAMETER(wzBaseUri); - UNREFERENCED_PARAMETER(ppwzResolvedUri); - UNREFERENCED_PARAMETER(pResolvedProtocol); - - HRESULT hr = E_NOTIMPL; -#if 0 - URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; - - hr = UriProtocol(wzUri, &protocol); - UriExitOnFailure(hr, "Failed to determine protocol for URL: %ls", wzUri); - - UriExitOnNull(ppwzResolvedUri, hr, E_INVALIDARG, "Failed to resolve URI, because no method of output was provided"); - - if (URI_PROTOCOL_UNKNOWN == protocol) - { - UriExitOnNull(wzBaseUri, hr, E_INVALIDARG, "Failed to resolve URI - base URI provided was NULL"); - - if (L'/' == *wzUri || L'\\' == *wzUri) - { - hr = UriRoot(wzBaseUri, ppwzResolvedUri, &protocol); - UriExitOnFailure(hr, "Failed to get root from URI: %ls", wzBaseUri); - - hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); - UriExitOnFailure(hr, "Failed to concat file to base URI."); - } - else - { - hr = UriProtocol(wzBaseUri, &protocol); - UriExitOnFailure(hr, "Failed to get protocol of base URI: %ls", wzBaseUri); - - LPCWSTR pwcFile = const_cast (UriFile(wzBaseUri)); - if (!pwcFile) - { - hr = E_INVALIDARG; - UriExitOnFailure(hr, "Failed to get file from base URI: %ls", wzBaseUri); - } - - hr = StrAllocString(ppwzResolvedUri, wzBaseUri, pwcFile - wzBaseUri); - UriExitOnFailure(hr, "Failed to allocate string for resolved URI."); - - hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); - UriExitOnFailure(hr, "Failed to concat file to resolved URI."); - } - } - else - { - hr = StrAllocString(ppwzResolvedUri, wzUri, 0); - UriExitOnFailure(hr, "Failed to copy resolved URI."); - } - - if (pResolvedProtocol) - { - *pResolvedProtocol = protocol; - } - -LExit: -#endif - return hr; -} diff --git a/src/dutil/userutil.cpp b/src/dutil/userutil.cpp deleted file mode 100644 index ca6d5480..00000000 --- a/src/dutil/userutil.cpp +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// UserExit macros -#define UserExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) -#define UserExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) -#define UserExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) -#define UserExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) -#define UserExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) -#define UserExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) -#define UserExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__) -#define UserExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__) -#define UserExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__) -#define UserExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__) -#define UserExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_USERUTIL, e, x, s, __VA_ARGS__) -#define UserExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_USERUTIL, g, x, s, __VA_ARGS__) - -static BOOL CheckIsMemberHelper( - __in_z LPCWSTR pwzGroupUserDomain, - __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData, - __in DWORD cguiGroupData - ); - -/******************************************************************* - UserBuildDomainUserName - builds a DOMAIN\USERNAME string - -********************************************************************/ -extern "C" HRESULT DAPI UserBuildDomainUserName( - __out_ecount_z(cchDest) LPWSTR wzDest, - __in int cchDest, - __in_z LPCWSTR pwzName, - __in_z LPCWSTR pwzDomain - ) -{ - HRESULT hr = S_OK; - DWORD cchLeft = cchDest; - WCHAR* pwz = wzDest; - DWORD cchWz = cchDest; - DWORD cch; - - cch = lstrlenW(pwzDomain); - if (cch >= cchLeft) - { - hr = ERROR_MORE_DATA; - UserExitOnFailure(hr, "Buffer size is not big enough to hold domain name: %ls", pwzDomain); - } - else if (cch > 0) - { - // handle the domain case - - hr = ::StringCchCopyNW(pwz, cchWz, pwzDomain, cchLeft - 1); // last parameter does not include '\0' - UserExitOnFailure(hr, "Failed to copy Domain onto string."); - - cchLeft -= cch; - pwz += cch; - cchWz -= cch; - - if (1 >= cchLeft) - { - hr = ERROR_MORE_DATA; - UserExitOnFailure(hr, "Insufficient buffer size while building domain user name"); - } - - hr = ::StringCchCopyNW(pwz, cchWz, L"\\", cchLeft - 1); // last parameter does not include '\0' - UserExitOnFailure(hr, "Failed to copy backslash onto string."); - - --cchLeft; - ++pwz; - --cchWz; - } - - cch = lstrlenW(pwzName); - if (cch >= cchLeft) - { - hr = ERROR_MORE_DATA; - UserExitOnFailure(hr, "Buffer size is not big enough to hold user name: %ls", pwzName); - } - - hr = ::StringCchCopyNW(pwz, cchWz, pwzName, cchLeft - 1); // last parameter does not include '\0' - UserExitOnFailure(hr, "Failed to copy User name onto string."); - -LExit: - return hr; -} - - -/******************************************************************* - Checks whether a user is a member of a group - outputs the result via lpfMember -********************************************************************/ -extern "C" HRESULT DAPI UserCheckIsMember( - __in_z LPCWSTR pwzName, - __in_z LPCWSTR pwzDomain, - __in_z LPCWSTR pwzGroupName, - __in_z LPCWSTR pwzGroupDomain, - __out LPBOOL lpfMember - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - - DWORD dwRead = 0; - DWORD dwTotal = 0; - LPCWSTR wz = NULL; - GROUP_USERS_INFO_0 *pguiGroupData = NULL; - WCHAR wzGroupUserDomain[MAX_DARWIN_COLUMN + 1]; // GROUPDOMAIN\GROUPNAME - WCHAR wzUserDomain[MAX_DARWIN_COLUMN + 1]; // USERDOMAIN\USERNAME - BSTR bstrUser = NULL; - BSTR bstrGroup = NULL; - - IADsGroup *pGroup = NULL; - VARIANT_BOOL vtBoolResult = VARIANT_FALSE; - - hr = UserBuildDomainUserName(wzGroupUserDomain, countof(wzGroupUserDomain), pwzGroupName, pwzGroupDomain); - UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); - - hr = UserBuildDomainUserName(wzUserDomain, countof(wzUserDomain), pwzName, pwzDomain); - UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); - - if (pwzDomain && *pwzDomain) - { - wz = pwzDomain; - } - - er = ::NetUserGetGroups(wz, pwzName, 0, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal); - // Ignore these errors, and just go to the fallback checks - if (ERROR_BAD_NETPATH == er || ERROR_INVALID_NAME == er || NERR_UserNotFound == er) - { - Trace(REPORT_VERBOSE, "failed to get groups for user %ls from domain %ls with error code 0x%x - continuing", pwzName, (wz != NULL) ? wz : L"", HRESULT_FROM_WIN32(er)); - er = ERROR_SUCCESS; - } - UserExitOnWin32Error(er, hr, "Failed to get list of global groups for user while checking group membership information for user: %ls", pwzName); - - if (dwRead != dwTotal) - { - hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); - UserExitOnRootFailure(hr, "Failed to get entire list of groups (global) for user while checking group membership information for user: %ls", pwzName); - } - - if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead)) - { - *lpfMember = TRUE; - ExitFunction1(hr = S_OK); - } - - if (NULL != pguiGroupData) - { - ::NetApiBufferFree(pguiGroupData); - pguiGroupData = NULL; - } - - // If we fail with the global groups, try again with the local groups - er = ::NetUserGetLocalGroups(NULL, wzUserDomain, 0, LG_INCLUDE_INDIRECT, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal); - // Ignore these errors, and just go to the fallback checks - if (NERR_UserNotFound == er || NERR_DCNotFound == er || RPC_S_SERVER_UNAVAILABLE == er) - { - Trace(REPORT_VERBOSE, "failed to get local groups for user %ls from domain %ls with error code 0x%x - continuing", pwzName, (wz != NULL) ? wz : L"", HRESULT_FROM_WIN32(er)); - er = ERROR_SUCCESS; - } - UserExitOnWin32Error(er, hr, "Failed to get list of groups for user while checking group membership information for user: %ls", pwzName); - - if (dwRead != dwTotal) - { - hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); - UserExitOnRootFailure(hr, "Failed to get entire list of groups (local) for user while checking group membership information for user: %ls", pwzName); - } - - if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead)) - { - *lpfMember = TRUE; - ExitFunction1(hr = S_OK); - } - - // If the above methods failed, let's try active directory - hr = UserCreateADsPath(pwzDomain, pwzName, &bstrUser); - UserExitOnFailure(hr, "failed to create user ADsPath in order to check group membership for group: %ls domain: %ls", pwzName, pwzDomain); - - hr = UserCreateADsPath(pwzGroupDomain, pwzGroupName, &bstrGroup); - UserExitOnFailure(hr, "failed to create group ADsPath in order to check group membership for group: %ls domain: %ls", pwzGroupName, pwzGroupDomain); - - if (lstrlenW(pwzGroupDomain) > 0) - { - hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); - UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); - - hr = pGroup->IsMember(bstrUser, &vtBoolResult); - UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); - } - - if (vtBoolResult) - { - *lpfMember = TRUE; - ExitFunction1(hr = S_OK); - } - - hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); - UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); - - hr = pGroup->IsMember(bstrUser, &vtBoolResult); - UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); - - if (vtBoolResult) - { - *lpfMember = TRUE; - ExitFunction1(hr = S_OK); - } - -LExit: - ReleaseObject(pGroup); - ReleaseBSTR(bstrUser); - ReleaseBSTR(bstrGroup); - - if (NULL != pguiGroupData) - { - ::NetApiBufferFree(pguiGroupData); - } - - return hr; -} - - -/******************************************************************* - Takes a domain and name, and allocates a BSTR which represents - DOMAIN\NAME's active directory path. The BSTR this function returns - should be released manually using the ReleaseBSTR() macro. -********************************************************************/ -extern "C" HRESULT DAPI UserCreateADsPath( - __in_z LPCWSTR wzObjectDomain, - __in_z LPCWSTR wzObjectName, - __out BSTR *pbstrAdsPath - ) -{ - Assert(wzObjectDomain && wzObjectName && *wzObjectName); - - HRESULT hr = S_OK; - LPWSTR pwzAdsPath = NULL; - - hr = StrAllocString(&pwzAdsPath, L"WinNT://", 0); - UserExitOnFailure(hr, "failed to allocate AdsPath string"); - - if (*wzObjectDomain) - { - hr = StrAllocFormatted(&pwzAdsPath, L"%s/%s", wzObjectDomain, wzObjectName); - UserExitOnFailure(hr, "failed to allocate AdsPath string"); - } - else if (NULL != wcsstr(wzObjectName, L"\\") || NULL != wcsstr(wzObjectName, L"/")) - { - hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0); - UserExitOnFailure(hr, "failed to concat objectname: %ls", wzObjectName); - } - else - { - hr = StrAllocConcat(&pwzAdsPath, L"Localhost/", 0); - UserExitOnFailure(hr, "failed to concat LocalHost/"); - - hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0); - UserExitOnFailure(hr, "failed to concat object name: %ls", wzObjectName); - } - - *pbstrAdsPath = ::SysAllocString(pwzAdsPath); - if (NULL == *pbstrAdsPath) - { - hr = E_OUTOFMEMORY; - } - -LExit: - ReleaseStr(pwzAdsPath); - - return hr; -} - - -/******************************************************************* - Helper function to check if pwzGroupUserDomain (in form of "domain\username" is - a member of a given LOCALGROUP_USERS_INFO_0 structure. Useful to pass in the - output from both NetUserGetGroups() and NetUserGetLocalGroups() -********************************************************************/ -static BOOL CheckIsMemberHelper( - __in_z LPCWSTR pwzGroupUserDomain, - __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData, - __in DWORD cguiGroupData - ) -{ - if (NULL == pguiGroupData) - { - return FALSE; - } - - for (DWORD dwCounter = 0; dwCounter < cguiGroupData; ++dwCounter) - { - // If the user is a member of the group, set the output flag to true - if (0 == lstrcmpiW(pwzGroupUserDomain, pguiGroupData[dwCounter].grui0_name)) - { - return TRUE; - } - } - - return FALSE; -} diff --git a/src/dutil/verutil.cpp b/src/dutil/verutil.cpp deleted file mode 100644 index 21626f94..00000000 --- a/src/dutil/verutil.cpp +++ /dev/null @@ -1,647 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// Exit macros -#define VerExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) -#define VerExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) -#define VerExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) -#define VerExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) -#define VerExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) -#define VerExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) -#define VerExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__) -#define VerExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__) -#define VerExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__) -#define VerExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__) -#define VerExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_VERUTIL, e, x, s, __VA_ARGS__) - -// constants -const DWORD GROW_RELEASE_LABELS = 3; - -// Forward declarations. -static int CompareDword( - __in const DWORD& dw1, - __in const DWORD& dw2 - ); -static HRESULT CompareReleaseLabel( - __in const VERUTIL_VERSION_RELEASE_LABEL* p1, - __in LPCWSTR wzVersion1, - __in const VERUTIL_VERSION_RELEASE_LABEL* p2, - __in LPCWSTR wzVersion2, - __out int* pnResult - ); -static HRESULT CompareVersionSubstring( - __in LPCWSTR wzString1, - __in int cchCount1, - __in LPCWSTR wzString2, - __in int cchCount2, - __out int* pnResult - ); - - -DAPI_(HRESULT) VerCompareParsedVersions( - __in_opt VERUTIL_VERSION* pVersion1, - __in_opt VERUTIL_VERSION* pVersion2, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - int nResult = 0; - DWORD cMaxReleaseLabels = 0; - BOOL fCompareMetadata = FALSE; - - if (pVersion1 && !pVersion1->sczVersion || - pVersion2 && !pVersion2->sczVersion) - { - ExitFunction1(hr = E_INVALIDARG); - } - - if (pVersion1 == pVersion2) - { - ExitFunction1(nResult = 0); - } - else if (pVersion1 && !pVersion2) - { - ExitFunction1(nResult = 1); - } - else if (!pVersion1 && pVersion2) - { - ExitFunction1(nResult = -1); - } - - nResult = CompareDword(pVersion1->dwMajor, pVersion2->dwMajor); - if (0 != nResult) - { - ExitFunction(); - } - - nResult = CompareDword(pVersion1->dwMinor, pVersion2->dwMinor); - if (0 != nResult) - { - ExitFunction(); - } - - nResult = CompareDword(pVersion1->dwPatch, pVersion2->dwPatch); - if (0 != nResult) - { - ExitFunction(); - } - - nResult = CompareDword(pVersion1->dwRevision, pVersion2->dwRevision); - if (0 != nResult) - { - ExitFunction(); - } - - if (pVersion1->cReleaseLabels) - { - if (pVersion2->cReleaseLabels) - { - cMaxReleaseLabels = max(pVersion1->cReleaseLabels, pVersion2->cReleaseLabels); - } - else - { - ExitFunction1(nResult = -1); - } - } - else if (pVersion2->cReleaseLabels) - { - ExitFunction1(nResult = 1); - } - - if (cMaxReleaseLabels) - { - for (DWORD i = 0; i < cMaxReleaseLabels; ++i) - { - VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel1 = pVersion1->cReleaseLabels > i ? pVersion1->rgReleaseLabels + i : NULL; - VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel2 = pVersion2->cReleaseLabels > i ? pVersion2->rgReleaseLabels + i : NULL; - - hr = CompareReleaseLabel(pReleaseLabel1, pVersion1->sczVersion, pReleaseLabel2, pVersion2->sczVersion, &nResult); - if (FAILED(hr) || 0 != nResult) - { - ExitFunction(); - } - } - } - - if (pVersion1->fInvalid) - { - if (!pVersion2->fInvalid) - { - ExitFunction1(nResult = -1); - } - else - { - fCompareMetadata = TRUE; - } - } - else if (pVersion2->fInvalid) - { - ExitFunction1(nResult = 1); - } - - if (fCompareMetadata) - { - hr = CompareVersionSubstring(pVersion1->sczVersion + pVersion1->cchMetadataOffset, -1, pVersion2->sczVersion + pVersion2->cchMetadataOffset, -1, &nResult); - } - -LExit: - *pnResult = nResult; - return hr; -} - -DAPI_(HRESULT) VerCompareStringVersions( - __in_z LPCWSTR wzVersion1, - __in_z LPCWSTR wzVersion2, - __in BOOL fStrict, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - int nResult = 0; - - hr = VerParseVersion(wzVersion1, 0, fStrict, &pVersion1); - VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, fStrict, &pVersion2); - VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion2); - - hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult); - VerExitOnFailure(hr, "Failed to compare parsed Verutil versions '%ls' and '%ls'.", wzVersion1, wzVersion2); - -LExit: - *pnResult = nResult; - - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - - return hr; -} - -DAPI_(HRESULT) VerCopyVersion( - __in VERUTIL_VERSION* pSource, - __out VERUTIL_VERSION** ppVersion - ) -{ - HRESULT hr = S_OK; - VERUTIL_VERSION* pCopy = NULL; - - pCopy = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); - VerExitOnNull(pCopy, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version copy."); - - hr = StrAllocString(&pCopy->sczVersion, pSource->sczVersion, 0); - VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", pSource->sczVersion); - - pCopy->dwMajor = pSource->dwMajor; - pCopy->dwMinor = pSource->dwMinor; - pCopy->dwPatch = pSource->dwPatch; - pCopy->dwRevision = pSource->dwRevision; - - if (pSource->cReleaseLabels) - { - hr = MemEnsureArraySize(reinterpret_cast(&pCopy->rgReleaseLabels), 0, sizeof(VERUTIL_VERSION_RELEASE_LABEL), pSource->cReleaseLabels); - VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels copies."); - - pCopy->cReleaseLabels = pSource->cReleaseLabels; - - for (DWORD i = 0; i < pCopy->cReleaseLabels; ++i) - { - VERUTIL_VERSION_RELEASE_LABEL* pSourceLabel = pSource->rgReleaseLabels + i; - VERUTIL_VERSION_RELEASE_LABEL* pCopyLabel = pCopy->rgReleaseLabels + i; - - pCopyLabel->cchLabelOffset = pSourceLabel->cchLabelOffset; - pCopyLabel->cchLabel = pSourceLabel->cchLabel; - pCopyLabel->fNumeric = pSourceLabel->fNumeric; - pCopyLabel->dwValue = pSourceLabel->dwValue; - } - } - - pCopy->cchMetadataOffset = pSource->cchMetadataOffset; - pCopy->fInvalid = pSource->fInvalid; - - *ppVersion = pCopy; - pCopy = NULL; - -LExit: - ReleaseVerutilVersion(pCopy); - - return hr; -} - -DAPI_(void) VerFreeVersion( - __in VERUTIL_VERSION* pVersion - ) -{ - if (pVersion) - { - ReleaseStr(pVersion->sczVersion); - ReleaseMem(pVersion->rgReleaseLabels); - ReleaseMem(pVersion); - } -} - -DAPI_(HRESULT) VerParseVersion( - __in_z LPCWSTR wzVersion, - __in SIZE_T cchVersion, - __in BOOL fStrict, - __out VERUTIL_VERSION** ppVersion - ) -{ - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion = NULL; - LPCWSTR wzEnd = NULL; - LPCWSTR wzPartBegin = NULL; - LPCWSTR wzPartEnd = NULL; - BOOL fInvalid = FALSE; - BOOL fLastPart = FALSE; - BOOL fTrailingDot = FALSE; - BOOL fParsedVersionNumber = FALSE; - BOOL fExpectedReleaseLabels = FALSE; - DWORD iPart = 0; - - if (!wzVersion || !ppVersion) - { - ExitFunction1(hr = E_INVALIDARG); - } - - // Get string length if not provided. - if (!cchVersion) - { - hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast(&cchVersion)); - VerExitOnRootFailure(hr, "Failed to get length of version string: %ls", wzVersion); - } - else if (INT_MAX < cchVersion) - { - VerExitOnRootFailure(hr = E_INVALIDARG, "Version string is too long: %Iu", cchVersion); - } - - if (L'v' == *wzVersion || L'V' == *wzVersion) - { - ++wzVersion; - --cchVersion; - } - - pVersion = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); - VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version '%ls'.", wzVersion); - - hr = StrAllocString(&pVersion->sczVersion, wzVersion, cchVersion); - VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", wzVersion); - - wzVersion = wzPartBegin = wzPartEnd = pVersion->sczVersion; - - // Save end pointer. - wzEnd = wzVersion + cchVersion; - - // Parse version number - while (wzPartBegin < wzEnd) - { - fTrailingDot = FALSE; - - // Find end of part. - for (;;) - { - if (wzPartEnd >= wzEnd) - { - fLastPart = TRUE; - break; - } - - switch (*wzPartEnd) - { - case L'0': - case L'1': - case L'2': - case L'3': - case L'4': - case L'5': - case L'6': - case L'7': - case L'8': - case L'9': - ++wzPartEnd; - continue; - case L'.': - fTrailingDot = TRUE; - break; - case L'-': - case L'+': - fLastPart = TRUE; - break; - default: - fInvalid = TRUE; - break; - } - - break; - } - - if (wzPartBegin == wzPartEnd) - { - fInvalid = TRUE; - } - - if (fInvalid) - { - break; - } - - DWORD cchPart = 0; - hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart); - if (FAILED(hr)) - { - fInvalid = TRUE; - break; - } - - // Parse version part. - UINT uPart = 0; - hr = StrStringToUInt32(wzPartBegin, cchPart, &uPart); - if (FAILED(hr)) - { - fInvalid = TRUE; - break; - } - - switch (iPart) - { - case 0: - pVersion->dwMajor = uPart; - break; - case 1: - pVersion->dwMinor = uPart; - break; - case 2: - pVersion->dwPatch = uPart; - break; - case 3: - pVersion->dwRevision = uPart; - break; - } - - if (fTrailingDot) - { - ++wzPartEnd; - } - wzPartBegin = wzPartEnd; - ++iPart; - - if (4 <= iPart || fLastPart) - { - fParsedVersionNumber = TRUE; - break; - } - } - - fInvalid |= !fParsedVersionNumber || fTrailingDot; - - if (!fInvalid && wzPartBegin < wzEnd && *wzPartBegin == L'-') - { - wzPartBegin = wzPartEnd = wzPartBegin + 1; - fExpectedReleaseLabels = TRUE; - fLastPart = FALSE; - } - - while (fExpectedReleaseLabels && wzPartBegin < wzEnd) - { - fTrailingDot = FALSE; - - // Find end of part. - for (;;) - { - if (wzPartEnd >= wzEnd) - { - fLastPart = TRUE; - break; - } - - if (*wzPartEnd >= L'0' && *wzPartEnd <= L'9' || - *wzPartEnd >= L'A' && *wzPartEnd <= L'Z' || - *wzPartEnd >= L'a' && *wzPartEnd <= L'z' || - *wzPartEnd == L'-') - { - ++wzPartEnd; - continue; - } - else if (*wzPartEnd == L'+') - { - fLastPart = TRUE; - } - else if (*wzPartEnd == L'.') - { - fTrailingDot = TRUE; - } - else - { - fInvalid = TRUE; - } - - break; - } - - if (wzPartBegin == wzPartEnd) - { - fInvalid = TRUE; - } - - if (fInvalid) - { - break; - } - - int cchLabel = 0; - hr = ::PtrdiffTToInt32(wzPartEnd - wzPartBegin, &cchLabel); - if (FAILED(hr) || 0 > cchLabel) - { - fInvalid = TRUE; - break; - } - - hr = MemReAllocArray(reinterpret_cast(&pVersion->rgReleaseLabels), pVersion->cReleaseLabels, sizeof(VERUTIL_VERSION_RELEASE_LABEL), GROW_RELEASE_LABELS - (pVersion->cReleaseLabels % GROW_RELEASE_LABELS)); - VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels '%ls'", wzVersion); - - VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel = pVersion->rgReleaseLabels + pVersion->cReleaseLabels; - ++pVersion->cReleaseLabels; - - // Try to parse as number. - UINT uLabel = 0; - hr = StrStringToUInt32(wzPartBegin, cchLabel, &uLabel); - if (SUCCEEDED(hr)) - { - pReleaseLabel->fNumeric = TRUE; - pReleaseLabel->dwValue = uLabel; - } - - pReleaseLabel->cchLabelOffset = wzPartBegin - pVersion->sczVersion; - pReleaseLabel->cchLabel = cchLabel; - - if (fTrailingDot) - { - ++wzPartEnd; - } - wzPartBegin = wzPartEnd; - - if (fLastPart) - { - break; - } - } - - fInvalid |= fExpectedReleaseLabels && (!pVersion->cReleaseLabels || fTrailingDot); - - if (!fInvalid && wzPartBegin < wzEnd) - { - if (*wzPartBegin == L'+') - { - wzPartBegin = wzPartEnd = wzPartBegin + 1; - } - else - { - fInvalid = TRUE; - } - } - - if (fInvalid && fStrict) - { - ExitFunction1(hr = E_INVALIDARG); - } - - pVersion->cchMetadataOffset = min(wzPartBegin, wzEnd) - pVersion->sczVersion; - pVersion->fInvalid = fInvalid; - - *ppVersion = pVersion; - pVersion = NULL; - hr = S_OK; - -LExit: - ReleaseVerutilVersion(pVersion); - - return hr; -} - -DAPI_(HRESULT) VerVersionFromQword( - __in DWORD64 qwVersion, - __out VERUTIL_VERSION** ppVersion - ) -{ - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion = NULL; - - pVersion = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); - VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version from QWORD."); - - pVersion->dwMajor = (WORD)(qwVersion >> 48 & 0xffff); - pVersion->dwMinor = (WORD)(qwVersion >> 32 & 0xffff); - pVersion->dwPatch = (WORD)(qwVersion >> 16 & 0xffff); - pVersion->dwRevision = (WORD)(qwVersion & 0xffff); - - hr = StrAllocFormatted(&pVersion->sczVersion, L"%lu.%lu.%lu.%lu", pVersion->dwMajor, pVersion->dwMinor, pVersion->dwPatch, pVersion->dwRevision); - ExitOnFailure(hr, "Failed to allocate and format the version string."); - - pVersion->cchMetadataOffset = lstrlenW(pVersion->sczVersion); - - *ppVersion = pVersion; - pVersion = NULL; - -LExit: - ReleaseVerutilVersion(pVersion); - - return hr; -} - - -static int CompareDword( - __in const DWORD& dw1, - __in const DWORD& dw2 - ) -{ - int nResult = 0; - - if (dw1 > dw2) - { - nResult = 1; - } - else if (dw1 < dw2) - { - nResult = -1; - } - - return nResult; -} - -static HRESULT CompareReleaseLabel( - __in const VERUTIL_VERSION_RELEASE_LABEL* p1, - __in LPCWSTR wzVersion1, - __in const VERUTIL_VERSION_RELEASE_LABEL* p2, - __in LPCWSTR wzVersion2, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - int nResult = 0; - - if (p1 == p2) - { - ExitFunction(); - } - else if (p1 && !p2) - { - ExitFunction1(nResult = 1); - } - else if (!p1 && p2) - { - ExitFunction1(nResult = -1); - } - - if (p1->fNumeric) - { - if (p2->fNumeric) - { - nResult = CompareDword(p1->dwValue, p2->dwValue); - } - else - { - nResult = -1; - } - } - else - { - if (p2->fNumeric) - { - nResult = 1; - } - else - { - hr = CompareVersionSubstring(wzVersion1 + p1->cchLabelOffset, p1->cchLabel, wzVersion2 + p2->cchLabelOffset, p2->cchLabel, &nResult); - } - } - -LExit: - *pnResult = nResult; - - return hr; -} - -static HRESULT CompareVersionSubstring( - __in LPCWSTR wzString1, - __in int cchCount1, - __in LPCWSTR wzString2, - __in int cchCount2, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - int nResult = 0; - - nResult = ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzString1, cchCount1, wzString2, cchCount2); - if (!nResult) - { - VerExitOnLastError(hr, "Failed to compare version substrings"); - } - -LExit: - *pnResult = nResult - 2; - - return hr; -} diff --git a/src/dutil/wiutil.cpp b/src/dutil/wiutil.cpp deleted file mode 100644 index 7414ac42..00000000 --- a/src/dutil/wiutil.cpp +++ /dev/null @@ -1,1629 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define WiuExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) -#define WiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__) -#define WiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__) -#define WiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__) -#define WiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__) -#define WiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_WIUTIL, e, x, s, __VA_ARGS__) -#define WiuExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_WIUTIL, g, x, s, __VA_ARGS__) - - -// constants - -const DWORD WIU_MSI_PROGRESS_INVALID = 0xFFFFFFFF; -const DWORD WIU_GOOD_ENOUGH_PROPERTY_LENGTH = 64; - - -// structs - - -static PFN_MSIENABLELOGW vpfnMsiEnableLogW = ::MsiEnableLogW; -static PFN_MSIGETPRODUCTINFOW vpfnMsiGetProductInfoW = ::MsiGetProductInfoW; -static PFN_MSIQUERYFEATURESTATEW vpfnMsiQueryFeatureStateW = ::MsiQueryFeatureStateW; -static PFN_MSIGETCOMPONENTPATHW vpfnMsiGetComponentPathW = ::MsiGetComponentPathW; -static PFN_MSILOCATECOMPONENTW vpfnMsiLocateComponentW = ::MsiLocateComponentW; -static PFN_MSIINSTALLPRODUCTW vpfnMsiInstallProductW = ::MsiInstallProductW; -static PFN_MSICONFIGUREPRODUCTEXW vpfnMsiConfigureProductExW = ::MsiConfigureProductExW; -static PFN_MSIREMOVEPATCHESW vpfnMsiRemovePatchesW = ::MsiRemovePatchesW; -static PFN_MSISETINTERNALUI vpfnMsiSetInternalUI = ::MsiSetInternalUI; -static PFN_MSISETEXTERNALUIW vpfnMsiSetExternalUIW = ::MsiSetExternalUIW; -static PFN_MSIENUMPRODUCTSW vpfnMsiEnumProductsW = ::MsiEnumProductsW; -static PFN_MSIENUMRELATEDPRODUCTSW vpfnMsiEnumRelatedProductsW = ::MsiEnumRelatedProductsW; - -// MSI 3.0+ -static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceW = NULL; -static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesW = NULL; -static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExW = NULL; -static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExW = NULL; -static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExW = NULL; -static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecord = NULL; -static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL; - -static HMODULE vhMsiDll = NULL; -static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; -static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; -static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExWFromLibrary = NULL; -static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExWFromLibrary = NULL; -static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExWFromLibrary = NULL; -static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecordFromLibrary = NULL; -static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExWFromLibrary = NULL; - -// MSI Transactions v4.5+ -static PFN_MSIBEGINTRANSACTIONW vpfnMsiBeginTransaction = NULL; -static PFN_MSIENDTRANSACTION vpfnMsiEndTransaction = NULL; - -static BOOL vfWiuInitialized = FALSE; - -// globals -static DWORD vdwMsiDllMajorMinor = 0; -static DWORD vdwMsiDllBuildRevision = 0; - - -// internal function declarations - -static DWORD CheckForRestartErrorCode( - __in DWORD dwErrorCode, - __out WIU_RESTART* pRestart - ); -static INT CALLBACK InstallEngineCallback( - __in LPVOID pvContext, - __in UINT uiMessage, - __in_z_opt LPCWSTR wzMessage - ); -static INT CALLBACK InstallEngineRecordCallback( - __in LPVOID pvContext, - __in UINT uiMessage, - __in_opt MSIHANDLE hRecord - ); -static INT HandleInstallMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in INSTALLMESSAGE mt, - __in UINT uiFlags, - __in_z LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ); -static INT HandleInstallProgress( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in_z_opt LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ); -static INT SendMsiMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in INSTALLMESSAGE mt, - __in UINT uiFlags, - __in_z LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ); -static INT SendErrorMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in UINT uiFlags, - __in_z LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ); -static INT SendFilesInUseMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in_opt MSIHANDLE hRecord, - __in BOOL fRestartManagerRequest - ); -static INT SendProgressUpdate( - __in WIU_MSI_EXECUTE_CONTEXT* pContext - ); -static void ResetProgress( - __in WIU_MSI_EXECUTE_CONTEXT* pContext - ); -static DWORD CalculatePhaseProgress( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in DWORD dwProgressIndex, - __in DWORD dwWeightPercentage - ); -void InitializeMessageData( - __in_opt MSIHANDLE hRecord, - __deref_out_ecount(*pcData) LPWSTR** prgsczData, - __out DWORD* pcData - ); -void UninitializeMessageData( - __in LPWSTR* rgsczData, - __in DWORD cData - ); - - -/******************************************************************** - WiuInitialize - initializes wiutil - -*********************************************************************/ -extern "C" HRESULT DAPI WiuInitialize( - ) -{ - HRESULT hr = S_OK; - LPWSTR sczMsiDllPath = NULL; - - hr = LoadSystemLibraryWithPath(L"Msi.dll", &vhMsiDll, &sczMsiDllPath); - WiuExitOnFailure(hr, "Failed to load Msi.DLL"); - - // Ignore failures - FileVersion(sczMsiDllPath, &vdwMsiDllMajorMinor, &vdwMsiDllBuildRevision); - - vpfnMsiDeterminePatchSequenceWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiDeterminePatchSequenceW")); - if (NULL == vpfnMsiDeterminePatchSequenceW) - { - vpfnMsiDeterminePatchSequenceW = vpfnMsiDeterminePatchSequenceWFromLibrary; - } - - vpfnMsiDetermineApplicablePatchesWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiDetermineApplicablePatchesW")); - if (NULL == vpfnMsiDetermineApplicablePatchesW) - { - vpfnMsiDetermineApplicablePatchesW = vpfnMsiDetermineApplicablePatchesWFromLibrary; - } - - vpfnMsiEnumProductsExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiEnumProductsExW")); - if (NULL == vpfnMsiEnumProductsExW) - { - vpfnMsiEnumProductsExW = vpfnMsiEnumProductsExWFromLibrary; - } - - vpfnMsiGetPatchInfoExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiGetPatchInfoExW")); - if (NULL == vpfnMsiGetPatchInfoExW) - { - vpfnMsiGetPatchInfoExW = vpfnMsiGetPatchInfoExWFromLibrary; - } - - vpfnMsiGetProductInfoExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiGetProductInfoExW")); - if (NULL == vpfnMsiGetProductInfoExW) - { - vpfnMsiGetProductInfoExW = vpfnMsiGetProductInfoExWFromLibrary; - } - - vpfnMsiSetExternalUIRecordFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiSetExternalUIRecord")); - if (NULL == vpfnMsiSetExternalUIRecord) - { - vpfnMsiSetExternalUIRecord = vpfnMsiSetExternalUIRecordFromLibrary; - } - - //static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL; - vpfnMsiSourceListAddSourceExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiSourceListAddSourceExW")); - if (NULL == vpfnMsiSourceListAddSourceExW) - { - vpfnMsiSourceListAddSourceExW = vpfnMsiSourceListAddSourceExWFromLibrary; - } - - // MSI Transaction functions - if (NULL == vpfnMsiBeginTransaction) - { - vpfnMsiBeginTransaction = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiBeginTransactionW")); - } - - if (NULL == vpfnMsiEndTransaction) - { - vpfnMsiEndTransaction = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiEndTransaction")); - } - - vfWiuInitialized = TRUE; - -LExit: - ReleaseStr(sczMsiDllPath); - return hr; -} - - -/******************************************************************** - WiuUninitialize - uninitializes wiutil - -*********************************************************************/ -extern "C" void DAPI WiuUninitialize( - ) -{ - if (vhMsiDll) - { - ::FreeLibrary(vhMsiDll); - vhMsiDll = NULL; - vpfnMsiSetExternalUIRecordFromLibrary = NULL; - vpfnMsiGetProductInfoExWFromLibrary = NULL; - vpfnMsiGetPatchInfoExWFromLibrary = NULL; - vpfnMsiEnumProductsExWFromLibrary = NULL; - vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; - vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; - vpfnMsiSourceListAddSourceExWFromLibrary = NULL; - vpfnMsiBeginTransaction = NULL; - vpfnMsiEndTransaction = NULL; - } - - vfWiuInitialized = FALSE; -} - - -/******************************************************************** - WiuFunctionOverride - overrides the Windows installer functions. Typically used - for unit testing. - -*********************************************************************/ -extern "C" void DAPI WiuFunctionOverride( - __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW, - __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW, - __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW, - __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW, - __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW, - __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW, - __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW, - __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW, - __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI, - __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW, - __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW, - __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord, - __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW - ) -{ - vpfnMsiEnableLogW = pfnMsiEnableLogW ? pfnMsiEnableLogW : ::MsiEnableLogW; - vpfnMsiGetComponentPathW = pfnMsiGetComponentPathW ? pfnMsiGetComponentPathW : ::MsiGetComponentPathW; - vpfnMsiLocateComponentW = pfnMsiLocateComponentW ? pfnMsiLocateComponentW : ::MsiLocateComponentW; - vpfnMsiQueryFeatureStateW = pfnMsiQueryFeatureStateW ? pfnMsiQueryFeatureStateW : ::MsiQueryFeatureStateW; - vpfnMsiGetProductInfoW = pfnMsiGetProductInfoW ? pfnMsiGetProductInfoW : vpfnMsiGetProductInfoW; - vpfnMsiInstallProductW = pfnMsiInstallProductW ? pfnMsiInstallProductW : ::MsiInstallProductW; - vpfnMsiConfigureProductExW = pfnMsiConfigureProductExW ? pfnMsiConfigureProductExW : ::MsiConfigureProductExW; - vpfnMsiSetInternalUI = pfnMsiSetInternalUI ? pfnMsiSetInternalUI : ::MsiSetInternalUI; - vpfnMsiSetExternalUIW = pfnMsiSetExternalUIW ? pfnMsiSetExternalUIW : ::MsiSetExternalUIW; - vpfnMsiEnumRelatedProductsW = pfnMsiEnumRelatedProductsW ? pfnMsiEnumRelatedProductsW : ::MsiEnumRelatedProductsW; - vpfnMsiGetProductInfoExW = pfnMsiGetProductInfoExW ? pfnMsiGetProductInfoExW : vpfnMsiGetProductInfoExWFromLibrary; - vpfnMsiSetExternalUIRecord = pfnMsiSetExternalUIRecord ? pfnMsiSetExternalUIRecord : vpfnMsiSetExternalUIRecordFromLibrary; - vpfnMsiSourceListAddSourceExW = pfnMsiSourceListAddSourceExW ? pfnMsiSourceListAddSourceExW : vpfnMsiSourceListAddSourceExWFromLibrary; -} - - -extern "C" HRESULT DAPI WiuGetComponentPath( - __in_z LPCWSTR wzProductCode, - __in_z LPCWSTR wzComponentId, - __out INSTALLSTATE* pInstallState, - __out_z LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; - DWORD cchCompare; - - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to allocate string for component path."); - - cchCompare = cch; - *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); - if (INSTALLSTATE_MOREDATA == *pInstallState) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for component path."); - - cchCompare = cch; - *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); - } - - if (INSTALLSTATE_INVALIDARG == *pInstallState) - { - hr = E_INVALIDARG; - WiuExitOnRootFailure(hr, "Invalid argument when getting component path."); - } - else if (INSTALLSTATE_UNKNOWN == *pInstallState) - { - ExitFunction(); - } - - // If the actual path length is greater than or equal to the original buffer - // allocate a larger buffer and get the path again, just in case we are - // missing any part of the path. - if (cchCompare <= cch) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for component path."); - - *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuLocateComponent( - __in_z LPCWSTR wzComponentId, - __out INSTALLSTATE* pInstallState, - __out_z LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; - DWORD cchCompare; - - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to allocate string for component path."); - - cchCompare = cch; - *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); - if (INSTALLSTATE_MOREDATA == *pInstallState) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for component path."); - - cchCompare = cch; - *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); - } - - if (INSTALLSTATE_INVALIDARG == *pInstallState) - { - hr = E_INVALIDARG; - WiuExitOnRootFailure(hr, "Invalid argument when locating component."); - } - else if (INSTALLSTATE_UNKNOWN == *pInstallState) - { - ExitFunction(); - } - - // If the actual path length is greater than or equal to the original buffer - // allocate a larger buffer and get the path again, just in case we are - // missing any part of the path. - if (cchCompare <= cch) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for component path."); - - *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuQueryFeatureState( - __in_z LPCWSTR wzProduct, - __in_z LPCWSTR wzFeature, - __out INSTALLSTATE* pInstallState - ) -{ - HRESULT hr = S_OK; - - *pInstallState = vpfnMsiQueryFeatureStateW(wzProduct, wzFeature); - if (INSTALLSTATE_INVALIDARG == *pInstallState) - { - hr = E_INVALIDARG; - WiuExitOnRootFailure(hr, "Failed to query state of feature: %ls in product: %ls", wzFeature, wzProduct); - } - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuGetProductInfo( - __in_z LPCWSTR wzProductCode, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; - - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to allocate string for product info."); - - er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); - if (ERROR_MORE_DATA == er) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for product info."); - - er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); - } - WiuExitOnWin32Error(er, hr, "Failed to get product info."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuGetProductInfoEx( - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; - - if (!vpfnMsiGetProductInfoExW) - { - hr = WiuGetProductInfo(wzProductCode, wzProperty, psczValue); - WiuExitOnFailure(hr, "Failed to get product info when extended info was not available."); - - ExitFunction(); - } - - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to allocate string for extended product info."); - - er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); - if (ERROR_MORE_DATA == er) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for extended product info."); - - er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); - } - WiuExitOnWin32Error(er, hr, "Failed to get extended product info."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuGetProductProperty( - __in MSIHANDLE hProduct, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; - - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to allocate string for product property."); - - er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); - if (ERROR_MORE_DATA == er) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for product property."); - - er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); - } - WiuExitOnWin32Error(er, hr, "Failed to get product property."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuGetPatchInfoEx( - __in_z LPCWSTR wzPatchCode, - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in_z LPCWSTR wzProperty, - __out LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; - - if (!vpfnMsiGetPatchInfoExW) - { - ExitFunction1(hr = E_NOTIMPL); - } - - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to allocate string for extended patch info."); - - er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); - if (ERROR_MORE_DATA == er) - { - ++cch; - hr = StrAlloc(psczValue, cch); - WiuExitOnFailure(hr, "Failed to reallocate string for extended patch info."); - - er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); - } - WiuExitOnWin32Error(er, hr, "Failed to get extended patch info."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuDeterminePatchSequence( - __in_z LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT context, - __in PMSIPATCHSEQUENCEINFOW pPatchInfo, - __in DWORD cPatchInfo - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - if (!vpfnMsiDeterminePatchSequenceW) - { - ExitFunction1(hr = E_NOTIMPL); - } - - er = vpfnMsiDeterminePatchSequenceW(wzProductCode, wzUserSid, context, cPatchInfo, pPatchInfo); - WiuExitOnWin32Error(er, hr, "Failed to determine patch sequence for product code."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuDetermineApplicablePatches( - __in_z LPCWSTR wzProductPackagePath, - __in PMSIPATCHSEQUENCEINFOW pPatchInfo, - __in DWORD cPatchInfo - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - if (!vpfnMsiDetermineApplicablePatchesW) - { - ExitFunction1(hr = E_NOTIMPL); - } - - er = vpfnMsiDetermineApplicablePatchesW(wzProductPackagePath, cPatchInfo, pPatchInfo); - WiuExitOnWin32Error(er, hr, "Failed to determine applicable patches for product package."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuEnumProducts( - __in DWORD iProductIndex, - __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiEnumProductsW(iProductIndex, wzProductCode); - if (ERROR_NO_MORE_ITEMS == er) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(er)); - } - WiuExitOnWin32Error(er, hr, "Failed to enumerate products."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuEnumProductsEx( - __in_z_opt LPCWSTR wzProductCode, - __in_z_opt LPCWSTR wzUserSid, - __in DWORD dwContext, - __in DWORD dwIndex, - __out_opt WCHAR wzInstalledProductCode[39], - __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, - __out_opt LPWSTR wzSid, - __inout_opt LPDWORD pcchSid - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - if (!vpfnMsiEnumProductsExW) - { - ExitFunction1(hr = E_NOTIMPL); - } - - er = vpfnMsiEnumProductsExW(wzProductCode, wzUserSid, dwContext, dwIndex, wzInstalledProductCode, pdwInstalledContext, wzSid, pcchSid); - if (ERROR_NO_MORE_ITEMS == er) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(er)); - } - WiuExitOnWin32Error(er, hr, "Failed to enumerate products."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuEnumRelatedProducts( - __in_z LPCWSTR wzUpgradeCode, - __in DWORD iProductIndex, - __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiEnumRelatedProductsW(wzUpgradeCode, 0, iProductIndex, wzProductCode); - if (ERROR_NO_MORE_ITEMS == er) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(er)); - } - WiuExitOnWin32Error(er, hr, "Failed to enumerate related products for updgrade code: %ls", wzUpgradeCode); - -LExit: - return hr; -} - -/******************************************************************** - WiuEnumRelatedProductCodes - Returns an array of related products for a given upgrade code. - - Parameters: - wzUpgradeCode - The upgrade code that will be used to find the related products. - prgsczProductCodes - Pointer to the array that will contain the product codes. - pcRelatedProducts - Returns the count of the number of related products found. - fReturnHighestVersionOnly - When set to "TRUE", will only return the product code of the highest version found. -********************************************************************/ -extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( - __in_z LPCWSTR wzUpgradeCode, - __deref_out_ecount_opt(*pcRelatedProducts) LPWSTR** prgsczProductCodes, - __out DWORD* pcRelatedProducts, - __in BOOL fReturnHighestVersionOnly - ) -{ - HRESULT hr = S_OK; - WCHAR wzCurrentProductCode[MAX_GUID_CHARS + 1] = { }; - LPWSTR sczInstalledVersion = NULL; - VERUTIL_VERSION* pCurrentVersion = NULL; - VERUTIL_VERSION* pHighestVersion = NULL; - int nCompare = 0; - - // make sure we start at zero - *pcRelatedProducts = 0; - - for (DWORD i = 0; ; ++i) - { - hr = WiuEnumRelatedProducts(wzUpgradeCode, i, wzCurrentProductCode); - - if (E_NOMOREITEMS == hr) - { - hr = S_OK; - break; - } - WiuExitOnFailure(hr, "Failed to enumerate related products for upgrade code: %ls", wzUpgradeCode); - - if (fReturnHighestVersionOnly) - { - // try to get the version but if the product registration is broken - // (for whatever reason), skip this product - hr = WiuGetProductInfo(wzCurrentProductCode, L"VersionString", &sczInstalledVersion); - if (FAILED(hr)) - { - WiuExitTrace(hr, "Could not get product version for product code: %ls, skipping...", wzCurrentProductCode); - continue; - } - - hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pCurrentVersion); - WiuExitOnFailure(hr, "Failed to parse version: %ls for product code: %ls", sczInstalledVersion, wzCurrentProductCode); - - if (pCurrentVersion->fInvalid) - { - WiuExitTrace(E_INVALIDDATA, "Enumerated msi package with invalid version, product code: '%1!ls!', version: '%2!ls!'"); - } - - // if this is the first product found then it is the highest version (for now) - if (!pHighestVersion) - { - pHighestVersion = pCurrentVersion; - pCurrentVersion = NULL; - } - else - { - hr = VerCompareParsedVersions(pCurrentVersion, pHighestVersion, &nCompare); - WiuExitOnFailure(hr, "Failed to compare version '%ls' to highest version: '%ls'", pCurrentVersion->sczVersion, pHighestVersion->sczVersion); - - // if this is the highest version encountered so far then overwrite - // the first item in the array (there will never be more than one item) - if (nCompare > 0) - { - ReleaseVerutilVersion(pHighestVersion); - pHighestVersion = pCurrentVersion; - pCurrentVersion = NULL; - - hr = StrAllocString(prgsczProductCodes[0], wzCurrentProductCode, 0); - WiuExitOnFailure(hr, "Failed to update array with higher versioned product code."); - } - else - { - ReleaseVerutilVersion(pCurrentVersion); - } - - // continue here as we don't want anything else added to the list - continue; - } - } - - hr = StrArrayAllocString(prgsczProductCodes, (LPUINT)(pcRelatedProducts), wzCurrentProductCode, 0); - WiuExitOnFailure(hr, "Failed to add product code to array."); - } - -LExit: - ReleaseVerutilVersion(pCurrentVersion); - ReleaseVerutilVersion(pHighestVersion); - ReleaseStr(sczInstalledVersion); - return hr; -} - - -extern "C" HRESULT DAPI WiuEnableLog( - __in DWORD dwLogMode, - __in_z LPCWSTR wzLogFile, - __in DWORD dwLogAttributes - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiEnableLogW(dwLogMode, wzLogFile, dwLogAttributes); - WiuExitOnWin32Error(er, hr, "Failed to enable MSI internal logging."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuInitializeInternalUI( - __in INSTALLUILEVEL internalUILevel, - __in_opt HWND hwndParent, - __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext - ) -{ - HRESULT hr = S_OK; - - memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); - - pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(internalUILevel, &hwndParent); - pExecuteContext->hwndPreviousParentWindow = hwndParent; - - if (INSTALLUILEVEL_NOCHANGE == pExecuteContext->previousInstallUILevel) - { - hr = E_INVALIDARG; - } - - return hr; -} - - -extern "C" HRESULT DAPI WiuInitializeExternalUI( - __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, - __in INSTALLUILEVEL internalUILevel, - __in_opt HWND hwndParent, - __in LPVOID pvContext, - __in BOOL fRollback, - __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - DWORD dwMessageFilter = INSTALLLOGMODE_INITIALIZE | INSTALLLOGMODE_TERMINATE | - INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | - INSTALLLOGMODE_RESOLVESOURCE | INSTALLLOGMODE_OUTOFDISKSPACE | - INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA | - INSTALLLOGMODE_PROGRESS | INSTALLLOGMODE_FILESINUSE; - - if (MAKEDWORD(0, 4) <= vdwMsiDllMajorMinor) - { - dwMessageFilter |= INSTALLLOGMODE_RMFILESINUSE; - } - - // Wire the internal and external UI handler. - hr = WiuInitializeInternalUI(internalUILevel, hwndParent, pExecuteContext); - WiuExitOnFailure(hr, "Failed to set internal UI level and window."); - - pExecuteContext->fRollback = fRollback; - pExecuteContext->pfnMessageHandler = pfnMessageHandler; - pExecuteContext->pvContext = pvContext; - - // If the external UI record is available (MSI version >= 3.1) use it but fall back to the standard external - // UI handler if necesary. - if (vpfnMsiSetExternalUIRecord) - { - er = vpfnMsiSetExternalUIRecord(InstallEngineRecordCallback, dwMessageFilter, pExecuteContext, &pExecuteContext->pfnPreviousExternalUIRecord); - WiuExitOnWin32Error(er, hr, "Failed to wire up external UI record handler."); - pExecuteContext->fSetPreviousExternalUIRecord = TRUE; - } - else - { - pExecuteContext->pfnPreviousExternalUI = vpfnMsiSetExternalUIW(InstallEngineCallback, dwMessageFilter, pExecuteContext); - pExecuteContext->fSetPreviousExternalUI = TRUE; - } - -LExit: - return hr; -} - - -extern "C" void DAPI WiuUninitializeExternalUI( - __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext - ) -{ - if (INSTALLUILEVEL_NOCHANGE != pExecuteContext->previousInstallUILevel) - { - pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(pExecuteContext->previousInstallUILevel, &pExecuteContext->hwndPreviousParentWindow); - } - - if (pExecuteContext->fSetPreviousExternalUI) // unset the UI handler - { - vpfnMsiSetExternalUIW(pExecuteContext->pfnPreviousExternalUI, 0, NULL); - } - - if (pExecuteContext->fSetPreviousExternalUIRecord) // unset the UI record handler - { - vpfnMsiSetExternalUIRecord(pExecuteContext->pfnPreviousExternalUIRecord, 0, NULL, NULL); - } - - memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); -} - - -extern "C" HRESULT DAPI WiuConfigureProductEx( - __in_z LPCWSTR wzProduct, - __in int iInstallLevel, - __in INSTALLSTATE eInstallState, - __in_z LPCWSTR wzCommandLine, - __out WIU_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiConfigureProductExW(wzProduct, iInstallLevel, eInstallState, wzCommandLine); - er = CheckForRestartErrorCode(er, pRestart); - WiuExitOnWin32Error(er, hr, "Failed to configure product: %ls", wzProduct); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuInstallProduct( - __in_z LPCWSTR wzPackagePath, - __in_z LPCWSTR wzCommandLine, - __out WIU_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiInstallProductW(wzPackagePath, wzCommandLine); - er = CheckForRestartErrorCode(er, pRestart); - WiuExitOnWin32Error(er, hr, "Failed to install product: %ls", wzPackagePath); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuRemovePatches( - __in_z LPCWSTR wzPatchList, - __in_z LPCWSTR wzProductCode, - __in_z LPCWSTR wzPropertyList, - __out WIU_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiRemovePatchesW(wzPatchList, wzProductCode, INSTALLTYPE_SINGLE_INSTANCE, wzPropertyList); - er = CheckForRestartErrorCode(er, pRestart); - WiuExitOnWin32Error(er, hr, "Failed to remove patches."); - -LExit: - return hr; -} - - -extern "C" HRESULT DAPI WiuSourceListAddSourceEx( - __in_z LPCWSTR wzProductCodeOrPatchCode, - __in_z_opt LPCWSTR wzUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in DWORD dwCode, - __in_z LPCWSTR wzSource, - __in_opt DWORD dwIndex - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - er = vpfnMsiSourceListAddSourceExW(wzProductCodeOrPatchCode, wzUserSid, dwContext, MSISOURCETYPE_NETWORK | dwCode, wzSource, dwIndex); - WiuExitOnWin32Error(er, hr, "Failed to add source."); - -LExit: - return hr; -} - -extern "C" BOOL DAPI WiuIsMsiTransactionSupported( - ) -{ - return vpfnMsiBeginTransaction && vpfnMsiEndTransaction; -} - -extern "C" HRESULT DAPI WiuBeginTransaction( - __in_z LPCWSTR szName, - __in DWORD dwTransactionAttributes, - __out MSIHANDLE * phTransactionHandle, - __out HANDLE * phChangeOfOwnerEvent, - __in DWORD dwLogMode, - __in_z LPCWSTR szLogPath - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - if (!WiuIsMsiTransactionSupported()) - { - WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); - } - - hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); - WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction"); - - er = vpfnMsiBeginTransaction(szName, dwTransactionAttributes, phTransactionHandle, phChangeOfOwnerEvent); - WiuExitOnWin32Error(er, hr, "Failed to begin transaction."); - -LExit: - return hr; -} - -extern "C" HRESULT DAPI WiuEndTransaction( - __in DWORD dwTransactionState, - __in DWORD dwLogMode, - __in_z LPCWSTR szLogPath - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - if (!WiuIsMsiTransactionSupported()) - { - WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); - } - - hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); - WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction"); - - er = vpfnMsiEndTransaction(dwTransactionState); - WiuExitOnWin32Error(er, hr, "Failed to end transaction."); - -LExit: - return hr; -} - - - -static DWORD CheckForRestartErrorCode( - __in DWORD dwErrorCode, - __out WIU_RESTART* pRestart - ) -{ - switch (dwErrorCode) - { - case ERROR_SUCCESS_REBOOT_REQUIRED: - case ERROR_SUCCESS_RESTART_REQUIRED: - *pRestart = WIU_RESTART_REQUIRED; - dwErrorCode = ERROR_SUCCESS; - break; - - case ERROR_SUCCESS_REBOOT_INITIATED: - case ERROR_INSTALL_SUSPEND: - *pRestart = WIU_RESTART_INITIATED; - dwErrorCode = ERROR_SUCCESS; - break; - } - - return dwErrorCode; -} - -static INT CALLBACK InstallEngineCallback( - __in LPVOID pvContext, - __in UINT uiMessage, - __in_z_opt LPCWSTR wzMessage - ) -{ - INT nResult = IDNOACTION; - WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext; - INSTALLMESSAGE mt = static_cast(0xFF000000 & uiMessage); - UINT uiFlags = 0x00FFFFFF & uiMessage; - - if (wzMessage) - { - if (INSTALLMESSAGE_PROGRESS == mt) - { - nResult = HandleInstallProgress(pContext, wzMessage, NULL); - } - else - { - nResult = HandleInstallMessage(pContext, mt, uiFlags, wzMessage, NULL); - } - } - - return nResult; -} - -static INT CALLBACK InstallEngineRecordCallback( - __in LPVOID pvContext, - __in UINT uiMessage, - __in_opt MSIHANDLE hRecord - ) -{ - INT nResult = IDNOACTION; - HRESULT hr = S_OK; - WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext; - - INSTALLMESSAGE mt = static_cast(0xFF000000 & uiMessage); - UINT uiFlags = 0x00FFFFFF & uiMessage; - LPWSTR sczMessage = NULL; - DWORD cchMessage = 0; - - if (hRecord) - { - if (INSTALLMESSAGE_PROGRESS == mt) - { - nResult = HandleInstallProgress(pContext, NULL, hRecord); - } - else - { - // create formated message string -#pragma prefast(push) -#pragma prefast(disable:6298) // docs explicitly say this is a valid option for getting the buffer size - DWORD er = ::MsiFormatRecordW(NULL, hRecord, L"", &cchMessage); -#pragma prefast(pop) - if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) - { - hr = StrAlloc(&sczMessage, ++cchMessage); - } - else - { - hr = HRESULT_FROM_WIN32(er); - } - WiuExitOnFailure(hr, "Failed to allocate string for formated message."); - - er = ::MsiFormatRecordW(NULL, hRecord, sczMessage, &cchMessage); - WiuExitOnWin32Error(er, hr, "Failed to format message record."); - - // Pass to handler including both the formated message and the original record. - nResult = HandleInstallMessage(pContext, mt, uiFlags, sczMessage, hRecord); - } - } - -LExit: - ReleaseStr(sczMessage); - return nResult; -} - -static INT HandleInstallMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in INSTALLMESSAGE mt, - __in UINT uiFlags, - __in_z LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ) -{ - INT nResult = IDNOACTION; - -Trace(REPORT_STANDARD, "MSI install[%x]: %ls", pContext->dwCurrentProgressIndex, wzMessage); - - // Handle the message. - switch (mt) - { - case INSTALLMESSAGE_INITIALIZE: // this message is received prior to internal UI initialization, no string data - ResetProgress(pContext); - break; - - case INSTALLMESSAGE_TERMINATE: // sent after UI termination, no string data - break; - - case INSTALLMESSAGE_ACTIONSTART: - if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData) - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; - } - - nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); - break; - - case INSTALLMESSAGE_ACTIONDATA: - if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData) - { - if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward) - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep; - } - else // rollback. - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep; - } - - nResult = SendProgressUpdate(pContext); - } - else - { - nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); - } - break; - - case INSTALLMESSAGE_OUTOFDISKSPACE: __fallthrough; - case INSTALLMESSAGE_FATALEXIT: __fallthrough; - case INSTALLMESSAGE_ERROR: - nResult = SendErrorMessage(pContext, uiFlags, wzMessage, hRecord); - break; - - case INSTALLMESSAGE_FILESINUSE: - case INSTALLMESSAGE_RMFILESINUSE: - nResult = SendFilesInUseMessage(pContext, hRecord, INSTALLMESSAGE_RMFILESINUSE == mt); - break; - -/* -#if 0 - case INSTALLMESSAGE_COMMONDATA: - if (L'1' == wzMessage[0] && L':' == wzMessage[1] && L' ' == wzMessage[2]) - { - if (L'0' == wzMessage[3]) - { - // TODO: handle the language common data message. - lres = IDOK; - return lres; - } - else if (L'1' == wzMessage[3]) - { - // TODO: really handle sending the caption. - lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CAPTION, uiFlags, reinterpret_cast(wzMessage + 3)); - return lres; - } - else if (L'2' == wzMessage[3]) - { - // TODO: really handle sending the cancel button status. - lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CANCEL, uiFlags, reinterpret_cast(wzMessage + 3)); - return lres; - } - } - break; -#endif -*/ - - //case INSTALLMESSAGE_WARNING: - //case INSTALLMESSAGE_USER: - //case INSTALLMESSAGE_INFO: - //case INSTALLMESSAGE_SHOWDIALOG: // sent prior to display of authored dialog or wizard - default: - nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); - break; - } - - // Always return "no action" (0) for resolve source messages. - return (INSTALLMESSAGE_RESOLVESOURCE == mt) ? IDNOACTION : nResult; -} - -static INT HandleInstallProgress( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in_z_opt LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ) -{ - HRESULT hr = S_OK; - INT nResult = IDNOACTION; - INT iFields[4] = { }; - INT cFields = 0; - LPCWSTR pwz = NULL; - DWORD cch = 0; - - // get field values - if (hRecord) - { - cFields = ::MsiRecordGetFieldCount(hRecord); - cFields = min(cFields, countof(iFields)); // avoid buffer overrun if there are more fields than our buffer can hold - for (INT i = 0; i < cFields; ++i) - { - iFields[i] = ::MsiRecordGetInteger(hRecord, i + 1); - } - } - else - { - Assert(wzMessage); - - // parse message string - pwz = wzMessage; - while (cFields < 4) - { - // check if we have the start of a valid part - if ((L'1' + cFields) != pwz[0] || L':' != pwz[1] || L' ' != pwz[2]) - { - break; - } - pwz += 3; - - // find character count of number - cch = 0; - while (pwz[cch] && L' ' != pwz[cch]) - { - ++cch; - } - - // parse number - hr = StrStringToInt32(pwz, cch, &iFields[cFields]); - WiuExitOnFailure(hr, "Failed to parse MSI message part."); - - // increment field count - ++cFields; - } - } - -#ifdef _DEBUG - WCHAR wz[256]; - ::StringCchPrintfW(wz, countof(wz), L"1: %d 2: %d 3: %d 4: %d", iFields[0], iFields[1], iFields[2], iFields[3]); - Trace(REPORT_STANDARD, "MSI progress[%x]: %ls", pContext->dwCurrentProgressIndex, wz); -#endif - - // Verify that we have the enough field values. - if (1 > cFields) - { - ExitFunction(); // unknown message, bail - } - - // Handle based on message type. - switch (iFields[0]) - { - case 0: // master progress reset - if (4 > cFields) - { - Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); - ExitFunction(); - } - //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - MASTER RESET - %d, %d, %d", iFields[1], iFields[2], iFields[3]); - - // Update the index into progress array. - if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex) - { - pContext->dwCurrentProgressIndex = 0; - } - else if (pContext->dwCurrentProgressIndex + 1 < countof(pContext->rgMsiProgress)) - { - ++pContext->dwCurrentProgressIndex; - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - WiuExitOnRootFailure(hr, "Insufficient space to hold progress information."); - } - - // we only care about the first stage after script execution has started - //if (!pEngineInfo->fMsiProgressScriptInProgress && 1 != iFields[3]) - //{ - // pEngineInfo->fMsiProgressFinished = TRUE; - //} - - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal = iFields[1]; - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted = 0 == iFields[2] ? 0 : iFields[1]; // if forward start at 0, if backwards start at max - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward = (0 == iFields[2]); - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fScriptInProgress = (1 == iFields[3]); - - if (0 == pContext->dwCurrentProgressIndex) - { - // HACK!!! this is a hack courtesy of the Windows Installer team. It seems the script planning phase - // is always off by "about 50". So we'll toss an extra 50 ticks on so that the standard progress - // doesn't go over 100%. If there are any custom actions, they may blow the total so we'll call this - // "close" and deal with the rest. - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += 50; - } - break; - - case 1: // action info. - if (3 > cFields) - { - Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); - ExitFunction(); - } - //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - ACTION INFO - %d, %d, %d", iFields[1], iFields[2], iFields[3]); - - if (0 == iFields[2]) - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; - } - else - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = TRUE; - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep = iFields[1]; - } - break; - - case 2: // progress report. - if (2 > cFields) - { - Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); - break; - } - - //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - PROGRESS REPORT - %d, %d, %d", iFields[1], iFields[2], iFields[3]); - - if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex) - { - break; - } - else if (0 == pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal) - { - break; - } - - // Update progress. - if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward) - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += iFields[1]; - } - else // rollback. - { - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= iFields[1]; - } - break; - - case 3: // extend the progress bar. - pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += iFields[1]; - break; - - default: - ExitFunction(); // unknown message, bail - } - - // If we have a valid progress index, send an update. - if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex) - { - nResult = SendProgressUpdate(pContext); - } - -LExit: - return nResult; -} - -static INT SendMsiMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in INSTALLMESSAGE mt, - __in UINT uiFlags, - __in_z LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ) -{ - INT nResult = IDNOACTION; - WIU_MSI_EXECUTE_MESSAGE message = { }; - LPWSTR* rgsczData = NULL; - DWORD cData = 0; - - InitializeMessageData(hRecord, &rgsczData, &cData); - - message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE; - message.dwAllowedResults = uiFlags; - message.cData = cData; - message.rgwzData = (LPCWSTR*)rgsczData; - message.msiMessage.mt = mt; - message.msiMessage.wzMessage = wzMessage; - nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); - - UninitializeMessageData(rgsczData, cData); - return nResult; -} - -static INT SendErrorMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in UINT uiFlags, - __in_z LPCWSTR wzMessage, - __in_opt MSIHANDLE hRecord - ) -{ - INT nResult = IDNOACTION; - WIU_MSI_EXECUTE_MESSAGE message = { }; - DWORD dwErrorCode = 0; - LPWSTR* rgsczData = NULL; - DWORD cData = 0; - - if (hRecord) - { - dwErrorCode = ::MsiRecordGetInteger(hRecord, 1); - - // Set the recommendation if it's a known error code. - switch (dwErrorCode) - { - case 1605: // continue with install even if there isn't enough room for rollback. - nResult = IDIGNORE; - break; - - case 1704: // rollback suspended installs so our install can continue. - nResult = IDOK; - break; - } - } - - InitializeMessageData(hRecord, &rgsczData, &cData); - - message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR; - message.dwAllowedResults = uiFlags; - message.nResultRecommendation = nResult; - message.cData = cData; - message.rgwzData = (LPCWSTR*)rgsczData; - message.error.dwErrorCode = dwErrorCode; - message.error.wzMessage = wzMessage; - nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); - - UninitializeMessageData(rgsczData, cData); - return nResult; -} - -static INT SendFilesInUseMessage( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in_opt MSIHANDLE hRecord, - __in BOOL /*fRestartManagerRequest*/ - ) -{ - INT nResult = IDNOACTION; - WIU_MSI_EXECUTE_MESSAGE message = { }; - LPWSTR* rgsczData = NULL; - DWORD cData = 0; - - InitializeMessageData(hRecord, &rgsczData, &cData); - - message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE; - message.dwAllowedResults = WIU_MB_OKIGNORECANCELRETRY; - message.cData = cData; - message.rgwzData = (LPCWSTR*)rgsczData; - message.msiFilesInUse.cFiles = message.cData; // point the files in use information to the message record information. - message.msiFilesInUse.rgwzFiles = message.rgwzData; - nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); - - UninitializeMessageData(rgsczData, cData); - return nResult; -} - -static INT SendProgressUpdate( - __in WIU_MSI_EXECUTE_CONTEXT* pContext - ) -{ - int nResult = IDNOACTION; - DWORD dwPercentage = 0; // number representing 0 - 100% - WIU_MSI_EXECUTE_MESSAGE message = { }; - - //DWORD dwMsiProgressTotal = pEngineInfo->dwMsiProgressTotal; - //DWORD dwMsiProgressComplete = pEngineInfo->dwMsiProgressComplete; //min(dwMsiProgressTotal, pEngineInfo->dwMsiProgressComplete); - //double dProgressGauge = 0; - //double dProgressStageTotal = (double)pEngineInfo->qwProgressStageTotal; - - // Calculate progress for the phases of Windows Installer. - // TODO: handle upgrade progress which would add another phase. - dwPercentage += CalculatePhaseProgress(pContext, 0, 15); - dwPercentage += CalculatePhaseProgress(pContext, 1, 80); - dwPercentage += CalculatePhaseProgress(pContext, 2, 5); - dwPercentage = min(dwPercentage, 100); // ensure the percentage never goes over 100%. - - if (pContext->fRollback) - { - dwPercentage = 100 - dwPercentage; - } - - //if (qwTotal) // avoid "divide by zero" if the MSI range is blank. - //{ - // // calculate gauge. - // double dProgressGauge = static_cast(qwCompleted) / static_cast(qwTotal); - // dProgressGauge = (1.0 / (1.0 + exp(3.7 - dProgressGauge * 7.5)) - 0.024127021417669196) / 0.975872978582330804; - // qwCompleted = (DWORD)(dProgressGauge * qwTotal); - - // // calculate progress within range - // //qwProgressComplete = (DWORD64)(dwMsiProgressComplete * (dProgressStageTotal / dwMsiProgressTotal)); - // //qwProgressComplete = min(qwProgressComplete, pEngineInfo->qwProgressStageTotal); - //} - -#ifdef _DEBUG - DWORD64 qwCompleted = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted; - DWORD64 qwTotal = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal; - Trace(REPORT_STANDARD, "MSI progress: %I64u/%I64u (%u%%)", qwCompleted, qwTotal, dwPercentage); - //AssertSz(qwCompleted <= qwTotal, "Completed progress is larger than total progress."); -#endif - - message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS; - message.dwAllowedResults = MB_OKCANCEL; - message.progress.dwPercentage = dwPercentage; - nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); - - return nResult; -} - -static void ResetProgress( - __in WIU_MSI_EXECUTE_CONTEXT* pContext - ) -{ - memset(pContext->rgMsiProgress, 0, sizeof(pContext->rgMsiProgress)); - pContext->dwCurrentProgressIndex = WIU_MSI_PROGRESS_INVALID; -} - -static DWORD CalculatePhaseProgress( - __in WIU_MSI_EXECUTE_CONTEXT* pContext, - __in DWORD dwProgressIndex, - __in DWORD dwWeightPercentage - ) -{ - DWORD dwPhasePercentage = 0; - - // If we've already passed this progress index, return the maximum percentage possible (the weight) - if (dwProgressIndex < pContext->dwCurrentProgressIndex) - { - dwPhasePercentage = dwWeightPercentage; - } - else if (dwProgressIndex == pContext->dwCurrentProgressIndex) // have to do the math for the current progress. - { - WIU_MSI_PROGRESS* pProgress = pContext->rgMsiProgress + dwProgressIndex; - if (pProgress->dwTotal) - { - DWORD64 dw64Completed = pProgress->dwCompleted; - dwPhasePercentage = static_cast(dw64Completed * dwWeightPercentage / pProgress->dwTotal); - } - } - // else we're not there yet so it has to be zero. - - return dwPhasePercentage; -} - -void InitializeMessageData( - __in_opt MSIHANDLE hRecord, - __deref_out_ecount(*pcData) LPWSTR** prgsczData, - __out DWORD* pcData - ) -{ - DWORD cData = 0; - LPWSTR* rgsczData = NULL; - - // If we have a record based message, try to get the extra data. - if (hRecord) - { - cData = ::MsiRecordGetFieldCount(hRecord); - if (cData) - { - rgsczData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cData, TRUE); - } - - for (DWORD i = 0; rgsczData && i < cData; ++i) - { - DWORD cch = 0; - - // get string from record -#pragma prefast(push) -#pragma prefast(disable:6298) - DWORD er = ::MsiRecordGetStringW(hRecord, i + 1, L"", &cch); -#pragma prefast(pop) - if (ERROR_MORE_DATA == er) - { - HRESULT hr = StrAlloc(&rgsczData[i], ++cch); - if (SUCCEEDED(hr)) - { - er = ::MsiRecordGetStringW(hRecord, i + 1, rgsczData[i], &cch); - } - } - } - } - - *prgsczData = rgsczData; - *pcData = cData; -} - -void UninitializeMessageData( - __in LPWSTR* rgsczData, - __in DWORD cData - ) -{ - // Clean up if there was any data allocated. - if (rgsczData) - { - for (DWORD i = 0; i < cData; ++i) - { - ReleaseStr(rgsczData[i]); - } - - MemFree(rgsczData); - } -} diff --git a/src/dutil/wuautil.cpp b/src/dutil/wuautil.cpp deleted file mode 100644 index dfb28818..00000000 --- a/src/dutil/wuautil.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define WuaExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) -#define WuaExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) -#define WuaExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) -#define WuaExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) -#define WuaExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) -#define WuaExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) -#define WuaExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_WUAUTIL, p, x, e, s, __VA_ARGS__) -#define WuaExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, p, x, s, __VA_ARGS__) -#define WuaExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_WUAUTIL, p, x, e, s, __VA_ARGS__) -#define WuaExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, p, x, s, __VA_ARGS__) -#define WuaExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_WUAUTIL, e, x, s, __VA_ARGS__) -#define WuaExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_WUAUTIL, g, x, s, __VA_ARGS__) - - -// internal function declarations - -static HRESULT GetAutomaticUpdatesService( - __out IAutomaticUpdates **ppAutomaticUpdates - ); - - -// function definitions - -extern "C" HRESULT DAPI WuaPauseAutomaticUpdates() -{ - HRESULT hr = S_OK; - IAutomaticUpdates *pAutomaticUpdates = NULL; - - hr = GetAutomaticUpdatesService(&pAutomaticUpdates); - WuaExitOnFailure(hr, "Failed to get the Automatic Updates service."); - - hr = pAutomaticUpdates->Pause(); - WuaExitOnFailure(hr, "Failed to pause the Automatic Updates service."); - -LExit: - ReleaseObject(pAutomaticUpdates); - - return hr; -} - -extern "C" HRESULT DAPI WuaResumeAutomaticUpdates() -{ - HRESULT hr = S_OK; - IAutomaticUpdates *pAutomaticUpdates = NULL; - - hr = GetAutomaticUpdatesService(&pAutomaticUpdates); - WuaExitOnFailure(hr, "Failed to get the Automatic Updates service."); - - hr = pAutomaticUpdates->Resume(); - WuaExitOnFailure(hr, "Failed to resume the Automatic Updates service."); - -LExit: - ReleaseObject(pAutomaticUpdates); - - return hr; -} - -extern "C" HRESULT DAPI WuaRestartRequired( - __out BOOL* pfRestartRequired - ) -{ - HRESULT hr = S_OK; - ISystemInformation* pSystemInformation = NULL; - VARIANT_BOOL bRestartRequired; - - hr = ::CoCreateInstance(__uuidof(SystemInformation), NULL, CLSCTX_INPROC_SERVER, __uuidof(ISystemInformation), reinterpret_cast(&pSystemInformation)); - WuaExitOnRootFailure(hr, "Failed to get WUA system information interface."); - - hr = pSystemInformation->get_RebootRequired(&bRestartRequired); - WuaExitOnRootFailure(hr, "Failed to determine if restart is required from WUA."); - - *pfRestartRequired = (VARIANT_FALSE != bRestartRequired); - -LExit: - ReleaseObject(pSystemInformation); - - return hr; -} - - -// internal function definitions - -static HRESULT GetAutomaticUpdatesService( - __out IAutomaticUpdates **ppAutomaticUpdates - ) -{ - HRESULT hr = S_OK; - CLSID clsidAutomaticUpdates = { }; - - hr = ::CLSIDFromProgID(L"Microsoft.Update.AutoUpdate", &clsidAutomaticUpdates); - WuaExitOnFailure(hr, "Failed to get CLSID for Microsoft.Update.AutoUpdate."); - - hr = ::CoCreateInstance(clsidAutomaticUpdates, NULL, CLSCTX_INPROC_SERVER, IID_IAutomaticUpdates, reinterpret_cast(ppAutomaticUpdates)); - WuaExitOnFailure(hr, "Failed to create instance of Microsoft.Update.AutoUpdate."); - -LExit: - return hr; -} diff --git a/src/dutil/xmlutil.cpp b/src/dutil/xmlutil.cpp deleted file mode 100644 index 0f1e611d..00000000 --- a/src/dutil/xmlutil.cpp +++ /dev/null @@ -1,1332 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// Exit macros -#define XmlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) -#define XmlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) -#define XmlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) -#define XmlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) -#define XmlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) -#define XmlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) -#define XmlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_XMLUTIL, p, x, e, s, __VA_ARGS__) -#define XmlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, p, x, s, __VA_ARGS__) -#define XmlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_XMLUTIL, p, x, e, s, __VA_ARGS__) -#define XmlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, p, x, s, __VA_ARGS__) -#define XmlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_XMLUTIL, e, x, s, __VA_ARGS__) -#define XmlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_XMLUTIL, g, x, s, __VA_ARGS__) - -// intialization globals -CLSID vclsidXMLDOM = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0} }; -static volatile LONG vcXmlInitialized = 0; -static BOOL vfMsxml40 = FALSE; -static BOOL fComInitialized = FALSE; -BOOL vfMsxml30 = FALSE; - -/******************************************************************** - XmlInitialize - finds an appropriate version of the XML DOM - -*********************************************************************/ -extern "C" HRESULT DAPI XmlInitialize( - ) -{ - HRESULT hr = S_OK; - - if (!fComInitialized) - { - hr = ::CoInitialize(0); - if (RPC_E_CHANGED_MODE != hr) - { - XmlExitOnFailure(hr, "failed to initialize COM"); - fComInitialized = TRUE; - } - } - - LONG cInitialized = ::InterlockedIncrement(&vcXmlInitialized); - if (1 == cInitialized) - { - // NOTE: 4.0 behaves differently than 3.0 so there may be problems doing this -#if 0 - hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument.4.0", &vclsidXMLDOM); - if (S_OK == hr) - { - vfMsxml40 = TRUE; - Trace(REPORT_VERBOSE, "found Msxml2.DOMDocument.4.0"); - ExitFunction(); - } -#endif - hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument", &vclsidXMLDOM); - if (FAILED(hr)) - { - // try to fall back to old MSXML - hr = ::CLSIDFromProgID(L"MSXML.DOMDocument", &vclsidXMLDOM); - } - XmlExitOnFailure(hr, "failed to get CLSID for XML DOM"); - - Assert(IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument) || - IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20) || - IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument26) || - IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) || - IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument40) || - IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument50) || - IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument60)); - } - - hr = S_OK; -LExit: - return hr; -} - - -/******************************************************************** - XmUninitialize - - -*********************************************************************/ -extern "C" void DAPI XmlUninitialize( - ) -{ - AssertSz(vcXmlInitialized, "XmlUninitialize called when not initialized"); - - LONG cInitialized = ::InterlockedDecrement(&vcXmlInitialized); - - if (0 == cInitialized) - { - memset(&vclsidXMLDOM, 0, sizeof(vclsidXMLDOM)); - - if (fComInitialized) - { - ::CoUninitialize(); - } - } -} - -extern "C" HRESULT DAPI XmlCreateElement( - __in IXMLDOMDocument *pixdDocument, - __in_z LPCWSTR wzElementName, - __out IXMLDOMElement **ppixnElement - ) -{ - if (!ppixnElement || !pixdDocument) - { - return E_INVALIDARG; - } - - HRESULT hr = S_OK; - BSTR bstrElementName = ::SysAllocString(wzElementName); - XmlExitOnNull(bstrElementName, hr, E_OUTOFMEMORY, "failed SysAllocString"); - hr = pixdDocument->createElement(bstrElementName, ppixnElement); -LExit: - ReleaseBSTR(bstrElementName); - return hr; -} - - -/******************************************************************** - XmlCreateDocument - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlCreateDocument( - __in_opt LPCWSTR pwzElementName, - __out IXMLDOMDocument** ppixdDocument, - __out_opt IXMLDOMElement** ppixeRootElement - ) -{ - HRESULT hr = S_OK; - BOOL (WINAPI *pfnDisableWow64)(__out PVOID* ) = NULL; - BOOLEAN (WINAPI *pfnEnableWow64)(__in BOOLEAN ) = NULL; - BOOL (WINAPI *pfnRevertWow64)(__in PVOID ) = NULL; - BOOL fWow64Available = FALSE; - void *pvWow64State = NULL; - - // RELEASEME - IXMLDOMElement* pixeRootElement = NULL; - IXMLDOMDocument *pixdDocument = NULL; - - // Test if we have access to the Wow64 API, and store the result in fWow64Available - HMODULE hKernel32 = ::GetModuleHandleA("kernel32.dll"); - XmlExitOnNullWithLastError(hKernel32, hr, "failed to get handle to kernel32.dll"); - - // This will test if we have access to the Wow64 API - if (NULL != GetProcAddress(hKernel32, "IsWow64Process")) - { - pfnDisableWow64 = (BOOL (WINAPI *)(PVOID *))::GetProcAddress(hKernel32, "Wow64DisableWow64FsRedirection"); - pfnEnableWow64 = (BOOLEAN (WINAPI *)(BOOLEAN))::GetProcAddress(hKernel32, "Wow64EnableWow64FsRedirection"); - pfnRevertWow64 = (BOOL (WINAPI *)(PVOID))::GetProcAddress(hKernel32, "Wow64RevertWow64FsRedirection"); - - fWow64Available = pfnDisableWow64 && pfnEnableWow64 && pfnRevertWow64; - } - - // create the top level XML document - AssertSz(vcXmlInitialized, "XmlInitialize() was not called"); - - // Enable Wow64 Redirection, if possible - if (fWow64Available) - { - // We want to enable Wow64 redirection, but the Wow64 API requires us to disable it first to get its current state (so we can revert it later) - pfnDisableWow64(&pvWow64State); - // If we fail to enable it, don't bother trying to disable it later on - fWow64Available = pfnEnableWow64(TRUE); - } - - hr = ::CoCreateInstance(vclsidXMLDOM, NULL, CLSCTX_INPROC_SERVER, XmlUtil_IID_IXMLDOMDocument, (void**)&pixdDocument); - XmlExitOnFailure(hr, "failed to create XML DOM Document"); - Assert(pixdDocument); - - if (IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) || IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20)) - { - vfMsxml30 = TRUE; - } - - if (pwzElementName) - { - hr = XmlCreateElement(pixdDocument, pwzElementName, &pixeRootElement); - XmlExitOnFailure(hr, "failed XmlCreateElement"); - hr = pixdDocument->appendChild(pixeRootElement, NULL); - XmlExitOnFailure(hr, "failed appendChild"); - } - - *ppixdDocument = pixdDocument; - pixdDocument = NULL; - - if (ppixeRootElement) - { - *ppixeRootElement = pixeRootElement; - pixeRootElement = NULL; - } - -LExit: - // Re-disable Wow64 Redirection, if appropriate - if (fWow64Available && !pfnRevertWow64(pvWow64State)) - { - // If we expected to be able to revert, and couldn't, fail in the only graceful way we can - ::ExitProcess(1); - } - - ReleaseObject(pixeRootElement); - ReleaseObject(pixdDocument); - return hr; -} - - -/******************************************************************** - XmlLoadDocument - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlLoadDocument( - __in_z LPCWSTR wzDocument, - __out IXMLDOMDocument** ppixdDocument - ) -{ - return XmlLoadDocumentEx(wzDocument, 0, ppixdDocument); -} - - -/******************************************************************** - XmlReportParseError - - -*********************************************************************/ -static void XmlReportParseError( - __in IXMLDOMParseError* pixpe - ) -{ - HRESULT hr = S_OK; - long lNumber = 0; - BSTR bstr = NULL; - - Trace(REPORT_STANDARD, "Failed to parse XML. IXMLDOMParseError reports:"); - - hr = pixpe->get_errorCode(&lNumber); - XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.errorCode."); - Trace(REPORT_STANDARD, "errorCode = 0x%x", lNumber); - - hr = pixpe->get_filepos(&lNumber); - XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.filepos."); - Trace(REPORT_STANDARD, "filepos = %d", lNumber); - - hr = pixpe->get_line(&lNumber); - XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.line."); - Trace(REPORT_STANDARD, "line = %d", lNumber); - - hr = pixpe->get_linepos(&lNumber); - XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.linepos."); - Trace(REPORT_STANDARD, "linepos = %d", lNumber); - - hr = pixpe->get_reason(&bstr); - XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.reason."); - Trace(REPORT_STANDARD, "reason = %ls", bstr); - ReleaseNullBSTR(bstr); - - hr = pixpe->get_srcText (&bstr); - XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.srcText ."); - Trace(REPORT_STANDARD, "srcText = %ls", bstr); - ReleaseNullBSTR(bstr); - -LExit: - ReleaseBSTR(bstr); -} - -/******************************************************************** - XmlLoadDocumentEx - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlLoadDocumentEx( - __in_z LPCWSTR wzDocument, - __in DWORD dwAttributes, - __out IXMLDOMDocument** ppixdDocument - ) -{ - HRESULT hr = S_OK; - VARIANT_BOOL vbSuccess = 0; - - // RELEASEME - IXMLDOMDocument* pixd = NULL; - IXMLDOMParseError* pixpe = NULL; - BSTR bstrLoad = NULL; - - if (!wzDocument || !*wzDocument) - { - hr = E_UNEXPECTED; - XmlExitOnFailure(hr, "string must be non-null"); - } - - hr = XmlCreateDocument(NULL, &pixd); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed XmlCreateDocument"); - - if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE) - { - hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE); - XmlExitOnFailure(hr, "failed put_preserveWhiteSpace"); - } - - // Security issue. Avoid triggering anything external. - hr = pixd->put_validateOnParse(VARIANT_FALSE); - XmlExitOnFailure(hr, "failed put_validateOnParse"); - hr = pixd->put_resolveExternals(VARIANT_FALSE); - XmlExitOnFailure(hr, "failed put_resolveExternals"); - - bstrLoad = ::SysAllocString(wzDocument); - XmlExitOnNull(bstrLoad, hr, E_OUTOFMEMORY, "failed to allocate bstr for Load in XmlLoadDocumentEx"); - - hr = pixd->loadXML(bstrLoad, &vbSuccess); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); - } - - if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe)) - { - XmlReportParseError(pixpe); - } - - XmlExitOnFailure(hr, "failed loadXML"); - - - hr = S_OK; -LExit: - if (ppixdDocument) - { - *ppixdDocument = pixd; - pixd = NULL; - } - ReleaseBSTR(bstrLoad); - ReleaseObject(pixd); - ReleaseObject(pixpe); - - return hr; -} - - -/******************************************************************* - XmlLoadDocumentFromFile - -********************************************************************/ -extern "C" HRESULT DAPI XmlLoadDocumentFromFile( - __in_z LPCWSTR wzPath, - __out IXMLDOMDocument** ppixdDocument - ) -{ - return XmlLoadDocumentFromFileEx(wzPath, 0, ppixdDocument); -} - - -/******************************************************************* - XmlLoadDocumentFromFileEx - -********************************************************************/ -extern "C" HRESULT DAPI XmlLoadDocumentFromFileEx( - __in_z LPCWSTR wzPath, - __in DWORD dwAttributes, - __out IXMLDOMDocument** ppixdDocument - ) -{ - HRESULT hr = S_OK; - VARIANT varPath; - VARIANT_BOOL vbSuccess = 0; - - IXMLDOMDocument* pixd = NULL; - IXMLDOMParseError* pixpe = NULL; - - ::VariantInit(&varPath); - varPath.vt = VT_BSTR; - varPath.bstrVal = ::SysAllocString(wzPath); - XmlExitOnNull(varPath.bstrVal, hr, E_OUTOFMEMORY, "failed to allocate bstr for Path in XmlLoadDocumentFromFileEx"); - - hr = XmlCreateDocument(NULL, &pixd); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed XmlCreateDocument"); - - if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE) - { - hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE); - XmlExitOnFailure(hr, "failed put_preserveWhiteSpace"); - } - - // Avoid triggering anything external. - hr = pixd->put_validateOnParse(VARIANT_FALSE); - XmlExitOnFailure(hr, "failed put_validateOnParse"); - hr = pixd->put_resolveExternals(VARIANT_FALSE); - XmlExitOnFailure(hr, "failed put_resolveExternals"); - - pixd->put_async(VARIANT_FALSE); - hr = pixd->load(varPath, &vbSuccess); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); - } - - if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe)) - { - XmlReportParseError(pixpe); - } - - XmlExitOnFailure(hr, "failed to load XML from: %ls", wzPath); - - if (ppixdDocument) - { - *ppixdDocument = pixd; - pixd = NULL; - } - - hr = S_OK; -LExit: - ReleaseVariant(varPath); - ReleaseObject(pixd); - ReleaseObject(pixpe); - - return hr; -} - - -/******************************************************************** - XmlLoadDocumentFromBuffer - -*********************************************************************/ -extern "C" HRESULT DAPI XmlLoadDocumentFromBuffer( - __in_bcount(cbSource) const BYTE* pbSource, - __in SIZE_T cbSource, - __out IXMLDOMDocument** ppixdDocument - ) -{ - HRESULT hr = S_OK; - IXMLDOMDocument* pixdDocument = NULL; - SAFEARRAY sa = { }; - VARIANT vtXmlSource; - VARIANT_BOOL vbSuccess = 0; - - ::VariantInit(&vtXmlSource); - - // create document - hr = XmlCreateDocument(NULL, &pixdDocument); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed XmlCreateDocument"); - - // Security issue. Avoid triggering anything external. - hr = pixdDocument->put_validateOnParse(VARIANT_FALSE); - XmlExitOnFailure(hr, "failed put_validateOnParse"); - hr = pixdDocument->put_resolveExternals(VARIANT_FALSE); - XmlExitOnFailure(hr, "failed put_resolveExternals"); - - // load document - sa.cDims = 1; - sa.fFeatures = FADF_STATIC | FADF_FIXEDSIZE; - sa.cbElements = 1; - sa.pvData = (PVOID)pbSource; - sa.rgsabound[0].cElements = (ULONG)cbSource; - vtXmlSource.vt = VT_ARRAY | VT_UI1; - vtXmlSource.parray = &sa; - - hr = pixdDocument->load(vtXmlSource, &vbSuccess); - if (S_FALSE == hr) - { - hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); - } - XmlExitOnFailure(hr, "failed loadXML"); - - // return value - *ppixdDocument = pixdDocument; - pixdDocument = NULL; - -LExit: - ReleaseObject(pixdDocument); - return hr; -} - - -/******************************************************************** - XmlSetAttribute - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSetAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __in_z LPCWSTR pwzAttributeValue - ) -{ - HRESULT hr = S_OK; - VARIANT varAttributeValue; - ::VariantInit(&varAttributeValue); - - // RELEASEME - IXMLDOMDocument* pixdDocument = NULL; - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMAttribute* pixaAttribute = NULL; - IXMLDOMNode* pixaNode = NULL; - BSTR bstrAttributeName = ::SysAllocString(pwzAttribute); - XmlExitOnNull(bstrAttributeName, hr, E_OUTOFMEMORY, "failed to allocate bstr for AttributeName in XmlSetAttribute"); - - hr = pixnNode->get_attributes(&pixnnmAttributes); - XmlExitOnFailure(hr, "failed get_attributes in XmlSetAttribute(%ls)", pwzAttribute); - - hr = pixnNode->get_ownerDocument(&pixdDocument); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); - - hr = pixdDocument->createAttribute(bstrAttributeName, &pixaAttribute); - XmlExitOnFailure(hr, "failed createAttribute in XmlSetAttribute(%ls)", pwzAttribute); - - varAttributeValue.vt = VT_BSTR; - varAttributeValue.bstrVal = ::SysAllocString(pwzAttributeValue); - if (!varAttributeValue.bstrVal) - { - hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); - } - XmlExitOnFailure(hr, "failed SysAllocString in XmlSetAttribute"); - - hr = pixaAttribute->put_nodeValue(varAttributeValue); - XmlExitOnFailure(hr, "failed put_nodeValue in XmlSetAttribute(%ls)", pwzAttribute); - - hr = pixnnmAttributes->setNamedItem(pixaAttribute, &pixaNode); - XmlExitOnFailure(hr, "failed setNamedItem in XmlSetAttribute(%ls)", pwzAttribute); - -LExit: - ReleaseObject(pixdDocument); - ReleaseObject(pixnnmAttributes); - ReleaseObject(pixaAttribute); - ReleaseObject(pixaNode); - ReleaseBSTR(varAttributeValue.bstrVal); - ReleaseBSTR(bstrAttributeName); - - return hr; -} - - -/******************************************************************** - XmlSelectSingleNode - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSelectSingleNode( - __in IXMLDOMNode* pixnParent, - __in_z LPCWSTR wzXPath, - __out IXMLDOMNode **ppixnChild - ) -{ - HRESULT hr = S_OK; - - BSTR bstrXPath = NULL; - - XmlExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectSingleNode"); - XmlExitOnNull(ppixnChild, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectSingleNode"); - - bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L""); - XmlExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectSingleNode"); - - hr = pixnParent->selectSingleNode(bstrXPath, ppixnChild); - -LExit: - ReleaseBSTR(bstrXPath); - - return hr; -} - - -/******************************************************************** - XmlCreateTextNode - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlCreateTextNode( - __in IXMLDOMDocument *pixdDocument, - __in_z LPCWSTR wzText, - __out IXMLDOMText **ppixnTextNode - ) -{ - if (!ppixnTextNode || !pixdDocument) - { - return E_INVALIDARG; - } - - HRESULT hr = S_OK; - BSTR bstrText = ::SysAllocString(wzText); - XmlExitOnNull(bstrText, hr, E_OUTOFMEMORY, "failed SysAllocString"); - hr = pixdDocument->createTextNode(bstrText, ppixnTextNode); -LExit: - ReleaseBSTR(bstrText); - - return hr; -} - - -/******************************************************************** - XmlGetText - -*********************************************************************/ -extern "C" HRESULT DAPI XmlGetText( - __in IXMLDOMNode* pixnNode, - __deref_out_z BSTR* pbstrText - ) -{ - return pixnNode->get_text(pbstrText); -} - - -/******************************************************************** - XmlGetAttribute - -*********************************************************************/ -extern "C" HRESULT DAPI XmlGetAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __deref_out_z BSTR* pbstrAttributeValue - ) -{ - Assert(pixnNode); - HRESULT hr = S_OK; - - // RELEASEME - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNode* pixnAttribute = NULL; - VARIANT varAttributeValue; - BSTR bstrAttribute = SysAllocString(pwzAttribute); - - // INIT - ::VariantInit(&varAttributeValue); - - // get attribute value from source - hr = pixnNode->get_attributes(&pixnnmAttributes); - XmlExitOnFailure(hr, "failed get_attributes"); - - hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute); - if (S_FALSE == hr) - { - // hr = E_FAIL; - ExitFunction(); - } - XmlExitOnFailure(hr, "failed getNamedItem in XmlGetAttribute(%ls)", pwzAttribute); - - hr = pixnAttribute->get_nodeValue(&varAttributeValue); - XmlExitOnFailure(hr, "failed get_nodeValue in XmlGetAttribute(%ls)", pwzAttribute); - - // steal the BSTR from the VARIANT - if (S_OK == hr && pbstrAttributeValue) - { - *pbstrAttributeValue = varAttributeValue.bstrVal; - varAttributeValue.bstrVal = NULL; - } - -LExit: - ReleaseObject(pixnnmAttributes); - ReleaseObject(pixnAttribute); - ReleaseVariant(varAttributeValue); - ReleaseBSTR(bstrAttribute); - - return hr; -} - - -/******************************************************************** - XmlGetAttributeEx - -*********************************************************************/ -HRESULT DAPI XmlGetAttributeEx( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR wzAttribute, - __deref_out_z LPWSTR* psczAttributeValue - ) -{ - Assert(pixnNode); - HRESULT hr = S_OK; - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - IXMLDOMNode* pixnAttribute = NULL; - VARIANT varAttributeValue; - BSTR bstrAttribute = NULL; - - ::VariantInit(&varAttributeValue); - - // get attribute value from source - hr = pixnNode->get_attributes(&pixnnmAttributes); - XmlExitOnFailure(hr, "Failed get_attributes."); - - bstrAttribute = ::SysAllocString(wzAttribute); - XmlExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "Failed to allocate attribute name BSTR."); - - hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute); - if (S_FALSE == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - XmlExitOnFailure(hr, "Failed getNamedItem in XmlGetAttribute(%ls)", wzAttribute); - - hr = pixnAttribute->get_nodeValue(&varAttributeValue); - if (S_FALSE == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - XmlExitOnFailure(hr, "Failed get_nodeValue in XmlGetAttribute(%ls)", wzAttribute); - - // copy value - hr = StrAllocString(psczAttributeValue, varAttributeValue.bstrVal, 0); - XmlExitOnFailure(hr, "Failed to copy attribute value."); - -LExit: - ReleaseObject(pixnnmAttributes); - ReleaseObject(pixnAttribute); - ReleaseVariant(varAttributeValue); - ReleaseBSTR(bstrAttribute); - - return hr; -} - - -/******************************************************************** - XmlGetYesNoAttribute - -*********************************************************************/ -HRESULT DAPI XmlGetYesNoAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR wzAttribute, - __out BOOL* pfYes - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - hr = XmlGetAttributeEx(pixnNode, wzAttribute, &sczValue); - if (E_NOTFOUND != hr) - { - XmlExitOnFailure(hr, "Failed to get attribute."); - - *pfYes = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczValue, -1, L"yes", -1); - } - -LExit: - ReleaseStr(sczValue); - - return hr; -} - - - -/******************************************************************** - XmlGetAttributeNumber - -*********************************************************************/ -extern "C" HRESULT DAPI XmlGetAttributeNumber( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __out DWORD* pdwValue - ) -{ - HRESULT hr = XmlGetAttributeNumberBase(pixnNode, pwzAttribute, 10, pdwValue); - return hr; -} - - -/******************************************************************** - XmlGetAttributeNumberBase - -*********************************************************************/ -extern "C" HRESULT DAPI XmlGetAttributeNumberBase( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __in int nBase, - __out DWORD* pdwValue - ) -{ - HRESULT hr = S_OK; - BSTR bstrPointer = NULL; - - hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrPointer); - XmlExitOnFailure(hr, "Failed to get value from attribute."); - - if (S_OK == hr) - { - *pdwValue = wcstoul(bstrPointer, NULL, nBase); - } - -LExit: - ReleaseBSTR(bstrPointer); - return hr; -} - - -/******************************************************************** - XmlGetAttributeLargeNumber - -*********************************************************************/ -extern "C" HRESULT DAPI XmlGetAttributeLargeNumber( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute, - __out DWORD64* pdw64Value - ) -{ - HRESULT hr = S_OK; - BSTR bstrValue = NULL; - - hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrValue); - XmlExitOnFailure(hr, "failed XmlGetAttribute"); - - if (S_OK == hr) - { - LONGLONG ll = 0; - hr = StrStringToInt64(bstrValue, 0, &ll); - XmlExitOnFailure(hr, "Failed to treat attribute value as number."); - - *pdw64Value = ll; - } - else - { - *pdw64Value = 0; - } - -LExit: - ReleaseBSTR(bstrValue); - return hr; -} - - -/******************************************************************** - XmlGetNamedItem - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlGetNamedItem( - __in IXMLDOMNamedNodeMap *pixnmAttributes, - __in_opt LPCWSTR wzName, - __out IXMLDOMNode **ppixnNamedItem - ) -{ - if (!pixnmAttributes || !ppixnNamedItem) - { - return E_INVALIDARG; - } - - HRESULT hr = S_OK; - BSTR bstrName = ::SysAllocString(wzName); - XmlExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString"); - - hr = pixnmAttributes->getNamedItem(bstrName, ppixnNamedItem); - -LExit: - ReleaseBSTR(bstrName); - return hr; -} - - -/******************************************************************** - XmlSetText - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSetText( - __in IXMLDOMNode *pixnNode, - __in_z LPCWSTR pwzText - ) -{ - Assert(pixnNode && pwzText); - HRESULT hr = S_OK; - DOMNodeType dnType; - - // RELEASEME - IXMLDOMDocument* pixdDocument = NULL; - IXMLDOMNodeList* pixnlNodeList = NULL; - IXMLDOMNode* pixnChildNode = NULL; - IXMLDOMText* pixtTextNode = NULL; - VARIANT varText; - - ::VariantInit(&varText); - - // find the text node - hr = pixnNode->get_childNodes(&pixnlNodeList); - XmlExitOnFailure(hr, "failed to get child nodes"); - - while (S_OK == (hr = pixnlNodeList->nextNode(&pixnChildNode))) - { - hr = pixnChildNode->get_nodeType(&dnType); - XmlExitOnFailure(hr, "failed to get node type"); - - if (NODE_TEXT == dnType) - break; - ReleaseNullObject(pixnChildNode); - } - if (S_FALSE == hr) - { - hr = S_OK; - } - - if (pixnChildNode) - { - varText.vt = VT_BSTR; - varText.bstrVal = ::SysAllocString(pwzText); - if (!varText.bstrVal) - { - hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); - } - XmlExitOnFailure(hr, "failed SysAllocString in XmlSetText"); - - hr = pixnChildNode->put_nodeValue(varText); - XmlExitOnFailure(hr, "failed IXMLDOMNode::put_nodeValue"); - } - else - { - hr = pixnNode->get_ownerDocument(&pixdDocument); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); - - hr = XmlCreateTextNode(pixdDocument, pwzText, &pixtTextNode); - XmlExitOnFailure(hr, "failed createTextNode in XmlSetText(%ls)", pwzText); - - hr = pixnNode->appendChild(pixtTextNode, NULL); - XmlExitOnFailure(hr, "failed appendChild in XmlSetText(%ls)", pwzText); - } - - hr = *pwzText ? S_OK : S_FALSE; - -LExit: - ReleaseObject(pixnlNodeList); - ReleaseObject(pixnChildNode); - ReleaseObject(pixdDocument); - ReleaseObject(pixtTextNode); - ReleaseVariant(varText); - return hr; -} - - -/******************************************************************** - XmlSetTextNumber - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSetTextNumber( - __in IXMLDOMNode *pixnNode, - __in DWORD dwValue - ) -{ - HRESULT hr = S_OK; - WCHAR wzValue[12]; - - hr = ::StringCchPrintfW(wzValue, countof(wzValue), L"%u", dwValue); - XmlExitOnFailure(hr, "Failed to format numeric value as string."); - - hr = XmlSetText(pixnNode, wzValue); - -LExit: - return hr; -} - - -/******************************************************************** - XmlCreateChild - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlCreateChild( - __in IXMLDOMNode* pixnParent, - __in_z LPCWSTR pwzElementType, - __out IXMLDOMNode** ppixnChild - ) -{ - HRESULT hr = S_OK; - - // RELEASEME - IXMLDOMDocument* pixdDocument = NULL; - IXMLDOMNode* pixnChild = NULL; - - hr = pixnParent->get_ownerDocument(&pixdDocument); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed get_ownerDocument"); - - hr = XmlCreateElement(pixdDocument, pwzElementType, (IXMLDOMElement**) &pixnChild); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed createElement"); - - pixnParent->appendChild(pixnChild,NULL); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed appendChild"); - - if (ppixnChild) - { - *ppixnChild = pixnChild; - pixnChild = NULL; - } - -LExit: - ReleaseObject(pixdDocument); - ReleaseObject(pixnChild); - return hr; -} - -/******************************************************************** - XmlRemoveAttribute - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlRemoveAttribute( - __in IXMLDOMNode* pixnNode, - __in_z LPCWSTR pwzAttribute - ) -{ - HRESULT hr = S_OK; - - // RELEASEME - IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; - BSTR bstrAttribute = ::SysAllocString(pwzAttribute); - XmlExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "failed to allocate bstr for attribute in XmlRemoveAttribute"); - - hr = pixnNode->get_attributes(&pixnnmAttributes); - XmlExitOnFailure(hr, "failed get_attributes in RemoveXmlAttribute(%ls)", pwzAttribute); - - hr = pixnnmAttributes->removeNamedItem(bstrAttribute, NULL); - XmlExitOnFailure(hr, "failed removeNamedItem in RemoveXmlAttribute(%ls)", pwzAttribute); - -LExit: - ReleaseObject(pixnnmAttributes); - ReleaseBSTR(bstrAttribute); - - return hr; -} - - -/******************************************************************** - XmlSelectNodes - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSelectNodes( - __in IXMLDOMNode* pixnParent, - __in_z LPCWSTR wzXPath, - __out IXMLDOMNodeList **ppixnlChildren - ) -{ - HRESULT hr = S_OK; - - BSTR bstrXPath = NULL; - - XmlExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectNodes"); - XmlExitOnNull(ppixnlChildren, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectNodes"); - - bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L""); - XmlExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectNodes"); - - hr = pixnParent->selectNodes(bstrXPath, ppixnlChildren); - -LExit: - ReleaseBSTR(bstrXPath); - return hr; -} - - -/******************************************************************** - XmlNextAttribute - returns the next attribute in a node list - - NOTE: pbstrAttribute is optional - returns S_OK if found an element - returns S_FALSE if no element found - returns E_* if something went wrong -********************************************************************/ -extern "C" HRESULT DAPI XmlNextAttribute( - __in IXMLDOMNamedNodeMap* pixnnm, - __out IXMLDOMNode** pixnAttribute, - __deref_opt_out_z_opt BSTR* pbstrAttribute - ) -{ - Assert(pixnnm && pixnAttribute); - - HRESULT hr = S_OK; - IXMLDOMNode* pixn = NULL; - DOMNodeType nt; - - // null out the return values - *pixnAttribute = NULL; - if (pbstrAttribute) - { - *pbstrAttribute = NULL; - } - - hr = pixnnm->nextNode(&pixn); - XmlExitOnFailure(hr, "Failed to get next attribute."); - - if (S_OK == hr) - { - hr = pixn->get_nodeType(&nt); - XmlExitOnFailure(hr, "failed to get node type"); - - if (NODE_ATTRIBUTE != nt) - { - hr = E_UNEXPECTED; - XmlExitOnFailure(hr, "Failed to get expected node type back: attribute"); - } - - // if the caller asked for the attribute name - if (pbstrAttribute) - { - hr = pixn->get_baseName(pbstrAttribute); - XmlExitOnFailure(hr, "failed to get attribute name"); - } - - *pixnAttribute = pixn; - pixn = NULL; - } - -LExit: - ReleaseObject(pixn); - return hr; -} - - -/******************************************************************** - XmlNextElement - returns the next element in a node list - - NOTE: pbstrElement is optional - returns S_OK if found an element - returns S_FALSE if no element found - returns E_* if something went wrong -********************************************************************/ -extern "C" HRESULT DAPI XmlNextElement( - __in IXMLDOMNodeList* pixnl, - __out IXMLDOMNode** pixnElement, - __deref_opt_out_z_opt BSTR* pbstrElement - ) -{ - Assert(pixnl && pixnElement); - - HRESULT hr = S_OK; - IXMLDOMNode* pixn = NULL; - DOMNodeType nt; - - // null out the return values - *pixnElement = NULL; - if (pbstrElement) - { - *pbstrElement = NULL; - } - - // - // find the next element in the list - // - while (S_OK == (hr = pixnl->nextNode(&pixn))) - { - hr = pixn->get_nodeType(&nt); - XmlExitOnFailure(hr, "failed to get node type"); - - if (NODE_ELEMENT == nt) - break; - - ReleaseNullObject(pixn); - } - XmlExitOnFailure(hr, "failed to get next element"); - - // if we have a node and the caller asked for the element name - if (pixn && pbstrElement) - { - hr = pixn->get_baseName(pbstrElement); - XmlExitOnFailure(hr, "failed to get element name"); - } - - *pixnElement = pixn; - pixn = NULL; - - hr = *pixnElement ? S_OK : S_FALSE; -LExit: - ReleaseObject(pixn); - return hr; -} - - -/******************************************************************** - XmlRemoveChildren - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlRemoveChildren( - __in IXMLDOMNode* pixnSource, - __in_z LPCWSTR pwzXPath - ) -{ - HRESULT hr = S_OK; - - // RELEASEME - IXMLDOMNodeList* pixnlNodeList = NULL; - IXMLDOMNode* pixnNode = NULL; - IXMLDOMNode* pixnRemoveChild = NULL; - - if (pwzXPath) - { - hr = XmlSelectNodes(pixnSource, pwzXPath, &pixnlNodeList); - XmlExitOnFailure(hr, "failed XmlSelectNodes"); - } - else - { - hr = pixnSource->get_childNodes(&pixnlNodeList); - XmlExitOnFailure(hr, "failed childNodes"); - } - if (S_FALSE == hr) - { - ExitFunction(); - } - - while (S_OK == (hr = pixnlNodeList->nextNode(&pixnNode))) - { - hr = pixnSource->removeChild(pixnNode, &pixnRemoveChild); - XmlExitOnFailure(hr, "failed removeChild"); - - ReleaseNullObject(pixnRemoveChild); - ReleaseNullObject(pixnNode); - } - if (S_FALSE == hr) - { - hr = S_OK; - } - -LExit: - ReleaseObject(pixnlNodeList); - ReleaseObject(pixnNode); - ReleaseObject(pixnRemoveChild); - - return hr; -} - - -/******************************************************************** - XmlSaveDocument - - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSaveDocument( - __in IXMLDOMDocument* pixdDocument, - __inout LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - - // RELEASEME - VARIANT varsDestPath; - - ::VariantInit(&varsDestPath); - varsDestPath.vt = VT_BSTR; - varsDestPath.bstrVal = ::SysAllocString(wzPath); - if (!varsDestPath.bstrVal) - { - hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); - } - XmlExitOnFailure(hr, "failed to create BSTR"); - - hr = pixdDocument->save(varsDestPath); - if (hr == S_FALSE) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "failed save in WriteDocument"); - -LExit: - ReleaseVariant(varsDestPath); - return hr; -} - - -/******************************************************************** - XmlSaveDocumentToBuffer - -*********************************************************************/ -extern "C" HRESULT DAPI XmlSaveDocumentToBuffer( - __in IXMLDOMDocument* pixdDocument, - __deref_out_bcount(*pcbDest) BYTE** ppbDest, - __out DWORD* pcbDest - ) -{ - HRESULT hr = S_OK; - IStream* pStream = NULL; - LARGE_INTEGER li = { }; - STATSTG statstg = { }; - BYTE* pbDest = NULL; - ULONG cbRead = 0; - VARIANT vtDestination; - - ::VariantInit(&vtDestination); - - // create stream - hr = ::CreateStreamOnHGlobal(NULL, TRUE, &pStream); - XmlExitOnFailure(hr, "Failed to create stream."); - - // write document to stream - vtDestination.vt = VT_UNKNOWN; - vtDestination.punkVal = (IUnknown*)pStream; - hr = pixdDocument->save(vtDestination); - XmlExitOnFailure(hr, "Failed to save document."); - - // get stream size - hr = pStream->Stat(&statstg, STATFLAG_NONAME); - XmlExitOnFailure(hr, "Failed to get stream size."); - - // allocate buffer - pbDest = static_cast(MemAlloc(statstg.cbSize.LowPart, TRUE)); - XmlExitOnNull(pbDest, hr, E_OUTOFMEMORY, "Failed to allocate destination buffer."); - - // read data from stream - li.QuadPart = 0; - hr = pStream->Seek(li, STREAM_SEEK_SET, NULL); - XmlExitOnFailure(hr, "Failed to seek stream."); - - hr = pStream->Read(pbDest, statstg.cbSize.LowPart, &cbRead); - if (cbRead < statstg.cbSize.LowPart) - { - hr = E_FAIL; - } - XmlExitOnFailure(hr, "Failed to read stream content to buffer."); - - // return value - *ppbDest = pbDest; - pbDest = NULL; - *pcbDest = statstg.cbSize.LowPart; - -LExit: - ReleaseObject(pStream); - ReleaseMem(pbDest); - return hr; -} diff --git a/src/dutil/xsd/thmutil.xsd b/src/dutil/xsd/thmutil.xsd deleted file mode 100644 index 46c20e4a..00000000 --- a/src/dutil/xsd/thmutil.xsd +++ /dev/null @@ -1,1188 +0,0 @@ - - - - - - - - Schema for describing Theme files processed by thmutil. - - - - - - - - - This is the top-level container element for every thmutil Theme file. - - - - - - - - - - - Relative path to an image file that can serve as a single source for images in the rest of the theme. - This image is referenced by controls using the SourceX and SourceY attributes. - Mutually exclusive with the ImageResource attribute. - - - - - - - Identifier that references an image resource in the module for the window. - Mutually exclusive with the ImageFile attribute. - - - - - - - - - Defines a font including the size and color. - - - - - - Name of the font face (required). - - - - Numeric identifier for the font. Due to limitations in thmutil the first Font must start with "0" and each subsequent Font must increment the Id by 1. Failure to ensure the Font identifiers follow this strict ordering will create unexpected behavior or crashes. - - - - - Font size. Use negative numbers to specify the font in pixels. - - - - - Font weight. - - - - - - A system color id or a hexadecimal value representing BGR foreground color of the font. - "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black. - If this attribute is absent the foreground will be transparent. - Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext. - - - - - - - A system color id or a hexadecimal value representing BGR background color of the font. - "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black. - If this attribute is absent the background will be transparent. - Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext. - - - - - - Specifies whether the font is underlined. - - - - - - - - - - List of images which can be shared between multiple controls. - - - - - - - - - Name of the ImageList, to be referenced by other controls. - - - - - - - - - Named set of controls that can be shown and hidden collectively. - - - - - - - Optional name for the page. - - - - - - - - - Defines the overall look of the main window. - - - - - - - - - - Specifies whether the ThmUtil default window proc should process WM_SIZE and WM_SIZING events. - - - - - - Caption for the window. - This is required if not using the StringId attribute. - - - - - - Numeric identifier to the Font element that serves as the default font for the window. - - - - - Height of the window's client area. - - - - - - Hexadecimal window style. If this is not specified the default value is: WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU. - If SourceX and SourceY are specified, then WS_OVERLAPPED is replaced with WS_POPUP. - - - - - - Relative path to an icon file for the window. Mutually exclusive with IconResource and SourceX and SourceY attributes. - - - - - - Identifier that references an icon resource in the module for the icon for the window. - Mutually exclusive with IconFile and SourceX and SourceY attributes. - - - - - - Minimum height of the window. Only functions if AutoResize is enabled. - - - - - Minimum width of the window. Only functions if AutoResize is enabled. - - - - - X offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource. - - - - - Y offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource. - - - - - - Identifier that references a string resource in the module to define the window caption. - Mutually exclusive with the Caption attribute. - - - - - - Width of the window's client area. - - - - - - - - Defines a control that rotates through a set of images on a specified interval. - - - - - - - - - - Specifies the time to wait before showing the next image, in milliseconds. - - - - - - Specifies whether the billboard should loop through the images infinitely. - - - - - - - - Defines a button. - - - - - Text to display in the button. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - If multiple Action elements are given, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and could be changed at any time). - If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute. - - - - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons. - - - - - Numeric identifier to the Font element that serves as the font when the control is hovered over. Only valid when using graphic buttons. - - - - - - Relative path to an image file to define a graphic button. - The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused. - Mutually exclusive with ImageResource and SourceX and SourceY attributes. - - - - - - - Identifier that references an image resource in the module to define a graphic button. - The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused. - Mutually exclusive with ImageFile and SourceX and SourceY attributes. - - - - - - Numeric identifier to the Font element that serves as the font when the control is selected. Only valid when using graphic buttons. - - - - - - Identifier that references a string resource in the module to define the text for the control. - - - - - - - - - - When the button is pressed, a directory browser dialog is shown. - - - - - - - The condition that determines if the parent control will execute this action. - - - - - - - The name of the variable to update when the user selects a directory from the dialog. - - - - - - - - - - When the button is pressed, the specified page is shown. - - - - - - - When set to 'yes', none of the variable changes made on the current page are saved. - - - - - - - The condition that determines if the parent control will execute this action. - - - - - - - The Name of the Page to show. - - - - - - - - - - When the button is pressed, the WM_CLOSE message is sent to the window. - - - - - - - The condition that determines if the parent control will execute this action. - - - - - - - - - Defines a checkbox. - - - - - Text to display beside the checkbox. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - Identifier that references a string resource in the module to define the text for the control. - - - - - - - - - Defines a combobox. - - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - - - Defines a button. - - - - - Text to display in the button. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - If multiple Action elements are given, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and could be changed at any time). - If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute. - - - - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons. - - - - - - Relative path to an icon file to define a command link glyph. - Mutually exclusive with ImageResource and SourceX and SourceY attributes. - - - - - - - Identifier that references an icon resource in the module to define a command link glyph. - Mutually exclusive with ImageFile and SourceX and SourceY attributes. - - - - - - - Relative path to an image file to define a command link glyph. - Mutually exclusive with ImageResource and SourceX and SourceY attributes. - - - - - - - Identifier that references an image resource in the module to define a command link glyph. - Mutually exclusive with ImageFile and SourceX and SourceY attributes. - - - - - - - Identifier that references a string resource in the module to define the text for the control. - - - - - - - - - Defines an edit box. - - - - - - - Initial text for the control. - Mutually exclusive with the StringId attribute. - - - - - - Specifies whether the edit box should auto-complete with file system paths. - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - Identifier that references a string resource in the module to define the initial text for the control. - - - - - - - - - - - Defines a hyperlink. - - - - - Text to display as the link. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - - - - - Numeric identifier to the Font element that serves as the unselected font. - - - - - Numeric identifier to the Font element that serves as the font when the control is hovered over. - - - - - Numeric identifier to the Font element that serves as the font when the control is selected. - - - - - - Identifier that references a string resource in the module to define the text for the control. - - - - - - - - - Defines a text block with support for HTML <a> tags. - - - - - Text to display as the link. - Use HTML <a href="URL"> to create a link. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - Identifier that references a string resource in the module to define the text for the control. - - - - - - - - - Defines an image for an ImageList or Billboard. - - - - - Relative path to an image file. Mutually exclusive with ImageResource. - - - - - Identifier that references an image resource in the module. Mutually exclusive with ImageFile. - - - - - - - - Defines an image. - - - - - - Relative path to an image file. Mutually exclusive with ImageResource and SourceX and SourceY attributes. - - - - - Identifier that references an image resource in the module. Mutually exclusive with ImageFile and SourceX and SourceY attributes. - - - - - - - - Defines a label. - - - - - Text for the label to display. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - - - - - Specifies whether the text should be centered horizontally in the width of the control. Default is "no". - - - - - By default ampersands (&) in the text will underline the next character and treat it as an accelerator key. Set this attribute to "yes" to disable that behavior. Default is "no". - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - Identifier that references a string resource in the module to define the text for the label. - - - - - - - - - Defines a listview. - - - - - - - - - Numeric identifier to the Font element that serves as the default font for the ListView. - - - - - Hexadecimal extended window style. - - - - - - The name of the ImageList to assign to this listview with type LVSIL_NORMAL. - - - - - - - The name of the ImageList to assign to this listview with type LVSIL_SMALL. - - - - - - - The name of the ImageList to assign to this listview with type LVSIL_STATE. - - - - - - - The name of the ImageList to assign to this listview with type LVSIL_GROUPHEADER. - - - - - - - - - - Defines note text for a command link control based on an optional condition. - If multiple Note elements are given for one control, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and may be changed at any time). - If none of the conditions of a control's Note elements are true, then it uses the text of the Note element without the Condition attribute. - - - - - - - - Note text for the parent command link control. - - - - - - The condition that determines when the parent control will use this note text. - - - - - - - - - - - Defines a collection of controls. - - - - - - - - - - Defines a progress bar. - - - - - - Relative path to an image file for the control. The image must be 4 pixels wide: left pixel is the left side of progress bar, left middle pixel is progress used, right middle pixel is progress unused, right pixel is right side of progress bar. Mutually exclusive with ImageResource and SourceX and SourceY attributes. - - - - - Identifier that references an image resource in the module for the control. The image must be 4 pixels wide: left pixel is the left side of progress bar, left middle pixel is progress used, right middle pixel is progress unused, right pixel is right side of progress bar. Mutually exclusive with ImageFile and SourceX and SourceY attributes. - - - - - - - - Defines an individual radio button within a set of radio buttons. - - - - - Text to display beside the radio button. - Mutually exclusive with the StringId attribute and child Text elements. - - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - Identifier that references a string resource in the module to define the text for the control. - - - - - - Optional value used when setting the variable associated with the set of radio buttons. - - - - - - - - Defines a set of radio buttons. - - - - - - - - Optional variable name for the set of radio buttons. - - - - - - - - Defines a rich edit control. - - - - - Initial text for the control. - Mutually exclusive with the StringId attribute. - - - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - - Identifier that references a string resource in the module to define the initial text for the control. - - - - - - - - - Defines a straight line. - - - - - - - - - Defines an individual tab within a set of tabs. - - - - - - - Caption of the tab. - Mutually exclusive with the StringId attribute. - - - - - - Identifier that references a string resource in the module to define the caption of the tab. - - - - - - - - - - - Defines a set of tabs. - - - - - - - - - Numeric identifier to the Font element that serves as the font for the control. - - - - - - - - - Defines text for the parent control based on an optional condition. - If multiple Text elements are given for one control, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and may be changed at any time). - If none of the conditions of a control's Text elements are true, then it uses the text of the Text element without the Condition attribute. - - - - - - - - Text for the parent control. - - - - - - The condition that determines when the parent control will use this text. - - - - - - - - - - - - Defines text for the parent control's tooltip. - - - - - - - - Text for the parent control's tooltip. - - - - - - - - - - Defines a treeview. - - - - - - Specifies whether the row always appears selected even when the treeview has lost focus. - - - - - Specifies whether drag and drop is enabled for the treeview. - - - - - Specifies whether an entire row is selected for the treeview. - - - - - Specifies whether the treeview will show buttons. - - - - - Specifies whether lines appear for all treeview items. - - - - - Specifies whether the root nodes have lines beside them. - - - - - - - - A column of a list. - - - - - - - Text for the column header. - Mutually exclusive with the StringId attribute. - - - - - Width of the column. - - - - - - Whether or not this column can grow to fill available width of the listview. - More than one column can be marked with yes - all expandable columns will share available extra space. - This is especially useful if the Window/@AutoResize is yes. - - - - - - - Identifier that references a string resource in the module to define the text for the column header. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Optional name for the control. - - - - - Set to 'yes' to disable automatic variable getting and setting, EnableCondition, VisibleCondition, and conditional Text elements. The default is 'no'. - - - - - A condition that determines if the control is enabled. If this condition is true or omitted, then the control will be enabled. - - - - - Height of the control. Non-positive values extend the control to the bottom of the window minus the value. - - - - - Hexadecimal window style for the control. - - - - - Specifies whether the control should be hidden when disabled. - - - - - Specifies whether the control is part of the tab sequence of controls. - - - - - Specifies whether the control is initially visible. - - - - - - A condition that determines if the control is visible. If this condition is true or omitted, then the control will be visible. - - - - - - Width of the control. Non-positive values extend the control to the right of the window minus the value. - - - - - X coordinate for the control from the left of the window. Negative values are coordinates from the right of the window minus the width of the control. - - - - - Y coordinate for the control from the top of the window. Negative values are coordinates from the bottom of the window minus the height of the control. - - - - - - - Values of this type will either be "yes" or "no". - - - - - - - - - - - Indicates a system color for a font. - - - - - - - - - - - - - - - - - - Indicates the foreground or background color of a font. - - - - - diff --git a/src/libs/dutil/CustomizedNativeRecommendedRules.ruleset b/src/libs/dutil/CustomizedNativeRecommendedRules.ruleset new file mode 100644 index 00000000..142b141c --- /dev/null +++ b/src/libs/dutil/CustomizedNativeRecommendedRules.ruleset @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/libs/dutil/Directory.Build.props b/src/libs/dutil/Directory.Build.props new file mode 100644 index 00000000..fb34d54e --- /dev/null +++ b/src/libs/dutil/Directory.Build.props @@ -0,0 +1,26 @@ + + + + + + Debug + false + + $(MSBuildProjectName) + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) + $(BaseOutputPath)obj\$(ProjectName)\ + $(BaseOutputPath)$(Configuration)\ + + WiX Toolset Team + WiX Toolset + Copyright (c) .NET Foundation and contributors. All rights reserved. + MS-RL + WiX Toolset + + + + + diff --git a/src/libs/dutil/Directory.Build.targets b/src/libs/dutil/Directory.Build.targets new file mode 100644 index 00000000..44701fb6 --- /dev/null +++ b/src/libs/dutil/Directory.Build.targets @@ -0,0 +1,73 @@ + + + + + + $(BaseOutputPath)obj\.tools + $(SigningToolFolder)\SignClient.exe + $(SigningToolFolder)\empty-filelist.txt + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), signing.json))\signing.json + + + + false + $(OutputPath)\$(AssemblyName).xml + + + + + $(PrivateRepositoryUrl.Replace('.git','')) + + $(MSBuildProjectName).nuspec + $(MSBuildProjectDirectory) + $(NuspecProperties);Id=$(PackageId);Authors="$(Authors)";Configuration=$(Configuration);Copyright="$(Copyright)";Description="$(Description)";Title="$(Title)" + $(NuspecProperties);Version=$(NPMPackageVersion);RepositoryCommit=$(SourceRevisionId);RepositoryType=$(RepositoryType);RepositoryUrl=$(PrivateRepositoryUrl);ProjectFolder=$(MSBuildProjectDirectory)\;ProjectUrl=$(ProjectUrl) + true + snupkg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libs/dutil/Directory.csproj.props b/src/libs/dutil/Directory.csproj.props new file mode 100644 index 00000000..81d24ad1 --- /dev/null +++ b/src/libs/dutil/Directory.csproj.props @@ -0,0 +1,13 @@ + + + + + true + true + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk)) + false + + diff --git a/src/libs/dutil/Directory.vcxproj.props b/src/libs/dutil/Directory.vcxproj.props new file mode 100644 index 00000000..9ea7071b --- /dev/null +++ b/src/libs/dutil/Directory.vcxproj.props @@ -0,0 +1,115 @@ + + + + + + Win32 + $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ + $(OutputPath)$(Platform)\ + + + $(Company) + $(Copyright) + + win-x86;win-x64;win-arm64 + native,Version=v0.0 + + + + $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + + + + $(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset + + + + + $(DisableSpecificCompilerWarnings) + Level4 + $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + Use + precomp.h + StdCall + true + false + -YlprecompDefine + /Zc:threadSafeInit- %(AdditionalOptions) + true + + + $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) + $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) + + + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) + + + $(ProjectSubSystem) + $(ProjectModuleDefinitionFile) + $(ResourceOnlyDll) + true + $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) + /IGNORE:4099 %(AdditionalOptions) + + + + + + NoExtensions + + + + + CDecl + + + + + OldStyle + true + true + + + + + Disabled + EnableFastChecks + _DEBUG;DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + + MultiThreadedDebugDll + + + + + MinSpace + NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreaded + + + true + true + + + + + + MultiThreadedDll + + + + + $(LinkKeyFile) + $(LinkDelaySign) + + + diff --git a/src/libs/dutil/NativeMultiTargeting.Build.props b/src/libs/dutil/NativeMultiTargeting.Build.props new file mode 100644 index 00000000..1ff46559 --- /dev/null +++ b/src/libs/dutil/NativeMultiTargeting.Build.props @@ -0,0 +1,10 @@ + + + + + + + $(BaseIntermediateOutputPath)$(Configuration)\$(PlatformToolset)\$(PlatformTarget)\ + $(OutputPath)$(PlatformToolset)\$(PlatformTarget)\ + + diff --git a/src/libs/dutil/README.md b/src/libs/dutil/README.md new file mode 100644 index 00000000..2d6605fe --- /dev/null +++ b/src/libs/dutil/README.md @@ -0,0 +1,2 @@ +# dutil +dutil.lib - foundation library for all native code in WiX Toolset diff --git a/src/libs/dutil/WixToolset.DUtil/acl2util.cpp b/src/libs/dutil/WixToolset.DUtil/acl2util.cpp new file mode 100644 index 00000000..598f12e7 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/acl2util.cpp @@ -0,0 +1,135 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define AclExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) +#define AclExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) +#define AclExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) +#define AclExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) +#define AclExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ACLUTIL, e, x, s, __VA_ARGS__) +#define AclExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ACLUTIL, g, x, s, __VA_ARGS__) + +/******************************************************************** +AclCalculateServiceSidString - gets the SID string for the given service name + +NOTE: psczSid should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI AclCalculateServiceSidString( + __in LPCWSTR wzServiceName, + __in SIZE_T cchServiceName, + __deref_out_z LPWSTR* psczSid + ) +{ + // TODO: use undocumented RtlCreateServiceSid function? + // http://blogs.technet.com/b/voy/archive/2007/03/22/per-service-sid.aspx + // Assume little endian. + HRESULT hr = S_OK; + LPWSTR sczUpperServiceName = NULL; + DWORD cbHash = SHA1_HASH_LEN; + BYTE* pbHash = NULL; + + Assert(psczSid); + + if (0 == cchServiceName) + { + hr = ::StringCchLengthW(wzServiceName, STRSAFE_MAX_CCH, reinterpret_cast(&cchServiceName)); + AclExitOnFailure(hr, "Failed to get the length of the service name."); + } + + hr = StrAllocStringToUpperInvariant(&sczUpperServiceName, wzServiceName, cchServiceName); + AclExitOnFailure(hr, "Failed to upper case the service name."); + + pbHash = reinterpret_cast(MemAlloc(cbHash, TRUE)); + AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to allocate hash byte array."); + + hr = CrypHashBuffer(reinterpret_cast(sczUpperServiceName), cchServiceName * sizeof(WCHAR), PROV_RSA_FULL, CALG_SHA1, pbHash, cbHash); + AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to hash the service name."); + + hr = StrAllocFormatted(psczSid, L"S-1-5-80-%u-%u-%u-%u-%u", + MAKEDWORD(MAKEWORD(pbHash[0], pbHash[1]), MAKEWORD(pbHash[2], pbHash[3])), + MAKEDWORD(MAKEWORD(pbHash[4], pbHash[5]), MAKEWORD(pbHash[6], pbHash[7])), + MAKEDWORD(MAKEWORD(pbHash[8], pbHash[9]), MAKEWORD(pbHash[10], pbHash[11])), + MAKEDWORD(MAKEWORD(pbHash[12], pbHash[13]), MAKEWORD(pbHash[14], pbHash[15])), + MAKEDWORD(MAKEWORD(pbHash[16], pbHash[17]), MAKEWORD(pbHash[18], pbHash[19]))); + +LExit: + ReleaseMem(pbHash); + ReleaseStr(sczUpperServiceName); + + return hr; +} + + +/******************************************************************** +AclGetAccountSidStringEx - gets a string version of the account's SID + calculates a service's SID if lookup fails + +NOTE: psczSid should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI AclGetAccountSidStringEx( + __in_z LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out_z LPWSTR* psczSid + ) +{ + HRESULT hr = S_OK; + SIZE_T cchAccount = 0; + PSID psid = NULL; + LPWSTR pwz = NULL; + LPWSTR sczSid = NULL; + + Assert(psczSid); + + hr = AclGetAccountSid(wzSystem, wzAccount, &psid); + if (SUCCEEDED(hr)) + { + Assert(::IsValidSid(psid)); + + if (!::ConvertSidToStringSidW(psid, &pwz)) + { + AclExitWithLastError(hr, "Failed to convert SID to string for Account: %ls", wzAccount); + } + + hr = StrAllocString(psczSid, pwz, 0); + } + else + { + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr) + { + HRESULT hrLength = ::StringCchLengthW(wzAccount, STRSAFE_MAX_CCH, reinterpret_cast(&cchAccount)); + AclExitOnFailure(hrLength, "Failed to get the length of the account name."); + + if (11 < cchAccount && CSTR_EQUAL == CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"NT SERVICE\\", 11, wzAccount, 11)) + { + // If the service is not installed then LookupAccountName doesn't resolve the SID, but we can calculate it. + LPCWSTR wzServiceName = &wzAccount[11]; + hr = AclCalculateServiceSidString(wzServiceName, cchAccount - 11, &sczSid); + AclExitOnFailure(hr, "Failed to calculate the service SID for %ls", wzServiceName); + + *psczSid = sczSid; + sczSid = NULL; + } + } + AclExitOnFailure(hr, "Failed to get SID for account: %ls", wzAccount); + } + +LExit: + ReleaseStr(sczSid); + if (pwz) + { + ::LocalFree(pwz); + } + if (psid) + { + AclFreeSid(psid); + } + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/aclutil.cpp b/src/libs/dutil/WixToolset.DUtil/aclutil.cpp new file mode 100644 index 00000000..c9733033 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/aclutil.cpp @@ -0,0 +1,1044 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define AclExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__) +#define AclExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) +#define AclExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) +#define AclExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__) +#define AclExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__) +#define AclExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ACLUTIL, e, x, s, __VA_ARGS__) +#define AclExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ACLUTIL, g, x, s, __VA_ARGS__) + +/******************************************************************** +AclCheckAccess - determines if token has appropriate privileges + +NOTE: paa->fDenyAccess and paa->dwAccessMask are ignored and must be zero +if hToken is NULL, the thread will be checked +if hToken is not NULL the token must be an impersonation token +********************************************************************/ +extern "C" HRESULT DAPI AclCheckAccess( + __in HANDLE hToken, + __in ACL_ACCESS* paa + ) +{ + HRESULT hr = S_OK; + PSID psid = NULL; + BOOL fIsMember = FALSE; + + AclExitOnNull(paa, hr, E_INVALIDARG, "Failed to check ACL access, because no acl access provided to check"); + Assert(0 == paa->fDenyAccess && 0 == paa->dwAccessMask); + + if (paa->pwzAccountName) + { + hr = AclGetAccountSid(NULL, paa->pwzAccountName, &psid); + AclExitOnFailure(hr, "failed to get SID for account: %ls", paa->pwzAccountName); + } + else + { + if (!::AllocateAndInitializeSid(&paa->sia, paa->nSubAuthorityCount, paa->nSubAuthority[0], paa->nSubAuthority[1], paa->nSubAuthority[2], paa->nSubAuthority[3], paa->nSubAuthority[4], paa->nSubAuthority[5], paa->nSubAuthority[6], paa->nSubAuthority[7], &psid)) + { + AclExitWithLastError(hr, "failed to initialize SID"); + } + } + + if (!::CheckTokenMembership(hToken, psid, &fIsMember)) + { + AclExitWithLastError(hr, "failed to check membership"); + } + + fIsMember ? hr = S_OK : hr = S_FALSE; + +LExit: + if (psid) + { + ::FreeSid(psid); // TODO: does this have bad behavior if SID was allocated by Heap from AclGetAccountSid? + } + + return hr; +} + + +/******************************************************************** +AclCheckAdministratorAccess - determines if token has Administrator privileges + +NOTE: if hToken is NULL, the thread will be checked +if hToken is not NULL the token must be an impersonation token +********************************************************************/ +extern "C" HRESULT DAPI AclCheckAdministratorAccess( + __in HANDLE hToken + ) +{ + ACL_ACCESS aa; + SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; + + memset(&aa, 0, sizeof(aa)); + aa.sia = siaNt; + aa.nSubAuthorityCount = 2; + aa.nSubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID; + aa.nSubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS; + + return AclCheckAccess(hToken, &aa); +} + + +/******************************************************************** +AclCheckLocalSystemAccess - determines if token has LocalSystem privileges + +NOTE: if hToken is NULL, the thread will be checked +if hToken is not NULL the token must be an impersonation token +********************************************************************/ +extern "C" HRESULT DAPI AclCheckLocalSystemAccess( + __in HANDLE hToken + ) +{ + ACL_ACCESS aa; + SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; + + memset(&aa, 0, sizeof(aa)); + aa.sia = siaNt; + aa.nSubAuthorityCount = 1; + aa.nSubAuthority[0] = SECURITY_LOCAL_SYSTEM_RID; + + return AclCheckAccess(hToken, &aa); +} + + +/******************************************************************** +AclGetWellKnownSid - returns a SID for the specified account + +********************************************************************/ +extern "C" HRESULT DAPI AclGetWellKnownSid( + __in WELL_KNOWN_SID_TYPE wkst, + __deref_out PSID* ppsid + ) +{ + Assert(ppsid); + + HRESULT hr = S_OK;; + PSID psid = NULL; + DWORD cbSid = SECURITY_MAX_SID_SIZE; + + PSID psidTemp = NULL; +#if(_WIN32_WINNT < 0x0501) + SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY; + SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY; + SID_IDENTIFIER_AUTHORITY siaCreator = SECURITY_CREATOR_SID_AUTHORITY; + BOOL fSuccess = FALSE; +#endif + + // + // allocate memory for the SID and get it + // + psid = static_cast(MemAlloc(cbSid, TRUE)); + AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed allocate memory for well known SID"); + +#if(_WIN32_WINNT < 0x0501) + switch (wkst) + { + case WinWorldSid: // Everyone + fSuccess = ::AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinAuthenticatedUserSid: // Authenticated Users + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_AUTHENTICATED_USER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinLocalSystemSid: // LocalSystem + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinLocalServiceSid: // LocalService + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinNetworkServiceSid: // NetworkService + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_NETWORK_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinBuiltinGuestsSid: // Guests + fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinBuiltinAdministratorsSid: // Administrators + fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinBuiltinUsersSid: // Users + fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinCreatorOwnerSid: //CREATOR OWNER + fSuccess = ::AllocateAndInitializeSid(&siaCreator, 1, SECURITY_CREATOR_OWNER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + case WinInteractiveSid: // INTERACTIVE + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_INTERACTIVE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp); + break; + default: + hr = E_INVALIDARG; + AclExitOnFailure(hr, "unknown well known SID: %d", wkst); + } + + if (!fSuccess) + AclExitOnLastError(hr, "failed to allocate well known SID: %d", wkst); + + if (!::CopySid(cbSid, psid, psidTemp)) + AclExitOnLastError(hr, "failed to create well known SID: %d", wkst); +#else + Assert(NULL == psidTemp); + if (!::CreateWellKnownSid(wkst, NULL, psid, &cbSid)) + { + AclExitWithLastError(hr, "failed to create well known SID: %d", wkst); + } +#endif + + *ppsid = psid; + psid = NULL; // null it here so it won't be released below + + Assert(S_OK == hr && ::IsValidSid(*ppsid)); +LExit: + if (psidTemp) + { + ::FreeSid(psidTemp); + } + + ReleaseMem(psid); + + return hr; +} + + +/******************************************************************** +AclGetAccountSid - returns a SID for the specified account + +********************************************************************/ +extern "C" HRESULT DAPI AclGetAccountSid( + __in_opt LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out PSID* ppsid + ) +{ + Assert(wzAccount && *wzAccount && ppsid); + + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + PSID psid = NULL; + DWORD cbSid = SECURITY_MAX_SID_SIZE; + LPWSTR pwzDomainName = NULL; + DWORD cbDomainName = 255; + SID_NAME_USE peUse; + + // + // allocate memory for the SID and domain name + // + psid = static_cast(MemAlloc(cbSid, TRUE)); + AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed to allocate memory for SID"); + hr = StrAlloc(&pwzDomainName, cbDomainName); + AclExitOnFailure(hr, "failed to allocate string for domain name"); + + // + // try to lookup the account now + // + if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse)) + { + // if one of the buffers wasn't large enough + er = ::GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == er) + { + if (SECURITY_MAX_SID_SIZE < cbSid) + { + PSID psidNew = static_cast(MemReAlloc(psid, cbSid, TRUE)); + AclExitOnNullWithLastError(psidNew, hr, "failed to allocate memory for account: %ls", wzAccount); + + psid = psidNew; + } + if (255 < cbDomainName) + { + hr = StrAlloc(&pwzDomainName, cbDomainName); + AclExitOnFailure(hr, "failed to allocate string for domain name"); + } + + if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse)) + { + AclExitWithLastError(hr, "failed to lookup account: %ls", wzAccount); + } + } + else + { + AclExitOnWin32Error(er, hr, "failed to lookup account: %ls", wzAccount); + } + } + + *ppsid = psid; + psid = NULL; + + hr = S_OK; +LExit: + ReleaseStr(pwzDomainName); + ReleaseMem(psid); + + return hr; +} + + +/******************************************************************** +AclGetAccountSidString - gets a string version of the user's SID + +NOTE: ppwzSid should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI AclGetAccountSidString( + __in_z LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out_z LPWSTR* ppwzSid + ) +{ + Assert(ppwzSid); + HRESULT hr = S_OK; + PSID psid = NULL; + LPWSTR pwz = NULL; + + *ppwzSid = NULL; + + hr = AclGetAccountSid(wzSystem, wzAccount, &psid); + AclExitOnFailure(hr, "failed to get SID for account: %ls", wzAccount); + Assert(::IsValidSid(psid)); + + if (!::ConvertSidToStringSidW(psid, &pwz)) + { + AclExitWithLastError(hr, "failed to convert SID to string for Account: %ls", wzAccount); + } + + hr = StrAllocString(ppwzSid, pwz, 0); + +LExit: + if (FAILED(hr)) + { + ReleaseNullStr(*ppwzSid); + } + + if (pwz) + { + ::LocalFree(pwz); + } + + if (psid) + { + AclFreeSid(psid); + } + + return hr; +} + + +/******************************************************************** +AclCreateDacl - creates a DACL from ACL_ACE structures + +********************************************************************/ +extern "C" HRESULT DAPI AclCreateDacl( + __in_ecount(cDeny) ACL_ACE rgaaDeny[], + __in DWORD cDeny, + __in_ecount(cAllow) ACL_ACE rgaaAllow[], + __in DWORD cAllow, + __deref_out ACL** ppAcl + ) +{ + Assert(ppAcl); + HRESULT hr = S_OK; + ACL* pAcl = NULL; + DWORD cbAcl = 0; + DWORD i; + + *ppAcl = NULL; + + // initialize the ACL + cbAcl = sizeof(ACL); + for (i = 0; i < cDeny; ++i) + { + cbAcl += sizeof(ACCESS_DENIED_ACE) + ::GetLengthSid(rgaaDeny[i].psid) - sizeof(DWORD); + } + + for (i = 0; i < cAllow; ++i) + { + cbAcl += sizeof(ACCESS_ALLOWED_ACE) + ::GetLengthSid(rgaaAllow[i].psid) - sizeof(DWORD); + } + + pAcl = static_cast(MemAlloc(cbAcl, TRUE)); + AclExitOnNull(pAcl, hr, E_OUTOFMEMORY, "failed to allocate ACL"); + +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::InitializeAcl(pAcl, cbAcl, ACL_REVISION)) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to initialize ACL"); + } + + // add in the ACEs (denied first) + for (i = 0; i < cDeny; ++i) + { +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::AddAccessDeniedAceEx(pAcl, ACL_REVISION, rgaaDeny[i].dwFlags, rgaaDeny[i].dwMask, rgaaDeny[i].psid)) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to add access denied ACE #%d to ACL", i); + } + } + for (i = 0; i < cAllow; ++i) + { +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::AddAccessAllowedAceEx(pAcl, ACL_REVISION, rgaaAllow[i].dwFlags, rgaaAllow[i].dwMask, rgaaAllow[i].psid)) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to add access allowed ACE #%d to ACL", i); + } + } + + *ppAcl = pAcl; + pAcl = NULL; + AssertSz(::IsValidAcl(*ppAcl), "AclCreateDacl() - created invalid ACL"); + Assert(S_OK == hr); +LExit: + if (pAcl) + { + AclFreeDacl(pAcl); + } + + return hr; +} + + +/******************************************************************** +AclAddToDacl - creates a new DACL from an ACL plus new ACL_ACE structure + +********************************************************************/ +extern "C" HRESULT DAPI AclAddToDacl( + __in ACL* pAcl, + __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[], + __in DWORD cDeny, + __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[], + __in DWORD cAllow, + __deref_out ACL** ppAclNew + ) +{ + Assert(pAcl && ::IsValidAcl(pAcl) && ppAclNew); + HRESULT hr = S_OK; + + ACL_SIZE_INFORMATION asi; + ACL_ACE* paaNewDeny = NULL; + DWORD cNewDeny = 0; + ACL_ACE* paaNewAllow = NULL; + DWORD cNewAllow = 0; + + ACCESS_ALLOWED_ACE* paaa; + ACCESS_DENIED_ACE* pada; + DWORD i; + + // allocate memory for all the new ACEs (NOTE: this over calculates the memory necessary, but that's okay) + if (!::GetAclInformation(pAcl, &asi, sizeof(asi), AclSizeInformation)) + { + AclExitWithLastError(hr, "failed to get information about original ACL"); + } + + if ((asi.AceCount + cDeny) < asi.AceCount || // check for overflow + (asi.AceCount + cDeny) < cDeny || // check for overflow + (asi.AceCount + cDeny) >= MAXSIZE_T / sizeof(ACL_ACE)) + { + hr = E_OUTOFMEMORY; + AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cDeny)); + } + + paaNewDeny = static_cast(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cDeny), TRUE)); + AclExitOnNull(paaNewDeny, hr, E_OUTOFMEMORY, "failed to allocate memory for new deny ACEs"); + + if ((asi.AceCount + cAllow) < asi.AceCount || // check for overflow + (asi.AceCount + cAllow) < cAllow || // check for overflow + (asi.AceCount + cAllow) >= MAXSIZE_T / sizeof(ACL_ACE)) + { + hr = E_OUTOFMEMORY; + AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cAllow)); + } + + paaNewAllow = static_cast(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cAllow), TRUE)); + AclExitOnNull(paaNewAllow, hr, E_OUTOFMEMORY, "failed to allocate memory for new allow ACEs"); + + // fill in the new structures with old data then new data (denied first) + for (i = 0; i < asi.AceCount; ++i) + { + if (!::GetAce(pAcl, i, reinterpret_cast(&pada))) + { + AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i); + } + + if (ACCESS_DENIED_ACE_TYPE != pada->Header.AceType) + { + continue; // skip non-denied aces + } + + paaNewDeny[i].dwFlags = pada->Header.AceFlags; + paaNewDeny[i].dwMask = pada->Mask; + paaNewDeny[i].psid = reinterpret_cast(&(pada->SidStart)); + ++cNewDeny; + } + + memcpy(paaNewDeny + cNewDeny, rgaaDeny, sizeof(ACL_ACE) * cDeny); + cNewDeny += cDeny; + + + for (i = 0; i < asi.AceCount; ++i) + { + if (!::GetAce(pAcl, i, reinterpret_cast(&paaa))) + { + AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i); + } + + if (ACCESS_ALLOWED_ACE_TYPE != paaa->Header.AceType) + { + continue; // skip non-allowed aces + } + + paaNewAllow[i].dwFlags = paaa->Header.AceFlags; + paaNewAllow[i].dwMask = paaa->Mask; + paaNewAllow[i].psid = reinterpret_cast(&(paaa->SidStart)); + ++cNewAllow; + } + + memcpy(paaNewAllow + cNewAllow, rgaaAllow, sizeof(ACL_ACE) * cAllow); + cNewAllow += cAllow; + + // create the dacl with the new + hr = AclCreateDacl(paaNewDeny, cNewDeny, paaNewAllow, cNewAllow, ppAclNew); + AclExitOnFailure(hr, "failed to create new ACL from existing ACL"); + + AssertSz(::IsValidAcl(*ppAclNew), "AclAddToDacl() - created invalid ACL"); + Assert(S_OK == hr); +LExit: + ReleaseMem(paaNewAllow); + ReleaseMem(paaNewDeny); + + return hr; +} + + +/******************************************************************** +AclMergeDacls - creates a new DACL from two existing ACLs + +********************************************************************/ +extern "C" HRESULT DAPI AclMergeDacls( + __in const ACL* pAcl1, + __in const ACL* pAcl2, + __deref_out ACL** ppAclNew + ) +{ + HRESULT hr = E_NOTIMPL; + + Assert(pAcl1 && pAcl2 && ppAclNew); + UNREFERENCED_PARAMETER(pAcl1); + UNREFERENCED_PARAMETER(pAcl2); + UNREFERENCED_PARAMETER(ppAclNew); + +//LExit: + return hr; +} + + +/******************************************************************** +AclCreateDaclOld - creates a DACL from an ACL_ACCESS structure + +********************************************************************/ +extern "C" HRESULT DAPI AclCreateDaclOld( + __in_ecount(cAclAccesses) ACL_ACCESS* paa, + __in DWORD cAclAccesses, + __deref_out ACL** ppACL + ) +{ + Assert(ppACL); + HRESULT hr = S_OK; + DWORD* pdwAccessMask = NULL; + PSID* ppsid = NULL; + + DWORD i; + int cbAcl; + + *ppACL = NULL; + + // + // create the SIDs and calculate the space for the ACL + // + pdwAccessMask = static_cast(MemAlloc(sizeof(DWORD) * cAclAccesses, TRUE)); + AclExitOnNull(pdwAccessMask, hr, E_OUTOFMEMORY, "failed allocate memory for access mask"); + ppsid = static_cast(MemAlloc(sizeof(PSID) * cAclAccesses, TRUE)); + AclExitOnNull(ppsid, hr, E_OUTOFMEMORY, "failed allocate memory for sid"); + + cbAcl = sizeof (ACL); // start with the size of the header + for (i = 0; i < cAclAccesses; ++i) + { + if (paa[i].pwzAccountName) + { + hr = AclGetAccountSid(NULL, paa[i].pwzAccountName, ppsid + i); + AclExitOnFailure(hr, "failed to get SID for account: %ls", paa[i].pwzAccountName); + } + else + { + if ((!::AllocateAndInitializeSid(&paa[i].sia, paa[i].nSubAuthorityCount, + paa[i].nSubAuthority[0], paa[i].nSubAuthority[1], + paa[i].nSubAuthority[2], paa[i].nSubAuthority[3], + paa[i].nSubAuthority[4], paa[i].nSubAuthority[5], + paa[i].nSubAuthority[6], paa[i].nSubAuthority[7], + (void**)(ppsid + i)))) + { + AclExitWithLastError(hr, "failed to initialize SIDs #%u", i); + } + } + + // add the newly allocated SID size to the count of bytes for this ACL + cbAcl +=::GetLengthSid(*(ppsid + i)) - sizeof(DWORD); + if (paa[i].fDenyAccess) + { + cbAcl += sizeof(ACCESS_DENIED_ACE); + } + else + { + cbAcl += sizeof(ACCESS_ALLOWED_ACE); + } + + pdwAccessMask[i] = paa[i].dwAccessMask; + } + + // + // allocate the ACL and set the appropriate ACEs + // + *ppACL = static_cast(MemAlloc(cbAcl, FALSE)); + AclExitOnNull(*ppACL, hr, E_OUTOFMEMORY, "failed allocate memory for ACL"); + +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::InitializeAcl(*ppACL, cbAcl, ACL_REVISION)) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to initialize ACLs"); + } + + // add an access-allowed ACE for each of the SIDs + for (i = 0; i < cAclAccesses; ++i) + { + if (paa[i].fDenyAccess) + { +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::AddAccessDeniedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i))) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to add access denied for ACE"); + } + } + else + { +#pragma prefast(push) +#pragma prefast(disable:25029) + if (!::AddAccessAllowedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i))) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to add access allowed for ACE"); + } + } + } + +LExit: + if (FAILED(hr)) + { + ReleaseNullMem(*ppACL); + } + + if (ppsid) + { + for (i = 0; i < cAclAccesses; ++i) + { + if (ppsid[i]) + { + ::FreeSid(ppsid[i]); + } + } + + MemFree(ppsid); + } + + ReleaseMem(pdwAccessMask); + + return hr; +} + + +/******************************************************************** +AclCreateSecurityDescriptorFromDacl - creates a self-relative security +descriptor from an existing DACL + +********************************************************************/ +extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromDacl( + __in ACL* pACL, + __deref_out SECURITY_DESCRIPTOR** ppsd + ) +{ + HRESULT hr = S_OK; + + SECURITY_DESCRIPTOR sd; + DWORD cbSD; + + AclExitOnNull(pACL, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no DACL was provided"); + AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no output object was provided"); + + *ppsd = NULL; + + // + // create the absolute security descriptor + // + + // initialize our security descriptor, throw the ACL into it, and set the owner +#pragma prefast(push) +#pragma prefast(disable:25028) // We only call this when pACL isn't NULL, so this call is safe according to the docs +#pragma prefast(disable:25029) + if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) || + (!::SetSecurityDescriptorDacl(&sd, TRUE, pACL, FALSE)) || + (!::SetSecurityDescriptorOwner(&sd, NULL, FALSE))) +#pragma prefast(pop) + { + AclExitWithLastError(hr, "failed to initialize security descriptor"); + } + + // + // create the self-relative security descriptor + // + cbSD = ::GetSecurityDescriptorLength(&sd); + *ppsd = static_cast(MemAlloc(cbSD, FALSE)); + AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); + + ::MakeSelfRelativeSD(&sd, (BYTE*)*ppsd, &cbSD); + Assert(::IsValidSecurityDescriptor(*ppsd)); + +LExit: + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) + { + MemFree(*ppsd); + *ppsd = NULL; + } + + return hr; +} + + +/******************************************************************** +AclCreateSecurityDescriptor - creates a self-relative security descriptor from an +ACL_ACCESS structure + +NOTE: ppsd should be freed with AclFreeSecurityDescriptor() +********************************************************************/ +extern "C" HRESULT DAPI AclCreateSecurityDescriptor( + __in_ecount(cAclAccesses) ACL_ACCESS* paa, + __in DWORD cAclAccesses, + __deref_out SECURITY_DESCRIPTOR** ppsd + ) +{ + Assert(ppsd); + HRESULT hr = S_OK; + + ACL* pACL; + + *ppsd = NULL; + + // + // create the DACL + // + hr = AclCreateDaclOld(paa, cAclAccesses, &pACL); + AclExitOnFailure(hr, "failed to create DACL for security descriptor"); + + // + // create self-relative security descriptor + // + hr = AclCreateSecurityDescriptorFromDacl(pACL, ppsd); + +LExit: + return hr; +} + + +/******************************************************************** +AclCreateSecurityDescriptorFromString - creates a self-relative security +descriptor from an SDDL string + +NOTE: ppsd should be freed with AclFreeSecurityDescriptor() +********************************************************************/ +extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromString( + __deref_out SECURITY_DESCRIPTOR** ppsd, + __in_z __format_string LPCWSTR wzSddlFormat, + ... + ) +{ + Assert(ppsd); + HRESULT hr = S_OK; + LPWSTR pwzSddl = NULL; + va_list args; + PSECURITY_DESCRIPTOR psd = NULL; + DWORD cbSD = 0; + + *ppsd = NULL; + + va_start(args, wzSddlFormat); + hr = StrAllocFormattedArgs(&pwzSddl, wzSddlFormat, args); + va_end(args); + AclExitOnFailure(hr, "failed to create SDDL string for format: %ls", wzSddlFormat); + + if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSddl, SDDL_REVISION_1, &psd, &cbSD)) + { + AclExitWithLastError(hr, "failed to create security descriptor from SDDL: %ls", pwzSddl); + } + + *ppsd = static_cast(MemAlloc(cbSD, FALSE)); + AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed to allocate memory for security descriptor"); + + memcpy(*ppsd, psd, cbSD); + Assert(::IsValidSecurityDescriptor(*ppsd)); + + Assert(S_OK == hr); + +LExit: + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) + { + MemFree(*ppsd); + *ppsd = NULL; + } + + if (psd) + { + ::LocalFree(psd); + } + + ReleaseStr(pwzSddl); + return hr; +} + + +/******************************************************************** +AclDuplicateSecurityDescriptor - creates a copy of a self-relative security descriptor + +NOTE: passed in security descriptor must be in self-relative format +********************************************************************/ +extern "C" HRESULT DAPI AclDuplicateSecurityDescriptor( + __in SECURITY_DESCRIPTOR* psd, + __deref_out SECURITY_DESCRIPTOR** ppsd + ) +{ + HRESULT hr = S_OK; + DWORD cbSD; + + AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get duplicate ACL security descriptor because no place to output was provided"); + *ppsd = NULL; + + // + // create the self-relative security descriptor + // + cbSD = ::GetSecurityDescriptorLength(psd); + *ppsd = static_cast(MemAlloc(cbSD, 0)); + AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); + + memcpy(*ppsd, psd, cbSD); + Assert(::IsValidSecurityDescriptor(*ppsd)); + +LExit: + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) + { + MemFree(*ppsd); + *ppsd = NULL; + } + + return hr; +} + + +/******************************************************************** +AclGetSecurityDescriptor - returns self-relative security descriptor for named object + +NOTE: free ppsd with AclFreeSecurityDescriptor() +********************************************************************/ +extern "C" HRESULT DAPI AclGetSecurityDescriptor( + __in_z LPCWSTR wzObject, + __in SE_OBJECT_TYPE sot, + __in SECURITY_INFORMATION securityInformation, + __deref_out SECURITY_DESCRIPTOR** ppsd + ) +{ + HRESULT hr = S_OK; + DWORD er; + PSECURITY_DESCRIPTOR psd = NULL; + DWORD cbSD; + + AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get ACL Security Descriptor because no place to output was provided"); + *ppsd = NULL; + + // get the security descriptor for the object + er = ::GetNamedSecurityInfoW(const_cast(wzObject), sot, securityInformation, NULL, NULL, NULL, NULL, &psd); + AclExitOnWin32Error(er, hr, "failed to get security info from object: %ls", wzObject); + Assert(::IsValidSecurityDescriptor(psd)); + + // copy the self-relative security descriptor + cbSD = ::GetSecurityDescriptorLength(psd); + *ppsd = static_cast(MemAlloc(cbSD, 0)); + AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor"); + + memcpy(*ppsd, psd, cbSD); + Assert(::IsValidSecurityDescriptor(*ppsd)); + +LExit: + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd) + { + MemFree(*ppsd); + *ppsd = NULL; + } + + if (psd) + { + ::LocalFree(psd); + } + + return hr; +} + + +extern "C" HRESULT DAPI AclSetSecurityWithRetry( + __in_z LPCWSTR wzObject, + __in SE_OBJECT_TYPE sot, + __in SECURITY_INFORMATION securityInformation, + __in_opt PSID psidOwner, + __in_opt PSID psidGroup, + __in_opt PACL pDacl, + __in_opt PACL pSacl, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ) +{ + HRESULT hr = S_OK; + LPWSTR sczObject = NULL; + DWORD i = 0; + + hr = StrAllocString(&sczObject, wzObject, 0); + AclExitOnFailure(hr, "Failed to copy object to secure."); + + hr = E_FAIL; + for (i = 0; FAILED(hr) && i <= cRetry; ++i) + { + if (0 < i) + { + ::Sleep(dwWaitMilliseconds); + } + + DWORD er = ::SetNamedSecurityInfoW(sczObject, sot, securityInformation, psidOwner, psidGroup, pDacl, pSacl); + hr = HRESULT_FROM_WIN32(er); + } + AclExitOnRootFailure(hr, "Failed to set security on object '%ls' after %u retries.", wzObject, i); + +LExit: + ReleaseStr(sczObject); + + return hr; +} + + +/******************************************************************** +AclFreeSid - frees a SID created by any Acl* functions + +********************************************************************/ +extern "C" HRESULT DAPI AclFreeSid( + __in PSID psid + ) +{ + Assert(psid && ::IsValidSid(psid)); + HRESULT hr = S_OK; + + hr = MemFree(psid); + + return hr; +} + + +/******************************************************************** +AclFreeDacl - frees a DACL created by any Acl* functions + +********************************************************************/ +extern "C" HRESULT DAPI AclFreeDacl( + __in ACL* pACL + ) +{ + Assert(pACL); + HRESULT hr = S_OK; + + hr = MemFree(pACL); + + return hr; +} + + +/******************************************************************** +AclFreeSecurityDescriptor - frees a security descriptor created by any Acl* functions + +********************************************************************/ +extern "C" HRESULT DAPI AclFreeSecurityDescriptor( + __in SECURITY_DESCRIPTOR* psd + ) +{ + Assert(psd && ::IsValidSecurityDescriptor(psd)); + HRESULT hr = S_OK; + + hr = MemFree(psd); + + return hr; +} + + +/******************************************************************** +AclAddAdminToSecurityDescriptor - Adds the Administrators group to a security descriptor + +********************************************************************/ +extern "C" HRESULT DAPI AclAddAdminToSecurityDescriptor( + __in SECURITY_DESCRIPTOR* pSecurity, + __deref_out SECURITY_DESCRIPTOR** ppSecurityNew + ) +{ + HRESULT hr = S_OK; + PACL pAcl = NULL; + PACL pAclNew = NULL; + BOOL fValid, fDaclDefaulted; + ACL_ACE ace[1]; + SECURITY_DESCRIPTOR* pSecurityNew; + + if (!::GetSecurityDescriptorDacl(pSecurity, &fValid, &pAcl, &fDaclDefaulted) || !fValid) + { + AclExitOnLastError(hr, "Failed to get acl from security descriptor"); + } + + hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &ace[0].psid); + AclExitOnFailure(hr, "failed to get sid for Administrators group"); + + ace[0].dwFlags = NO_PROPAGATE_INHERIT_ACE; + ace[0].dwMask = GENERIC_ALL; + + hr = AclAddToDacl(pAcl, NULL, 0, ace, 1, &pAclNew); + AclExitOnFailure(hr, "failed to add Administrators ACE to ACL"); + + hr = AclCreateSecurityDescriptorFromDacl(pAclNew, &pSecurityNew); + AclExitOnLastError(hr, "Failed to create new security descriptor"); + + // The DACL is referenced by, not copied into, the security descriptor. Make sure not to free it. + pAclNew = NULL; + + *ppSecurityNew = pSecurityNew; + +LExit: + if (pAclNew) + { + AclFreeDacl(pAclNew); + } + if (ace[0].psid) + { + AclFreeSid(ace[0].psid); + } + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/apputil.cpp b/src/libs/dutil/WixToolset.DUtil/apputil.cpp new file mode 100644 index 00000000..589a09dd --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/apputil.cpp @@ -0,0 +1,124 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define AppExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__) +#define AppExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__) +#define AppExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__) +#define AppExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__) +#define AppExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__) +#define AppExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_APPUTIL, e, x, s, __VA_ARGS__) +#define AppExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_APPUTIL, g, x, s, __VA_ARGS__) + +const DWORD PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; +typedef BOOL(WINAPI *LPFN_SETDEFAULTDLLDIRECTORIES)(DWORD); +typedef BOOL(WINAPI *LPFN_SETDLLDIRECTORYW)(LPCWSTR); + +extern "C" void DAPI AppFreeCommandLineArgs( + __in LPWSTR* argv + ) +{ + // The "ignored" hack in AppParseCommandLine requires an adjustment. + LPWSTR* argvOriginal = argv - 1; + ::LocalFree(argvOriginal); +} + +/******************************************************************** +AppInitialize - initializes the standard safety precautions for an + installation application. + +********************************************************************/ +extern "C" void DAPI AppInitialize( + __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[], + __in DWORD cSafelyLoadSystemDlls + ) +{ + HRESULT hr = S_OK; + HMODULE hIgnored = NULL; + BOOL fSetDefaultDllDirectories = FALSE; + + ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); + + // Best effort call to initialize default DLL directories to system only. + HMODULE hKernel32 = ::GetModuleHandleW(L"kernel32"); + Assert(hKernel32); + LPFN_SETDEFAULTDLLDIRECTORIES pfnSetDefaultDllDirectories = (LPFN_SETDEFAULTDLLDIRECTORIES)::GetProcAddress(hKernel32, "SetDefaultDllDirectories"); + if (pfnSetDefaultDllDirectories) + { + if (pfnSetDefaultDllDirectories(PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32)) + { + fSetDefaultDllDirectories = TRUE; + } + else + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to call SetDefaultDllDirectories."); + } + } + + // Only need to safely load if the default DLL directories was not + // able to be set. + if (!fSetDefaultDllDirectories) + { + // Remove current working directory from search order. + LPFN_SETDLLDIRECTORYW pfnSetDllDirectory = (LPFN_SETDLLDIRECTORYW)::GetProcAddress(hKernel32, "SetDllDirectoryW"); + if (!pfnSetDllDirectory || !pfnSetDllDirectory(L"")) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to call SetDllDirectory."); + } + + for (DWORD i = 0; i < cSafelyLoadSystemDlls; ++i) + { + hr = LoadSystemLibrary(rgsczSafelyLoadSystemDlls[i], &hIgnored); + if (FAILED(hr)) + { + TraceError(hr, "Failed to safety load: %ls", rgsczSafelyLoadSystemDlls[i]); + } + } + } +} + +extern "C" void DAPI AppInitializeUnsafe() +{ + ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); +} + +extern "C" DAPI_(HRESULT) AppParseCommandLine( + __in LPCWSTR wzCommandLine, + __in int* pArgc, + __in LPWSTR** pArgv + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCommandLine = NULL; + LPWSTR* argv = NULL; + int argc = 0; + + // CommandLineToArgvW tries to treat the first argument as the path to the process, + // which fails pretty miserably if your first argument is something like + // FOO="C:\Program Files\My Company". So give it something harmless to play with. + hr = StrAllocConcat(&sczCommandLine, L"ignored ", 0); + AppExitOnFailure(hr, "Failed to initialize command line."); + + hr = StrAllocConcat(&sczCommandLine, wzCommandLine, 0); + AppExitOnFailure(hr, "Failed to copy command line."); + + argv = ::CommandLineToArgvW(sczCommandLine, &argc); + AppExitOnNullWithLastError(argv, hr, "Failed to parse command line."); + + // Skip "ignored" argument/hack. + *pArgv = argv + 1; + *pArgc = argc - 1; + +LExit: + ReleaseStr(sczCommandLine); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/apuputil.cpp b/src/libs/dutil/WixToolset.DUtil/apuputil.cpp new file mode 100644 index 00000000..eb96d515 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/apuputil.cpp @@ -0,0 +1,700 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define ApupExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__) +#define ApupExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_APUPUTIL, p, x, e, s, __VA_ARGS__) +#define ApupExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, p, x, s, __VA_ARGS__) +#define ApupExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_APUPUTIL, p, x, e, s, __VA_ARGS__) +#define ApupExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, p, x, s, __VA_ARGS__) +#define ApupExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_APUPUTIL, e, x, s, __VA_ARGS__) +#define ApupExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_APUPUTIL, g, x, s, __VA_ARGS__) + +// prototypes +static HRESULT ProcessEntry( + __in ATOM_ENTRY* pAtomEntry, + __in LPCWSTR wzDefaultAppId, + __inout APPLICATION_UPDATE_ENTRY* pApupEntry + ); +static HRESULT ParseEnclosure( + __in ATOM_LINK* pLink, + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure + ); +static __callback int __cdecl CompareEntries( + void* pvContext, + const void* pvLeft, + const void* pvRight + ); +static HRESULT FilterEntries( + __in APPLICATION_UPDATE_ENTRY* rgEntries, + __in DWORD cEntries, + __in VERUTIL_VERSION* pCurrentVersion, + __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries, + __inout DWORD* pcFilteredEntries + ); +static HRESULT CopyEntry( + __in const APPLICATION_UPDATE_ENTRY* pSrc, + __in APPLICATION_UPDATE_ENTRY* pDest + ); +static HRESULT CopyEnclosure( + __in const APPLICATION_UPDATE_ENCLOSURE* pSrc, + __in APPLICATION_UPDATE_ENCLOSURE* pDest + ); +static void FreeEntry( + __in APPLICATION_UPDATE_ENTRY* pApupEntry + ); +static void FreeEnclosure( + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure + ); + + +// +// ApupCalculateChainFromAtom - returns the chain of application updates found in an ATOM feed. +// +extern "C" HRESULT DAPI ApupAllocChainFromAtom( + __in ATOM_FEED* pFeed, + __out APPLICATION_UPDATE_CHAIN** ppChain + ) +{ + HRESULT hr = S_OK; + APPLICATION_UPDATE_CHAIN* pChain = NULL; + + pChain = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE)); + + // First search the ATOM feed's custom elements to try and find the default application identity. + for (ATOM_UNKNOWN_ELEMENT* pElement = pFeed->pUnknownElements; pElement; pElement = pElement->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1)) + { + hr = StrAllocString(&pChain->wzDefaultApplicationId, pElement->wzValue, 0); + ApupExitOnFailure(hr, "Failed to allocate default application id."); + + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1)) + { + hr = StrAllocString(&pChain->wzDefaultApplicationType, pAttribute->wzValue, 0); + ApupExitOnFailure(hr, "Failed to allocate default application type."); + } + } + } + } + } + + // Assume there will be as many application updates entries as their are feed entries. + if (pFeed->cEntries) + { + pChain->rgEntries = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_ENTRY) * pFeed->cEntries, TRUE)); + ApupExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to allocate memory for update entries."); + + // Process each entry, building up the chain. + for (DWORD i = 0; i < pFeed->cEntries; ++i) + { + hr = ProcessEntry(pFeed->rgEntries + i, pChain->wzDefaultApplicationId, pChain->rgEntries + pChain->cEntries); + ApupExitOnFailure(hr, "Failed to process ATOM entry."); + + if (S_FALSE != hr) + { + ++pChain->cEntries; + } + } + + // Sort the chain by descending version and ascending total size. + qsort_s(pChain->rgEntries, pChain->cEntries, sizeof(APPLICATION_UPDATE_ENTRY), CompareEntries, NULL); + } + + // Trim the unused entries from the end, if any of the entries failed to parse or validate + if (pChain->cEntries != pFeed->cEntries) + { + if (pChain->cEntries > 0) + { + pChain->rgEntries = static_cast(MemReAlloc(pChain->rgEntries, sizeof(APPLICATION_UPDATE_ENTRY) * pChain->cEntries, FALSE)); + ApupExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to reallocate memory for update entries."); + } + else + { + ReleaseNullMem(pChain->rgEntries); + } + } + + *ppChain = pChain; + pChain = NULL; + +LExit: + ReleaseApupChain(pChain); + + return hr; +} + + +// +// ApupFilterChain - remove the unneeded update elements from the chain. +// +HRESULT DAPI ApupFilterChain( + __in APPLICATION_UPDATE_CHAIN* pChain, + __in VERUTIL_VERSION* pVersion, + __out APPLICATION_UPDATE_CHAIN** ppFilteredChain + ) +{ + HRESULT hr = S_OK; + APPLICATION_UPDATE_CHAIN* pNewChain = NULL; + APPLICATION_UPDATE_ENTRY* prgEntries = NULL; + DWORD cEntries = NULL; + + pNewChain = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE)); + ApupExitOnNull(pNewChain, hr, E_OUTOFMEMORY, "Failed to allocate filtered chain."); + + hr = FilterEntries(pChain->rgEntries, pChain->cEntries, pVersion, &prgEntries, &cEntries); + ApupExitOnFailure(hr, "Failed to filter entries by version."); + + if (pChain->wzDefaultApplicationId) + { + hr = StrAllocString(&pNewChain->wzDefaultApplicationId, pChain->wzDefaultApplicationId, 0); + ApupExitOnFailure(hr, "Failed to copy default application id."); + } + + if (pChain->wzDefaultApplicationType) + { + hr = StrAllocString(&pNewChain->wzDefaultApplicationType, pChain->wzDefaultApplicationType, 0); + ApupExitOnFailure(hr, "Failed to copy default application type."); + } + + pNewChain->rgEntries = prgEntries; + pNewChain->cEntries = cEntries; + + *ppFilteredChain = pNewChain; + pNewChain = NULL; + +LExit: + ReleaseApupChain(pNewChain); + return hr; +} + + +// +// ApupFreeChain - frees a previously allocated application update chain. +// +extern "C" void DAPI ApupFreeChain( + __in APPLICATION_UPDATE_CHAIN* pChain + ) +{ + if (pChain) + { + for (DWORD i = 0; i < pChain->cEntries; ++i) + { + FreeEntry(pChain->rgEntries + i); + } + + ReleaseMem(pChain->rgEntries); + ReleaseStr(pChain->wzDefaultApplicationType); + ReleaseStr(pChain->wzDefaultApplicationId); + ReleaseMem(pChain); + } +} + + +static HRESULT ProcessEntry( + __in ATOM_ENTRY* pAtomEntry, + __in LPCWSTR wzDefaultAppId, + __inout APPLICATION_UPDATE_ENTRY* pApupEntry + ) +{ + HRESULT hr = S_OK; + int nCompareResult = 0; + + // First search the ATOM entry's custom elements to try and find the application update information. + for (ATOM_UNKNOWN_ELEMENT* pElement = pAtomEntry->pUnknownElements; pElement; pElement = pElement->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1)) + { + hr = StrAllocString(&pApupEntry->wzApplicationId, pElement->wzValue, 0); + ApupExitOnFailure(hr, "Failed to allocate application identity."); + + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1)) + { + hr = StrAllocString(&pApupEntry->wzApplicationType, pAttribute->wzValue, 0); + ApupExitOnFailure(hr, "Failed to allocate application type."); + } + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"upgrade", -1)) + { + hr = StrAllocString(&pApupEntry->wzUpgradeId, pElement->wzValue, 0); + ApupExitOnFailure(hr, "Failed to allocate upgrade id."); + + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"version", -1)) + { + hr = VerParseVersion(pAttribute->wzValue, 0, FALSE, &pApupEntry->pUpgradeVersion); + ApupExitOnFailure(hr, "Failed to parse upgrade version string '%ls' from ATOM entry.", pAttribute->wzValue); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"exclusive", -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzValue, -1, L"true", -1)) + { + pApupEntry->fUpgradeExclusive = TRUE; + } + } + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"version", -1)) + { + hr = VerParseVersion(pElement->wzValue, 0, FALSE, &pApupEntry->pVersion); + ApupExitOnFailure(hr, "Failed to parse version string '%ls' from ATOM entry.", pElement->wzValue); + } + } + } + + // If there is no application identity or no version, skip the whole thing. + if ((!pApupEntry->wzApplicationId && !wzDefaultAppId) || !pApupEntry->pVersion) + { + ExitFunction1(hr = S_FALSE); // skip this update since it has no application id or version. + } + + if (pApupEntry->pUpgradeVersion) + { + hr = VerCompareParsedVersions(pApupEntry->pUpgradeVersion, pApupEntry->pVersion, &nCompareResult); + ApupExitOnFailure(hr, "Failed to compare version to upgrade version."); + + if (nCompareResult >= 0) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ApupExitOnRootFailure(hr, "Upgrade version is greater than or equal to application version."); + } + } + + if (pAtomEntry->wzTitle) + { + hr = StrAllocString(&pApupEntry->wzTitle, pAtomEntry->wzTitle, 0); + ApupExitOnFailure(hr, "Failed to allocate application title."); + } + + if (pAtomEntry->wzSummary) + { + hr = StrAllocString(&pApupEntry->wzSummary, pAtomEntry->wzSummary, 0); + ApupExitOnFailure(hr, "Failed to allocate application summary."); + } + + if (pAtomEntry->pContent) + { + if (pAtomEntry->pContent->wzType) + { + hr = StrAllocString(&pApupEntry->wzContentType, pAtomEntry->pContent->wzType, 0); + ApupExitOnFailure(hr, "Failed to allocate content type."); + } + + if (pAtomEntry->pContent->wzValue) + { + hr = StrAllocString(&pApupEntry->wzContent, pAtomEntry->pContent->wzValue, 0); + ApupExitOnFailure(hr, "Failed to allocate content."); + } + } + // Now process the enclosures. Assume every link in the ATOM entry is an enclosure. + pApupEntry->rgEnclosures = static_cast(MemAlloc(sizeof(APPLICATION_UPDATE_ENCLOSURE) * pAtomEntry->cLinks, TRUE)); + ApupExitOnNull(pApupEntry->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate enclosures for application update entry."); + + for (DWORD i = 0; i < pAtomEntry->cLinks; ++i) + { + ATOM_LINK* pLink = pAtomEntry->rgLinks + i; + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLink->wzRel, -1, L"enclosure", -1)) + { + hr = ParseEnclosure(pLink, pApupEntry->rgEnclosures + pApupEntry->cEnclosures); + ApupExitOnFailure(hr, "Failed to parse enclosure."); + + pApupEntry->dw64TotalSize += pApupEntry->rgEnclosures[pApupEntry->cEnclosures].dw64Size; // total up the size of the enclosures + + ++pApupEntry->cEnclosures; + } + } + +LExit: + if (S_OK != hr) // if anything went wrong, free the entry. + { + FreeEntry(pApupEntry); + memset(pApupEntry, 0, sizeof(APPLICATION_UPDATE_ENTRY)); + } + + return hr; +} + + +static HRESULT ParseEnclosure( + __in ATOM_LINK* pLink, + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure + ) +{ + HRESULT hr = S_OK; + DWORD dwDigestLength = 0; + DWORD dwDigestStringLength = 0; + size_t cchDigestString = 0; + + // First search the ATOM link's custom elements to try and find the application update enclosure information. + for (ATOM_UNKNOWN_ELEMENT* pElement = pLink->pUnknownElements; pElement; pElement = pElement->pNext) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"digest", -1, pElement->wzElement, -1)) + { + // Find the digest[@algorithm] which is required. Everything else is ignored. + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext) + { + dwDigestLength = 0; + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"algorithm", -1, pAttribute->wzAttribute, -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"md5", -1, pAttribute->wzValue, -1)) + { + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_MD5; + dwDigestLength = MD5_HASH_LEN; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha1", -1, pAttribute->wzValue, -1)) + { + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA1; + dwDigestLength = SHA1_HASH_LEN; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha256", -1, pAttribute->wzValue, -1)) + { + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA256; + dwDigestLength = SHA256_HASH_LEN; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha512", -1, pAttribute->wzValue, -1)) + { + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA512; + dwDigestLength = SHA512_HASH_LEN; + } + break; + } + } + + if (dwDigestLength) + { + dwDigestStringLength = 2 * dwDigestLength; + + hr = ::StringCchLengthW(pElement->wzValue, STRSAFE_MAX_CCH, &cchDigestString); + ApupExitOnFailure(hr, "Failed to get string length of digest value."); + + if (dwDigestStringLength != cchDigestString) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ApupExitOnRootFailure(hr, "Invalid digest length (%Iu) for digest algorithm (%u).", cchDigestString, dwDigestStringLength); + } + + pEnclosure->cbDigest = sizeof(BYTE) * dwDigestLength; + pEnclosure->rgbDigest = static_cast(MemAlloc(pEnclosure->cbDigest, TRUE)); + ApupExitOnNull(pEnclosure->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for digest."); + + hr = StrHexDecode(pElement->wzValue, pEnclosure->rgbDigest, pEnclosure->cbDigest); + ApupExitOnFailure(hr, "Failed to decode digest value."); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ApupExitOnRootFailure(hr, "Unknown algorithm type for digest."); + } + + break; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"name", -1, pElement->wzElement, -1)) + { + hr = StrAllocString(&pEnclosure->wzLocalName, pElement->wzValue, 0); + ApupExitOnFailure(hr, "Failed to copy local name."); + } + } + } + + pEnclosure->dw64Size = pLink->dw64Length; + + hr = StrAllocString(&pEnclosure->wzUrl, pLink->wzUrl, 0); + ApupExitOnFailure(hr, "Failed to allocate enclosure URL."); + + pEnclosure->fInstaller = FALSE; + pEnclosure->wzLocalName = NULL; + +LExit: + return hr; +} + + +static __callback int __cdecl CompareEntries( + void* /*pvContext*/, + const void* pvLeft, + const void* pvRight + ) +{ + int ret = 0; + const APPLICATION_UPDATE_ENTRY* pEntryLeft = static_cast(pvLeft); + const APPLICATION_UPDATE_ENTRY* pEntryRight = static_cast(pvRight); + + VerCompareParsedVersions(pEntryLeft->pVersion, pEntryRight->pVersion, &ret); + if (0 == ret) + { + VerCompareParsedVersions(pEntryLeft->pUpgradeVersion, pEntryRight->pUpgradeVersion, &ret); + if (0 == ret) + { + ret = (pEntryLeft->dw64TotalSize < pEntryRight->dw64TotalSize) ? -1 : + (pEntryLeft->dw64TotalSize > pEntryRight->dw64TotalSize) ? 1 : 0; + } + } + + // Sort descending. + ret = -ret; + + return ret; +} + + +static HRESULT FilterEntries( + __in APPLICATION_UPDATE_ENTRY* rgEntries, + __in DWORD cEntries, + __in VERUTIL_VERSION* pCurrentVersion, + __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries, + __inout DWORD* pcFilteredEntries + ) +{ + HRESULT hr = S_OK; + int nCompareResult = 0; + size_t cbAllocSize = 0; + const APPLICATION_UPDATE_ENTRY* pRequired = NULL;; + LPVOID pv = NULL; + + if (cEntries) + { + for (DWORD i = 0; i < cEntries; ++i) + { + const APPLICATION_UPDATE_ENTRY* pEntry = rgEntries + i; + + hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pVersion, &nCompareResult); + ApupExitOnFailure(hr, "Failed to compare versions."); + + if (nCompareResult >= 0) + { + continue; + } + + hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pUpgradeVersion, &nCompareResult); + ApupExitOnFailure(hr, "Failed to compare upgrade versions."); + + if (nCompareResult > 0 || (!pEntry->fUpgradeExclusive && nCompareResult == 0)) + { + pRequired = pEntry; + break; + } + } + + if (pRequired) + { + DWORD cNewFilteredEntries = *pcFilteredEntries + 1; + + hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENTRY), cNewFilteredEntries, &cbAllocSize); + ApupExitOnFailure(hr, "Overflow while calculating alloc size for more entries - number of entries: %u", cNewFilteredEntries); + + if (*prgFilteredEntries) + { + pv = MemReAlloc(*prgFilteredEntries, cbAllocSize, FALSE); + ApupExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for more entries."); + } + else + { + pv = MemAlloc(cbAllocSize, TRUE); + ApupExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for entries."); + } + + *pcFilteredEntries = cNewFilteredEntries; + *prgFilteredEntries = static_cast(pv); + pv = NULL; + + hr = CopyEntry(pRequired, *prgFilteredEntries + *pcFilteredEntries - 1); + ApupExitOnFailure(hr, "Failed to deep copy entry."); + + hr = VerCompareParsedVersions(pRequired->pVersion, rgEntries[0].pVersion, &nCompareResult); + ApupExitOnFailure(hr, "Failed to compare required version."); + + if (nCompareResult < 0) + { + FilterEntries(rgEntries, cEntries, pRequired->pVersion, prgFilteredEntries, pcFilteredEntries); + } + } + } + +LExit: + ReleaseMem(pv); + return hr; +} + + +static HRESULT CopyEntry( + __in const APPLICATION_UPDATE_ENTRY* pSrc, + __in APPLICATION_UPDATE_ENTRY* pDest + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + + memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENTRY)); + + if (pSrc->wzApplicationId) + { + hr = StrAllocString(&pDest->wzApplicationId, pSrc->wzApplicationId, 0); + ApupExitOnFailure(hr, "Failed to copy application id."); + } + + if (pSrc->wzApplicationType) + { + hr = StrAllocString(&pDest->wzApplicationType, pSrc->wzApplicationType, 0); + ApupExitOnFailure(hr, "Failed to copy application type."); + } + + if (pSrc->wzUpgradeId) + { + hr = StrAllocString(&pDest->wzUpgradeId, pSrc->wzUpgradeId, 0); + ApupExitOnFailure(hr, "Failed to copy upgrade id."); + } + + if (pSrc->wzTitle) + { + hr = StrAllocString(&pDest->wzTitle, pSrc->wzTitle, 0); + ApupExitOnFailure(hr, "Failed to copy title."); + } + + if (pSrc->wzSummary) + { + hr = StrAllocString(&pDest->wzSummary, pSrc->wzSummary, 0); + ApupExitOnFailure(hr, "Failed to copy summary."); + } + + if (pSrc->wzContentType) + { + hr = StrAllocString(&pDest->wzContentType, pSrc->wzContentType, 0); + ApupExitOnFailure(hr, "Failed to copy content type."); + } + + if (pSrc->wzContent) + { + hr = StrAllocString(&pDest->wzContent, pSrc->wzContent, 0); + ApupExitOnFailure(hr, "Failed to copy content."); + } + + pDest->dw64TotalSize = pSrc->dw64TotalSize; + + hr = VerCopyVersion(pSrc->pUpgradeVersion, &pDest->pUpgradeVersion); + ApupExitOnFailure(hr, "Failed to copy upgrade version."); + + hr = VerCopyVersion(pSrc->pVersion, &pDest->pVersion); + ApupExitOnFailure(hr, "Failed to copy version."); + + pDest->fUpgradeExclusive = pSrc->fUpgradeExclusive; + + hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENCLOSURE), pSrc->cEnclosures, &cbAllocSize); + ApupExitOnRootFailure(hr, "Overflow while calculating memory allocation size"); + + pDest->rgEnclosures = static_cast(MemAlloc(cbAllocSize, TRUE)); + ApupExitOnNull(pDest->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate copy of enclosures."); + + pDest->cEnclosures = pSrc->cEnclosures; + + for (DWORD i = 0; i < pDest->cEnclosures; ++i) + { + hr = CopyEnclosure(pSrc->rgEnclosures + i, pDest->rgEnclosures + i); + ApupExitOnFailure(hr, "Failed to copy enclosure."); + } + +LExit: + if (FAILED(hr)) + { + FreeEntry(pDest); + } + + return hr; +} + + +static HRESULT CopyEnclosure( + __in const APPLICATION_UPDATE_ENCLOSURE* pSrc, + __in APPLICATION_UPDATE_ENCLOSURE* pDest + ) +{ + HRESULT hr = S_OK; + + memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENCLOSURE)); + + if (pSrc->wzUrl) + { + hr = StrAllocString(&pDest->wzUrl, pSrc->wzUrl, 0); + ApupExitOnFailure(hr, "Failed copy url."); + } + + if (pSrc->wzLocalName) + { + hr = StrAllocString(&pDest->wzLocalName, pSrc->wzLocalName, 0); + ApupExitOnFailure(hr, "Failed copy url."); + } + + pDest->rgbDigest = static_cast(MemAlloc(sizeof(BYTE) * pSrc->cbDigest, FALSE)); + ApupExitOnNull(pDest->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for copy of digest."); + + pDest->cbDigest = pSrc->cbDigest; + + memcpy_s(pDest->rgbDigest, sizeof(BYTE) * pDest->cbDigest, pSrc->rgbDigest, sizeof(BYTE) * pSrc->cbDigest); + + pDest->digestAlgorithm = pSrc->digestAlgorithm; + + pDest->dw64Size = pSrc->dw64Size; + pDest->fInstaller = pSrc->fInstaller; + +LExit: + if (FAILED(hr)) + { + FreeEnclosure(pDest); + } + + return hr; +} + + +static void FreeEntry( + __in APPLICATION_UPDATE_ENTRY* pEntry + ) +{ + if (pEntry) + { + for (DWORD i = 0; i < pEntry->cEnclosures; ++i) + { + FreeEnclosure(pEntry->rgEnclosures + i); + } + + ReleaseStr(pEntry->wzUpgradeId); + ReleaseStr(pEntry->wzApplicationType); + ReleaseStr(pEntry->wzApplicationId); + ReleaseStr(pEntry->wzTitle); + ReleaseStr(pEntry->wzSummary); + ReleaseStr(pEntry->wzContentType); + ReleaseStr(pEntry->wzContent); + ReleaseVerutilVersion(pEntry->pVersion); + ReleaseVerutilVersion(pEntry->pUpgradeVersion); + } +} + + +static void FreeEnclosure( + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure + ) +{ + if (pEnclosure) + { + ReleaseMem(pEnclosure->rgbDigest); + ReleaseStr(pEnclosure->wzLocalName); + ReleaseStr(pEnclosure->wzUrl); + } +} diff --git a/src/libs/dutil/WixToolset.DUtil/atomutil.cpp b/src/libs/dutil/WixToolset.DUtil/atomutil.cpp new file mode 100644 index 00000000..c7c7975a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/atomutil.cpp @@ -0,0 +1,1297 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define AtomExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__) +#define AtomExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__) +#define AtomExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__) +#define AtomExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__) +#define AtomExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__) +#define AtomExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ATOMUTIL, e, x, s, __VA_ARGS__) +#define AtomExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ATOMUTIL, g, x, s, __VA_ARGS__) + +static HRESULT ParseAtomDocument( + __in IXMLDOMDocument *pixd, + __out ATOM_FEED **ppFeed + ); +static HRESULT ParseAtomFeed( + __in IXMLDOMNode *pixnFeed, + __out ATOM_FEED **ppFeed + ); +static HRESULT ParseAtomAuthor( + __in IXMLDOMNode* pixnAuthor, + __in ATOM_AUTHOR* pAuthor + ); +static HRESULT ParseAtomCategory( + __in IXMLDOMNode* pixnCategory, + __in ATOM_CATEGORY* pCategory + ); +static HRESULT ParseAtomEntry( + __in IXMLDOMNode* pixnEntry, + __in ATOM_ENTRY* pEntry + ); +static HRESULT ParseAtomLink( + __in IXMLDOMNode* pixnLink, + __in ATOM_LINK* pLink + ); +static HRESULT ParseAtomUnknownElement( + __in IXMLDOMNode *pNode, + __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement + ); +static HRESULT ParseAtomUnknownAttribute( + __in IXMLDOMNode *pNode, + __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute + ); +static HRESULT AssignDateTime( + __in FILETIME* pft, + __in IXMLDOMNode* pNode + ); +static HRESULT AssignString( + __out_z LPWSTR* pwzValue, + __in IXMLDOMNode* pNode + ); +static void FreeAtomAuthor( + __in_opt ATOM_AUTHOR* pAuthor + ); +static void FreeAtomContent( + __in_opt ATOM_CONTENT* pContent + ); +static void FreeAtomCategory( + __in_opt ATOM_CATEGORY* pCategory + ); +static void FreeAtomEntry( + __in_opt ATOM_ENTRY* pEntry + ); +static void FreeAtomLink( + __in_opt ATOM_LINK* pLink + ); +static void FreeAtomUnknownElementList( + __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement + ); +static void FreeAtomUnknownAttributeList( + __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute + ); + +template static HRESULT AllocateAtomType( + __in IXMLDOMNode* pixnParent, + __in LPCWSTR wzT, + __out T** pprgT, + __out DWORD* pcT + ); + + +/******************************************************************** + AtomInitialize - Initialize ATOM utilities. + +*********************************************************************/ +extern "C" HRESULT DAPI AtomInitialize() +{ + return XmlInitialize(); +} + + +/******************************************************************** + AtomUninitialize - Uninitialize ATOM utilities. + +*********************************************************************/ +extern "C" void DAPI AtomUninitialize() +{ + XmlUninitialize(); +} + + +/******************************************************************** + AtomParseFromString - parses out an ATOM feed from a string. + +*********************************************************************/ +extern "C" HRESULT DAPI AtomParseFromString( + __in_z LPCWSTR wzAtomString, + __out ATOM_FEED **ppFeed + ) +{ + Assert(wzAtomString); + Assert(ppFeed); + + HRESULT hr = S_OK; + ATOM_FEED *pNewFeed = NULL; + IXMLDOMDocument *pixdAtom = NULL; + + hr = XmlLoadDocument(wzAtomString, &pixdAtom); + AtomExitOnFailure(hr, "Failed to load ATOM string as XML document."); + + hr = ParseAtomDocument(pixdAtom, &pNewFeed); + AtomExitOnFailure(hr, "Failed to parse ATOM document."); + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseAtomFeed(pNewFeed); + ReleaseObject(pixdAtom); + + return hr; +} + + +/******************************************************************** + AtomParseFromFile - parses out an ATOM feed from a file path. + +*********************************************************************/ +extern "C" HRESULT DAPI AtomParseFromFile( + __in_z LPCWSTR wzAtomFile, + __out ATOM_FEED **ppFeed + ) +{ + Assert(wzAtomFile); + Assert(ppFeed); + + HRESULT hr = S_OK; + ATOM_FEED *pNewFeed = NULL; + IXMLDOMDocument *pixdAtom = NULL; + + hr = XmlLoadDocumentFromFile(wzAtomFile, &pixdAtom); + AtomExitOnFailure(hr, "Failed to load ATOM string as XML document."); + + hr = ParseAtomDocument(pixdAtom, &pNewFeed); + AtomExitOnFailure(hr, "Failed to parse ATOM document."); + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseAtomFeed(pNewFeed); + ReleaseObject(pixdAtom); + + return hr; +} + + +/******************************************************************** + AtomParseFromDocument - parses out an ATOM feed from an XML document. + +*********************************************************************/ +extern "C" HRESULT DAPI AtomParseFromDocument( + __in IXMLDOMDocument* pixdDocument, + __out ATOM_FEED **ppFeed + ) +{ + Assert(pixdDocument); + Assert(ppFeed); + + HRESULT hr = S_OK; + ATOM_FEED *pNewFeed = NULL; + + hr = ParseAtomDocument(pixdDocument, &pNewFeed); + AtomExitOnFailure(hr, "Failed to parse ATOM document."); + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseAtomFeed(pNewFeed); + + return hr; +} + + +/******************************************************************** + AtomFreeFeed - parses out an ATOM feed from a string. + +*********************************************************************/ +extern "C" void DAPI AtomFreeFeed( + __in_xcount(pFeed->cItems) ATOM_FEED* pFeed + ) +{ + if (pFeed) + { + FreeAtomUnknownElementList(pFeed->pUnknownElements); + ReleaseObject(pFeed->pixn); + + for (DWORD i = 0; i < pFeed->cLinks; ++i) + { + FreeAtomLink(pFeed->rgLinks + i); + } + ReleaseMem(pFeed->rgLinks); + + for (DWORD i = 0; i < pFeed->cEntries; ++i) + { + FreeAtomEntry(pFeed->rgEntries + i); + } + ReleaseMem(pFeed->rgEntries); + + for (DWORD i = 0; i < pFeed->cCategories; ++i) + { + FreeAtomCategory(pFeed->rgCategories + i); + } + ReleaseMem(pFeed->rgCategories); + + for (DWORD i = 0; i < pFeed->cAuthors; ++i) + { + FreeAtomAuthor(pFeed->rgAuthors + i); + } + ReleaseMem(pFeed->rgAuthors); + + ReleaseStr(pFeed->wzGenerator); + ReleaseStr(pFeed->wzIcon); + ReleaseStr(pFeed->wzId); + ReleaseStr(pFeed->wzLogo); + ReleaseStr(pFeed->wzSubtitle); + ReleaseStr(pFeed->wzTitle); + + MemFree(pFeed); + } +} + + +/******************************************************************** + ParseAtomDocument - parses out an ATOM feed from a loaded XML DOM document. + +*********************************************************************/ +static HRESULT ParseAtomDocument( + __in IXMLDOMDocument *pixd, + __out ATOM_FEED **ppFeed + ) +{ + Assert(pixd); + Assert(ppFeed); + + HRESULT hr = S_OK; + IXMLDOMElement *pFeedElement = NULL; + + ATOM_FEED *pNewFeed = NULL; + + // + // Get the document element and start processing feeds. + // + hr = pixd->get_documentElement(&pFeedElement); + AtomExitOnFailure(hr, "failed get_documentElement in ParseAtomDocument"); + + hr = ParseAtomFeed(pFeedElement, &pNewFeed); + AtomExitOnFailure(hr, "Failed to parse ATOM feed."); + + if (S_FALSE == hr) + { + hr = S_OK; + } + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseObject(pFeedElement); + + ReleaseAtomFeed(pNewFeed); + + return hr; +} + + +/******************************************************************** + ParseAtomFeed - parses out an ATOM feed from a loaded XML DOM element. + +*********************************************************************/ +static HRESULT ParseAtomFeed( + __in IXMLDOMNode *pixnFeed, + __out ATOM_FEED **ppFeed + ) +{ + Assert(pixnFeed); + Assert(ppFeed); + + HRESULT hr = S_OK; + IXMLDOMNodeList *pNodeList = NULL; + + ATOM_FEED *pNewFeed = NULL; + DWORD cAuthors = 0; + DWORD cCategories = 0; + DWORD cEntries = 0; + DWORD cLinks = 0; + + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + // First, allocate the new feed and all the possible sub elements. + pNewFeed = (ATOM_FEED*)MemAlloc(sizeof(ATOM_FEED), TRUE); + AtomExitOnNull(pNewFeed, hr, E_OUTOFMEMORY, "Failed to allocate ATOM feed structure."); + + pNewFeed->pixn = pixnFeed; + pNewFeed->pixn->AddRef(); + + hr = AllocateAtomType(pixnFeed, L"author", &pNewFeed->rgAuthors, &pNewFeed->cAuthors); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed authors."); + + hr = AllocateAtomType(pixnFeed, L"category", &pNewFeed->rgCategories, &pNewFeed->cCategories); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed categories."); + + hr = AllocateAtomType(pixnFeed, L"entry", &pNewFeed->rgEntries, &pNewFeed->cEntries); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed entries."); + + hr = AllocateAtomType(pixnFeed, L"link", &pNewFeed->rgLinks, &pNewFeed->cLinks); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed links."); + + // Second, process the elements under a feed. + hr = pixnFeed->get_childNodes(&pNodeList); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM feed element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"generator", -1)) + { + hr = AssignString(&pNewFeed->wzGenerator, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed generator."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"icon", -1)) + { + hr = AssignString(&pNewFeed->wzIcon, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed icon."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) + { + hr = AssignString(&pNewFeed->wzId, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed id."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"logo", -1)) + { + hr = AssignString(&pNewFeed->wzLogo, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed logo."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"subtitle", -1)) + { + hr = AssignString(&pNewFeed->wzSubtitle, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed subtitle."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) + { + hr = AssignString(&pNewFeed->wzTitle, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed title."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) + { + hr = AssignDateTime(&pNewFeed->ftUpdated, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM feed updated."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) + { + hr = ParseAtomAuthor(pNode, &pNewFeed->rgAuthors[cAuthors]); + AtomExitOnFailure(hr, "Failed to parse ATOM author."); + + ++cAuthors; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) + { + hr = ParseAtomCategory(pNode, &pNewFeed->rgCategories[cCategories]); + AtomExitOnFailure(hr, "Failed to parse ATOM category."); + + ++cCategories; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"entry", -1)) + { + hr = ParseAtomEntry(pNode, &pNewFeed->rgEntries[cEntries]); + AtomExitOnFailure(hr, "Failed to parse ATOM entry."); + + ++cEntries; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) + { + hr = ParseAtomLink(pNode, &pNewFeed->rgLinks[cLinks]); + AtomExitOnFailure(hr, "Failed to parse ATOM link."); + + ++cLinks; + } + else + { + hr = ParseAtomUnknownElement(pNode, &pNewFeed->pUnknownElements); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM feed element: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + + if (!pNewFeed->wzId || !*pNewFeed->wzId) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Failed to find required feed/id element."); + } + else if (!pNewFeed->wzTitle || !*pNewFeed->wzTitle) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Failed to find required feed/title element."); + } + else if (0 == pNewFeed->ftUpdated.dwHighDateTime && 0 == pNewFeed->ftUpdated.dwLowDateTime) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Failed to find required feed/updated element."); + } + + *ppFeed = pNewFeed; + pNewFeed = NULL; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + + ReleaseAtomFeed(pNewFeed); + + return hr; +} + + +/******************************************************************** + AllocateAtomType - allocates enough space for all of the ATOM elements + of a particular type under a particular node. + +*********************************************************************/ +template static HRESULT AllocateAtomType( + __in IXMLDOMNode* pixnParent, + __in LPCWSTR wzT, + __out T** pprgT, + __out DWORD* pcT + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList *pNodeList = NULL; + + long cT = 0; + T* prgT = NULL; + + hr = XmlSelectNodes(pixnParent, wzT, &pNodeList); + AtomExitOnFailure(hr, "Failed to select all ATOM %ls.", wzT); + + if (S_OK == hr) + { + hr = pNodeList->get_length(&cT); + AtomExitOnFailure(hr, "Failed to count the number of ATOM %ls.", wzT); + + if (cT == 0) + { + ExitFunction(); + } + + prgT = static_cast(MemAlloc(sizeof(T) * cT, TRUE)); + AtomExitOnNull(prgT, hr, E_OUTOFMEMORY, "Failed to allocate ATOM."); + + *pcT = cT; + *pprgT = prgT; + prgT = NULL; + } + else + { + *pprgT = NULL; + *pcT = 0; + } + +LExit: + ReleaseMem(prgT); + ReleaseObject(pNodeList); + + return hr; +} + + +/******************************************************************** + ParseAtomAuthor - parses out an ATOM author from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomAuthor( + __in IXMLDOMNode* pixnAuthor, + __in ATOM_AUTHOR* pAuthor + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + hr = pixnAuthor->get_childNodes(&pNodeList); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM author element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"name", -1)) + { + hr = AssignString(&pAuthor->wzName, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM author name."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"email", -1)) + { + hr = AssignString(&pAuthor->wzEmail, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM author email."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"uri", -1)) + { + hr = AssignString(&pAuthor->wzUrl, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM author uri."); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM author elements."); + + hr = S_OK; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + + return hr; +} + + +/******************************************************************** + ParseAtomCategory - parses out an ATOM category from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomCategory( + __in IXMLDOMNode* pixnCategory, + __in ATOM_CATEGORY* pCategory + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + // Process attributes first. + hr = pixnCategory->get_attributes(&pixnnmAttributes); + AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); + + while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"label", -1)) + { + hr = AssignString(&pCategory->wzLabel, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM category label."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"scheme", -1)) + { + hr = AssignString(&pCategory->wzScheme, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM category scheme."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"term", -1)) + { + hr = AssignString(&pCategory->wzTerm, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM category term."); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM category attributes."); + + // Process elements second. + hr = pixnCategory->get_childNodes(&pNodeList); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM category element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + hr = ParseAtomUnknownElement(pNode, &pCategory->pUnknownElements); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM category element: %ls", bstrNodeName); + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM category elements."); + + hr = S_OK; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + ReleaseObject(pixnnmAttributes); + + return hr; +} + + +/******************************************************************** + ParseAtomContent - parses out an ATOM content from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomContent( + __in IXMLDOMNode* pixnContent, + __in ATOM_CONTENT* pContent + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + // Process attributes first. + hr = pixnContent->get_attributes(&pixnnmAttributes); + AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); + + while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) + { + hr = AssignString(&pContent->wzType, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM content type."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"url", -1)) + { + hr = AssignString(&pContent->wzUrl, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM content scheme."); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM content attributes."); + + // Process elements second. + hr = pixnContent->get_childNodes(&pNodeList); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM content element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + hr = ParseAtomUnknownElement(pNode, &pContent->pUnknownElements); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM content element: %ls", bstrNodeName); + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM content elements."); + + hr = AssignString(&pContent->wzValue, pixnContent); + AtomExitOnFailure(hr, "Failed to allocate ATOM content value."); + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + ReleaseObject(pixnnmAttributes); + + return hr; +} + + +/******************************************************************** + ParseAtomEntry - parses out an ATOM entry from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomEntry( + __in IXMLDOMNode* pixnEntry, + __in ATOM_ENTRY* pEntry + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + DWORD cAuthors = 0; + DWORD cCategories = 0; + DWORD cLinks = 0; + + pEntry->pixn = pixnEntry; + pEntry->pixn->AddRef(); + + // First, allocate all the possible sub elements. + hr = AllocateAtomType(pixnEntry, L"author", &pEntry->rgAuthors, &pEntry->cAuthors); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry authors."); + + hr = AllocateAtomType(pixnEntry, L"category", &pEntry->rgCategories, &pEntry->cCategories); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry categories."); + + hr = AllocateAtomType(pixnEntry, L"link", &pEntry->rgLinks, &pEntry->cLinks); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry links."); + + // Second, process elements. + hr = pixnEntry->get_childNodes(&pNodeList); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM entry element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1)) + { + hr = AssignString(&pEntry->wzId, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry id."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"summary", -1)) + { + hr = AssignString(&pEntry->wzSummary, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry summary."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) + { + hr = AssignString(&pEntry->wzTitle, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry title."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"published", -1)) + { + hr = AssignDateTime(&pEntry->ftPublished, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry published."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1)) + { + hr = AssignDateTime(&pEntry->ftUpdated, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM entry updated."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1)) + { + hr = ParseAtomAuthor(pNode, &pEntry->rgAuthors[cAuthors]); + AtomExitOnFailure(hr, "Failed to parse ATOM entry author."); + + ++cAuthors; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1)) + { + hr = ParseAtomCategory(pNode, &pEntry->rgCategories[cCategories]); + AtomExitOnFailure(hr, "Failed to parse ATOM entry category."); + + ++cCategories; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"content", -1)) + { + if (NULL != pEntry->pContent) + { + hr = E_UNEXPECTED; + AtomExitOnFailure(hr, "Cannot have two content elements in ATOM entry."); + } + + pEntry->pContent = static_cast(MemAlloc(sizeof(ATOM_CONTENT), TRUE)); + AtomExitOnNull(pEntry->pContent, hr, E_OUTOFMEMORY, "Failed to allocate ATOM entry content."); + + hr = ParseAtomContent(pNode, pEntry->pContent); + AtomExitOnFailure(hr, "Failed to parse ATOM entry content."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1)) + { + hr = ParseAtomLink(pNode, &pEntry->rgLinks[cLinks]); + AtomExitOnFailure(hr, "Failed to parse ATOM entry link."); + + ++cLinks; + } + else + { + hr = ParseAtomUnknownElement(pNode, &pEntry->pUnknownElements); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM entry element: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM entry elements."); + + if (!pEntry->wzId || !*pEntry->wzId) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Failed to find required feed/entry/id element."); + } + else if (!pEntry->wzTitle || !*pEntry->wzTitle) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Failed to find required feed/entry/title element."); + } + else if (0 == pEntry->ftUpdated.dwHighDateTime && 0 == pEntry->ftUpdated.dwLowDateTime) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Failed to find required feed/entry/updated element."); + } + + hr = S_OK; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + ReleaseObject(pixnnmAttributes); + + return hr; +} + + +/******************************************************************** + ParseAtomLink - parses out an ATOM link from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomLink( + __in IXMLDOMNode* pixnLink, + __in ATOM_LINK* pLink + ) +{ + HRESULT hr = S_OK; + + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNodeList *pNodeList = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + // Process attributes first. + hr = pixnLink->get_attributes(&pixnnmAttributes); + AtomExitOnFailure(hr, "Failed get attributes for ATOM link."); + + while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName))) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"rel", -1)) + { + hr = AssignString(&pLink->wzRel, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM link rel."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"href", -1)) + { + hr = AssignString(&pLink->wzUrl, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM link href."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"length", -1)) + { + hr = XmlGetAttributeLargeNumber(pixnLink, bstrNodeName, &pLink->dw64Length); + if (E_INVALIDARG == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + AtomExitOnFailure(hr, "Failed to parse ATOM link length."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1)) + { + hr = AssignString(&pLink->wzTitle, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM link title."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1)) + { + hr = AssignString(&pLink->wzType, pNode); + AtomExitOnFailure(hr, "Failed to allocate ATOM link type."); + } + else + { + hr = ParseAtomUnknownAttribute(pNode, &pLink->pUnknownAttributes); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM link attribute: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM link attributes."); + + // Process elements second. + hr = pixnLink->get_childNodes(&pNodeList); + AtomExitOnFailure(hr, "Failed to get child nodes of ATOM link element."); + + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + hr = ParseAtomUnknownElement(pNode, &pLink->pUnknownElements); + AtomExitOnFailure(hr, "Failed to parse unknown ATOM link element: %ls", bstrNodeName); + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + AtomExitOnFailure(hr, "Failed to process all ATOM link elements."); + + hr = AssignString(&pLink->wzValue, pixnLink); + AtomExitOnFailure(hr, "Failed to allocate ATOM link value."); + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + ReleaseObject(pixnnmAttributes); + + return hr; +} + + +/******************************************************************** + ParseAtomUnknownElement - parses out an unknown item from the ATOM feed from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseAtomUnknownElement( + __in IXMLDOMNode *pNode, + __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement + ) +{ + Assert(ppUnknownElement); + + HRESULT hr = S_OK; + BSTR bstrNodeNamespace = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNode* pixnAttribute = NULL; + ATOM_UNKNOWN_ELEMENT* pNewUnknownElement; + + pNewUnknownElement = (ATOM_UNKNOWN_ELEMENT*)MemAlloc(sizeof(ATOM_UNKNOWN_ELEMENT), TRUE); + AtomExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); + + hr = pNode->get_namespaceURI(&bstrNodeNamespace); + if (S_OK == hr) + { + hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element namespace."); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + AtomExitOnFailure(hr, "Failed to get unknown element namespace."); + + hr = pNode->get_baseName(&bstrNodeName); + AtomExitOnFailure(hr, "Failed to get unknown element name."); + + hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element name."); + + hr = XmlGetText(pNode, &bstrNodeValue); + AtomExitOnFailure(hr, "Failed to get unknown element value."); + + hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element value."); + + hr = pNode->get_attributes(&pixnnmAttributes); + AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element."); + + while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) + { + hr = ParseAtomUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); + AtomExitOnFailure(hr, "Failed to parse attribute on ATOM unknown element."); + + ReleaseNullObject(pixnAttribute); + } + + if (S_FALSE == hr) + { + hr = S_OK; + } + AtomExitOnFailure(hr, "Failed to enumerate all attributes on ATOM unknown element."); + + ATOM_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; + while (*ppTail) + { + ppTail = &(*ppTail)->pNext; + } + + *ppTail = pNewUnknownElement; + pNewUnknownElement = NULL; + +LExit: + FreeAtomUnknownElementList(pNewUnknownElement); + + ReleaseBSTR(bstrNodeNamespace); + ReleaseBSTR(bstrNodeName); + ReleaseBSTR(bstrNodeValue); + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixnAttribute); + + return hr; +} + + +/******************************************************************** + ParseAtomUnknownAttribute - parses out attribute from an unknown element + +*********************************************************************/ +static HRESULT ParseAtomUnknownAttribute( + __in IXMLDOMNode *pNode, + __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute + ) +{ + Assert(ppUnknownAttribute); + + HRESULT hr = S_OK; + BSTR bstrNodeNamespace = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + ATOM_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; + + pNewUnknownAttribute = (ATOM_UNKNOWN_ATTRIBUTE*)MemAlloc(sizeof(ATOM_UNKNOWN_ATTRIBUTE), TRUE); + AtomExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); + + hr = pNode->get_namespaceURI(&bstrNodeNamespace); + if (S_OK == hr) + { + hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute namespace."); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + AtomExitOnFailure(hr, "Failed to get unknown attribute namespace."); + + hr = pNode->get_baseName(&bstrNodeName); + AtomExitOnFailure(hr, "Failed to get unknown attribute name."); + + hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute name."); + + hr = XmlGetText(pNode, &bstrNodeValue); + AtomExitOnFailure(hr, "Failed to get unknown attribute value."); + + hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); + AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute value."); + + ATOM_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; + while (*ppTail) + { + ppTail = &(*ppTail)->pNext; + } + + *ppTail = pNewUnknownAttribute; + pNewUnknownAttribute = NULL; + +LExit: + FreeAtomUnknownAttributeList(pNewUnknownAttribute); + + ReleaseBSTR(bstrNodeNamespace); + ReleaseBSTR(bstrNodeName); + ReleaseBSTR(bstrNodeValue); + + return hr; +} + + +/******************************************************************** + AssignDateTime - assigns the value of a node to a FILETIME struct. + +*********************************************************************/ +static HRESULT AssignDateTime( + __in FILETIME* pft, + __in IXMLDOMNode* pNode + ) +{ + HRESULT hr = S_OK; + BSTR bstrValue = NULL; + + if (0 != pft->dwHighDateTime || 0 != pft->dwLowDateTime) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Already process this datetime value."); + } + + hr = XmlGetText(pNode, &bstrValue); + AtomExitOnFailure(hr, "Failed to get value."); + + if (S_OK == hr) + { + hr = TimeFromString3339(bstrValue, pft); + AtomExitOnFailure(hr, "Failed to convert value to time."); + } + else + { + ZeroMemory(pft, sizeof(FILETIME)); + hr = S_OK; + } + +LExit: + ReleaseBSTR(bstrValue); + + return hr; +} + + +/******************************************************************** + AssignString - assigns the value of a node to a dynamic string. + +*********************************************************************/ +static HRESULT AssignString( + __out_z LPWSTR* pwzValue, + __in IXMLDOMNode* pNode + ) +{ + HRESULT hr = S_OK; + BSTR bstrValue = NULL; + + if (pwzValue && *pwzValue) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + AtomExitOnRootFailure(hr, "Already processed this value."); + } + + hr = XmlGetText(pNode, &bstrValue); + AtomExitOnFailure(hr, "Failed to get value."); + + if (S_OK == hr) + { + hr = StrAllocString(pwzValue, bstrValue, 0); + AtomExitOnFailure(hr, "Failed to allocate value."); + } + else + { + ReleaseNullStr(pwzValue); + hr = S_OK; + } + +LExit: + ReleaseBSTR(bstrValue); + + return hr; +} + + +/******************************************************************** + FreeAtomAuthor - releases all of the memory used by an ATOM author. + +*********************************************************************/ +static void FreeAtomAuthor( + __in_opt ATOM_AUTHOR* pAuthor + ) +{ + if (pAuthor) + { + ReleaseStr(pAuthor->wzUrl); + ReleaseStr(pAuthor->wzEmail); + ReleaseStr(pAuthor->wzName); + } +} + + +/******************************************************************** + FreeAtomCategory - releases all of the memory used by an ATOM category. + +*********************************************************************/ +static void FreeAtomCategory( + __in_opt ATOM_CATEGORY* pCategory + ) +{ + if (pCategory) + { + FreeAtomUnknownElementList(pCategory->pUnknownElements); + + ReleaseStr(pCategory->wzTerm); + ReleaseStr(pCategory->wzScheme); + ReleaseStr(pCategory->wzLabel); + } +} + + +/******************************************************************** + FreeAtomContent - releases all of the memory used by an ATOM content. + +*********************************************************************/ +static void FreeAtomContent( + __in_opt ATOM_CONTENT* pContent + ) +{ + if (pContent) + { + FreeAtomUnknownElementList(pContent->pUnknownElements); + + ReleaseStr(pContent->wzValue); + ReleaseStr(pContent->wzUrl); + ReleaseStr(pContent->wzType); + } +} + + +/******************************************************************** + FreeAtomEntry - releases all of the memory used by an ATOM entry. + +*********************************************************************/ +static void FreeAtomEntry( + __in_opt ATOM_ENTRY* pEntry + ) +{ + if (pEntry) + { + FreeAtomUnknownElementList(pEntry->pUnknownElements); + ReleaseObject(pEntry->pixn); + + for (DWORD i = 0; i < pEntry->cLinks; ++i) + { + FreeAtomLink(pEntry->rgLinks + i); + } + ReleaseMem(pEntry->rgLinks); + + for (DWORD i = 0; i < pEntry->cCategories; ++i) + { + FreeAtomCategory(pEntry->rgCategories + i); + } + ReleaseMem(pEntry->rgCategories); + + for (DWORD i = 0; i < pEntry->cAuthors; ++i) + { + FreeAtomAuthor(pEntry->rgAuthors + i); + } + ReleaseMem(pEntry->rgAuthors); + + FreeAtomContent(pEntry->pContent); + ReleaseMem(pEntry->pContent); + + ReleaseStr(pEntry->wzTitle); + ReleaseStr(pEntry->wzSummary); + ReleaseStr(pEntry->wzId); + } +} + + +/******************************************************************** + FreeAtomLink - releases all of the memory used by an ATOM link. + +*********************************************************************/ +static void FreeAtomLink( + __in_opt ATOM_LINK* pLink + ) +{ + if (pLink) + { + FreeAtomUnknownElementList(pLink->pUnknownElements); + FreeAtomUnknownAttributeList(pLink->pUnknownAttributes); + + ReleaseStr(pLink->wzValue); + ReleaseStr(pLink->wzUrl); + ReleaseStr(pLink->wzType); + ReleaseStr(pLink->wzTitle); + ReleaseStr(pLink->wzRel); + } +} + + +/******************************************************************** + FreeAtomUnknownElement - releases all of the memory used by a list of unknown elements + +*********************************************************************/ +static void FreeAtomUnknownElementList( + __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement + ) +{ + while (pUnknownElement) + { + ATOM_UNKNOWN_ELEMENT* pFree = pUnknownElement; + pUnknownElement = pUnknownElement->pNext; + + FreeAtomUnknownAttributeList(pFree->pAttributes); + ReleaseStr(pFree->wzNamespace); + ReleaseStr(pFree->wzElement); + ReleaseStr(pFree->wzValue); + MemFree(pFree); + } +} + + +/******************************************************************** + FreeAtomUnknownAttribute - releases all of the memory used by a list of unknown attributes + +*********************************************************************/ +static void FreeAtomUnknownAttributeList( + __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute + ) +{ + while (pUnknownAttribute) + { + ATOM_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute; + pUnknownAttribute = pUnknownAttribute->pNext; + + ReleaseStr(pFree->wzNamespace); + ReleaseStr(pFree->wzAttribute); + ReleaseStr(pFree->wzValue); + MemFree(pFree); + } +} diff --git a/src/libs/dutil/WixToolset.DUtil/buffutil.cpp b/src/libs/dutil/WixToolset.DUtil/buffutil.cpp new file mode 100644 index 00000000..b6d58cc0 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/buffutil.cpp @@ -0,0 +1,529 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define BuffExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__) +#define BuffExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUFFUTIL, p, x, e, s, __VA_ARGS__) +#define BuffExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, p, x, s, __VA_ARGS__) +#define BuffExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, p, x, e, s, __VA_ARGS__) +#define BuffExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, p, x, s, __VA_ARGS__) +#define BuffExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUFFUTIL, e, x, s, __VA_ARGS__) +#define BuffExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_BUFFUTIL, g, x, s, __VA_ARGS__) + + +// constants + +#define BUFFER_INCREMENT 128 + + +// helper function declarations + +static HRESULT EnsureBufferSize( + __deref_inout_bcount(cbSize) BYTE** ppbBuffer, + __in SIZE_T cbSize + ); + + +// functions + +extern "C" HRESULT BuffReadNumber( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD* pdw + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pdw); + + HRESULT hr = S_OK; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size."); + + // verify buffer size + if (sizeof(DWORD) > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small."); + } + + *pdw = *(const DWORD*)(pbBuffer + *piBuffer); + *piBuffer += sizeof(DWORD); + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadNumber64( + __in_bcount(cbBuffer) const BYTE * pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD64* pdw64 + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pdw64); + + HRESULT hr = S_OK; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size."); + + // verify buffer size + if (sizeof(DWORD64) > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small."); + } + + *pdw64 = *(const DWORD64*)(pbBuffer + *piBuffer); + *piBuffer += sizeof(DWORD64); + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadPointer( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD_PTR* pdw64 + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pdw64); + + HRESULT hr = S_OK; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size."); + + // verify buffer size + if (sizeof(DWORD_PTR) > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small."); + } + + *pdw64 = *(const DWORD_PTR*)(pbBuffer + *piBuffer); + *piBuffer += sizeof(DWORD_PTR); + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadString( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_z LPWSTR* pscz + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pscz); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cb = 0; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count."); + + // verify buffer size + if (sizeof(SIZE_T) > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small."); + } + + // read character count + cch = *(const SIZE_T*)(pbBuffer + *piBuffer); + + hr = ::SIZETMult(cch, sizeof(WCHAR), &cb); + BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); + + hr = ::SIZETAdd(*piBuffer, sizeof(SIZE_T), piBuffer); + BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); + + // verify buffer size + if (cb > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small to hold character data."); + } + + // copy character data + hr = StrAllocString(pscz, cch ? (LPCWSTR)(pbBuffer + *piBuffer) : L"", cch); + BuffExitOnFailure(hr, "Failed to copy character data."); + + *piBuffer += cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadStringAnsi( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_z LPSTR* pscz + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(pscz); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cb = 0; + SIZE_T cbAvailable = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count."); + + // verify buffer size + if (sizeof(SIZE_T) > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small."); + } + + // read character count + cch = *(const SIZE_T*)(pbBuffer + *piBuffer); + + hr = ::SIZETMult(cch, sizeof(CHAR), &cb); + BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size"); + + hr = ::SIZETAdd(*piBuffer, sizeof(SIZE_T), piBuffer); + BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size"); + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for character buffer."); + + // verify buffer size + if (cb > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small to hold character count."); + } + + // copy character data + hr = StrAnsiAllocStringAnsi(pscz, cch ? (LPCSTR)(pbBuffer + *piBuffer) : "", cch); + BuffExitOnFailure(hr, "Failed to copy character data."); + + *piBuffer += cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffReadStream( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_inout_bcount(*pcbStream) BYTE** ppbStream, + __out SIZE_T* pcbStream + ) +{ + Assert(pbBuffer); + Assert(piBuffer); + Assert(ppbStream); + Assert(pcbStream); + + HRESULT hr = S_OK; + SIZE_T cb = 0; + SIZE_T cbAvailable = 0; + errno_t err = 0; + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for stream size."); + + // verify buffer size + if (sizeof(SIZE_T) > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small."); + } + + // read stream size + cb = *(const SIZE_T*)(pbBuffer + *piBuffer); + *piBuffer += sizeof(SIZE_T); + + // get availiable data size + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable); + BuffExitOnRootFailure(hr, "Failed to calculate available data size for stream buffer."); + + // verify buffer size + if (cb > cbAvailable) + { + hr = E_INVALIDARG; + BuffExitOnRootFailure(hr, "Buffer too small to hold byte count."); + } + + // allocate buffer + *ppbStream = (BYTE*)MemAlloc(cb, TRUE); + BuffExitOnNull(*ppbStream, hr, E_OUTOFMEMORY, "Failed to allocate stream."); + + // read stream data + err = memcpy_s(*ppbStream, cbBuffer - *piBuffer, pbBuffer + *piBuffer, cb); + if (err) + { + BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to read stream from buffer, error: %d", err); + } + + *piBuffer += cb; + + // return stream size + *pcbStream = cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteNumber( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD dw + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD)); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy data to buffer + *(DWORD*)(*ppbBuffer + *piBuffer) = dw; + *piBuffer += sizeof(DWORD); + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteNumber64( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD64 dw64 + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD64)); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy data to buffer + *(DWORD64*)(*ppbBuffer + *piBuffer) = dw64; + *piBuffer += sizeof(DWORD64); + +LExit: + return hr; +} + +extern "C" HRESULT BuffWritePointer( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD_PTR dw + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD_PTR)); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy data to buffer + *(DWORD_PTR*)(*ppbBuffer + *piBuffer) = dw; + *piBuffer += sizeof(DWORD_PTR); + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteString( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_z_opt LPCWSTR scz + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cb = 0; + errno_t err = 0; + + if (scz) + { + hr = ::StringCchLengthW(scz, STRSAFE_MAX_CCH, reinterpret_cast(&cch)); + BuffExitOnRootFailure(hr, "Failed to get string size.") + } + + cb = cch * sizeof(WCHAR); + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(SIZE_T) + cb)); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy character count to buffer + *(SIZE_T*)(*ppbBuffer + *piBuffer) = cch; + *piBuffer += sizeof(SIZE_T); + + // copy data to buffer + err = memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); + if (err) + { + BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write string to buffer: '%ls', error: %d", scz, err); + } + + *piBuffer += cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteStringAnsi( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_z_opt LPCSTR scz + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cb = 0; + errno_t err = 0; + + if (scz) + { + hr = ::StringCchLengthA(scz, STRSAFE_MAX_CCH, reinterpret_cast(&cch)); + BuffExitOnRootFailure(hr, "Failed to get string size.") + } + + cb = cch * sizeof(CHAR); + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(SIZE_T) + cb)); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy character count to buffer + *(SIZE_T*)(*ppbBuffer + *piBuffer) = cch; + *piBuffer += sizeof(SIZE_T); + + // copy data to buffer + err = memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb); + if (err) + { + BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write string to buffer: '%hs', error: %d", scz, err); + } + + *piBuffer += cb; + +LExit: + return hr; +} + +extern "C" HRESULT BuffWriteStream( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_bcount(cbStream) const BYTE* pbStream, + __in SIZE_T cbStream + ) +{ + Assert(ppbBuffer); + Assert(piBuffer); + Assert(pbStream); + + HRESULT hr = S_OK; + SIZE_T cb = cbStream; + errno_t err = 0; + + // make sure we have a buffer with sufficient space + hr = EnsureBufferSize(ppbBuffer, *piBuffer + cbStream + sizeof(SIZE_T)); + BuffExitOnFailure(hr, "Failed to ensure buffer size."); + + // copy byte count to buffer + *(SIZE_T*)(*ppbBuffer + *piBuffer) = cb; + *piBuffer += sizeof(SIZE_T); + + // copy data to buffer + err = memcpy_s(*ppbBuffer + *piBuffer, cbStream, pbStream, cbStream); + if (err) + { + BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write stream to buffer, error: %d", err); + } + + *piBuffer += cbStream; + +LExit: + return hr; +} + + +// helper functions + +static HRESULT EnsureBufferSize( + __deref_inout_bcount(cbSize) BYTE** ppbBuffer, + __in SIZE_T cbSize + ) +{ + HRESULT hr = S_OK; + SIZE_T cbTarget = ((cbSize / BUFFER_INCREMENT) + 1) * BUFFER_INCREMENT; + + if (*ppbBuffer) + { + if (MemSize(*ppbBuffer) < cbTarget) + { + LPVOID pv = MemReAlloc(*ppbBuffer, cbTarget, TRUE); + BuffExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate buffer."); + *ppbBuffer = (BYTE*)pv; + } + } + else + { + *ppbBuffer = (BYTE*)MemAlloc(cbTarget, TRUE); + BuffExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer."); + } + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/build/WixToolset.DUtil.props b/src/libs/dutil/WixToolset.DUtil/build/WixToolset.DUtil.props new file mode 100644 index 00000000..35749be8 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/build/WixToolset.DUtil.props @@ -0,0 +1,28 @@ + + + + + + + $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) + + + $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) + + + + + $(MSBuildThisFileDirectory)native\v140\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) + + + + + $(MSBuildThisFileDirectory)native\v141\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) + + + + + $(MSBuildThisFileDirectory)native\v142\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies) + + + diff --git a/src/libs/dutil/WixToolset.DUtil/butil.cpp b/src/libs/dutil/WixToolset.DUtil/butil.cpp new file mode 100644 index 00000000..e04b52e9 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/butil.cpp @@ -0,0 +1,257 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define ButilExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__) +#define ButilExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__) +#define ButilExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__) +#define ButilExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__) +#define ButilExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUTIL, e, x, s, __VA_ARGS__) + +// constants +// From engine/registration.h +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; + +// Forward declarations. +/******************************************************************** +OpenBundleKey - Opens the bundle uninstallation key for a given bundle + +NOTE: caller is responsible for closing key +********************************************************************/ +static HRESULT OpenBundleKey( + __in_z LPCWSTR wzBundleId, + __in BUNDLE_INSTALL_CONTEXT context, + __inout HKEY *key); + + +extern "C" HRESULT DAPI BundleGetBundleInfo( + __in_z LPCWSTR wzBundleId, + __in_z LPCWSTR wzAttribute, + __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, + __inout_opt LPDWORD pcchValueBuf + ) +{ + Assert(wzBundleId && wzAttribute); + + HRESULT hr = S_OK; + BUNDLE_INSTALL_CONTEXT context = BUNDLE_INSTALL_CONTEXT_MACHINE; + LPWSTR sczValue = NULL; + HKEY hkBundle = NULL; + DWORD cchSource = 0; + DWORD dwType = 0; + DWORD dwValue = 0; + + if ((lpValueBuf && !pcchValueBuf) || !wzBundleId || !wzAttribute) + { + ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); + } + + if (FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_MACHINE, &hkBundle)) && + FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_USER, &hkBundle))) + { + ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path."); + } + + // If the bundle doesn't have the property defined, return ERROR_UNKNOWN_PROPERTY + hr = RegGetType(hkBundle, wzAttribute, &dwType); + ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) : hr, "Failed to locate bundle property."); + + switch (dwType) + { + case REG_SZ: + hr = RegReadString(hkBundle, wzAttribute, &sczValue); + ButilExitOnFailure(hr, "Failed to read string property."); + break; + case REG_DWORD: + hr = RegReadNumber(hkBundle, wzAttribute, &dwValue); + ButilExitOnFailure(hr, "Failed to read dword property."); + + hr = StrAllocFormatted(&sczValue, L"%d", dwValue); + ButilExitOnFailure(hr, "Failed to format dword property as string."); + break; + default: + ButilExitOnFailure(hr = E_NOTIMPL, "Reading bundle info of type 0x%x not implemented.", dwType); + + } + + hr = ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); + ButilExitOnFailure(hr, "Failed to calculate length of string"); + + if (lpValueBuf) + { + // cchSource is the length of the string not including the terminating null character + if (*pcchValueBuf <= cchSource) + { + *pcchValueBuf = ++cchSource; + ButilExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA), "A buffer is too small to hold the requested data."); + } + + hr = ::StringCchCatNExW(lpValueBuf, *pcchValueBuf, sczValue, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer."); + + *pcchValueBuf = cchSource++; + } + +LExit: + ReleaseRegKey(hkBundle); + ReleaseStr(sczValue); + + return hr; +} + +HRESULT DAPI BundleEnumRelatedBundle( + __in_z LPCWSTR wzUpgradeCode, + __in BUNDLE_INSTALL_CONTEXT context, + __inout PDWORD pdwStartIndex, + __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf + ) +{ + HRESULT hr = S_OK; + HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + HKEY hkUninstall = NULL; + HKEY hkBundle = NULL; + LPWSTR sczUninstallSubKey = NULL; + DWORD cchUninstallSubKey = 0; + LPWSTR sczUninstallSubKeyPath = NULL; + LPWSTR sczValue = NULL; + DWORD dwType = 0; + + LPWSTR* rgsczBundleUpgradeCodes = NULL; + DWORD cBundleUpgradeCodes = 0; + BOOL fUpgradeCodeFound = FALSE; + + if (!wzUpgradeCode || !lpBundleIdBuf || !pdwStartIndex) + { + ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); + } + + hr = RegOpen(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstall); + ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); + + for (DWORD dwIndex = *pdwStartIndex; !fUpgradeCodeFound; dwIndex++) + { + hr = RegKeyEnum(hkUninstall, dwIndex, &sczUninstallSubKey); + ButilExitOnFailure(hr, "Failed to enumerate bundle uninstall key path."); + + hr = StrAllocFormatted(&sczUninstallSubKeyPath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, sczUninstallSubKey); + ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); + + hr = RegOpen(hkRoot, sczUninstallSubKeyPath, KEY_READ, &hkBundle); + ButilExitOnFailure(hr, "Failed to open uninstall key path."); + + // If it's a bundle, it should have a BundleUpgradeCode value of type REG_SZ (old) or REG_MULTI_SZ + hr = RegGetType(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &dwType); + if (FAILED(hr)) + { + ReleaseRegKey(hkBundle); + ReleaseNullStr(sczUninstallSubKey); + ReleaseNullStr(sczUninstallSubKeyPath); + // Not a bundle + continue; + } + + switch (dwType) + { + case REG_SZ: + hr = RegReadString(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &sczValue); + ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode string property."); + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, sczValue, -1, wzUpgradeCode, -1)) + { + *pdwStartIndex = dwIndex; + fUpgradeCodeFound = TRUE; + break; + } + + ReleaseNullStr(sczValue); + + break; + case REG_MULTI_SZ: + hr = RegReadStringArray(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczBundleUpgradeCodes, &cBundleUpgradeCodes); + ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode multi-string property."); + + for (DWORD i = 0; i < cBundleUpgradeCodes; i++) + { + LPWSTR wzBundleUpgradeCode = rgsczBundleUpgradeCodes[i]; + if (wzBundleUpgradeCode && *wzBundleUpgradeCode) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzBundleUpgradeCode, -1, wzUpgradeCode, -1)) + { + *pdwStartIndex = dwIndex; + fUpgradeCodeFound = TRUE; + break; + } + } + } + ReleaseNullStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes); + + break; + + default: + ButilExitOnFailure(hr = E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType); + + } + + if (fUpgradeCodeFound) + { + if (lpBundleIdBuf) + { + hr = ::StringCchLengthW(sczUninstallSubKey, STRSAFE_MAX_CCH, reinterpret_cast(&cchUninstallSubKey)); + ButilExitOnFailure(hr, "Failed to calculate length of string"); + + hr = ::StringCchCopyNExW(lpBundleIdBuf, MAX_GUID_CHARS + 1, sczUninstallSubKey, cchUninstallSubKey, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer."); + } + + break; + } + + // Cleanup before next iteration + ReleaseRegKey(hkBundle); + ReleaseNullStr(sczUninstallSubKey); + ReleaseNullStr(sczUninstallSubKeyPath); + } + +LExit: + ReleaseStr(sczValue); + ReleaseStr(sczUninstallSubKey); + ReleaseStr(sczUninstallSubKeyPath); + ReleaseRegKey(hkBundle); + ReleaseRegKey(hkUninstall); + ReleaseStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes); + + return hr; +} + + +HRESULT OpenBundleKey( + __in_z LPCWSTR wzBundleId, + __in BUNDLE_INSTALL_CONTEXT context, + __inout HKEY *key) +{ + Assert(key && wzBundleId); + AssertSz(NULL == *key, "*key should be null"); + + HRESULT hr = S_OK; + HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + LPWSTR sczKeypath = NULL; + + hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId); + ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); + + hr = RegOpen(hkRoot, sczKeypath, KEY_READ, key); + ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); + +LExit: + ReleaseStr(sczKeypath); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp b/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp new file mode 100644 index 00000000..93a9b7e1 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp @@ -0,0 +1,1539 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define CabcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__) +#define CabcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__) +#define CabcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__) +#define CabcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__) +#define CabcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__) +#define CabcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABCUTIL, e, x, s, __VA_ARGS__) +#define CabcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABCUTIL, g, x, s, __VA_ARGS__) + + +static const WCHAR CABC_MAGIC_UNICODE_STRING_MARKER = '?'; +static const DWORD MAX_CABINET_HEADER_SIZE = 16 * 1024 * 1024; + +// The minimum number of uncompressed bytes between FciFlushFolder() calls - if we call FciFlushFolder() +// too often (because of duplicates too close together) we theoretically ruin our compression ratio - +// left at zero to maximize install-time performance, because even a small minimum threshhold seems to +// have a high install-time performance cost for little or no size benefit. The value is left here for +// tweaking though - possible suggested values are 524288 for 512K, or 2097152 for 2MB. +static const DWORD MINFLUSHTHRESHHOLD = 0; + +// structs +struct MS_CABINET_HEADER +{ + DWORD sig; + DWORD csumHeader; + DWORD cbCabinet; + DWORD csumFolders; + DWORD coffFiles; + DWORD csumFiles; + WORD version; + WORD cFolders; + WORD cFiles; + WORD flags; + WORD setID; + WORD iCabinet; +}; + + +struct MS_CABINET_ITEM +{ + DWORD cbFile; + DWORD uoffFolderStart; + WORD iFolder; + WORD date; + WORD time; + WORD attribs; +}; + +struct CABC_INTERNAL_ADDFILEINFO +{ + LPCWSTR wzSourcePath; + LPCWSTR wzEmptyPath; +}; + +struct CABC_DUPLICATEFILE +{ + DWORD dwFileArrayIndex; + DWORD dwDuplicateCabFileIndex; + LPWSTR pwzSourcePath; + LPWSTR pwzToken; +}; + + +struct CABC_FILE +{ + DWORD dwCabFileIndex; + LPWSTR pwzSourcePath; + LPWSTR pwzToken; + PMSIFILEHASHINFO pmfHash; + LONGLONG llFileSize; + BOOL fHasDuplicates; +}; + + +struct CABC_DATA +{ + LONGLONG llBytesSinceLastFlush; + LONGLONG llFlushThreshhold; + + STRINGDICT_HANDLE shDictHandle; + + WCHAR wzCabinetPath[MAX_PATH]; + WCHAR wzEmptyFile[MAX_PATH]; + HANDLE hEmptyFile; + DWORD dwLastFileIndex; + + DWORD cFilePaths; + DWORD cMaxFilePaths; + CABC_FILE *prgFiles; + + DWORD cDuplicates; + DWORD cMaxDuplicates; + CABC_DUPLICATEFILE *prgDuplicates; + + HRESULT hrLastError; + BOOL fGoodCab; + + HFCI hfci; + ERF erf; + CCAB ccab; + TCOMP tc; + + // Below Field are used for Cabinet Splitting + BOOL fCabinetSplittingEnabled; + FileSplitCabNamesCallback fileSplitCabNamesCallback; + WCHAR wzFirstCabinetName[MAX_PATH]; // Stores Name of First Cabinet excluding ".cab" extention to help generate other names by Splitting +}; + +const int CABC_HANDLE_BYTES = sizeof(CABC_DATA); + +// +// prototypes +// +static void FreeCabCData( + __in CABC_DATA* pcd + ); +static HRESULT CheckForDuplicateFile( + __in CABC_DATA *pcd, + __out CABC_FILE **ppcf, + __in LPCWSTR wzFileName, + __in PMSIFILEHASHINFO *ppmfHash, + __in LONGLONG llFileSize + ); +static HRESULT AddDuplicateFile( + __in CABC_DATA *pcd, + __in DWORD dwFileArrayIndex, + __in_z LPCWSTR wzSourcePath, + __in_opt LPCWSTR wzToken, + __in DWORD dwDuplicateCabFileIndex + ); +static HRESULT AddNonDuplicateFile( + __in CABC_DATA *pcd, + __in LPCWSTR wzFile, + __in_opt LPCWSTR wzToken, + __in_opt const MSIFILEHASHINFO* pmfHash, + __in LONGLONG llFileSize, + __in DWORD dwCabFileIndex + ); +static HRESULT UpdateDuplicateFiles( + __in const CABC_DATA *pcd + ); +static HRESULT DuplicateFile( + __in MS_CABINET_HEADER *pHeader, + __in const CABC_DATA *pcd, + __in const CABC_DUPLICATEFILE *pDuplicate + ); +static HRESULT UtcFileTimeToLocalDosDateTime( + __in const FILETIME* pFileTime, + __out USHORT* pDate, + __out USHORT* pTime + ); + +static __callback int DIAMONDAPI CabCFilePlaced(__in PCCAB pccab, __in_z PSTR szFile, __in long cbFile, __in BOOL fContinuation, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback void * DIAMONDAPI CabCAlloc(__in ULONG cb); +static __callback void DIAMONDAPI CabCFree(__out_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback INT_PTR DIAMONDAPI CabCOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback UINT FAR DIAMONDAPI CabCRead(__in INT_PTR hf, __out_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback UINT FAR DIAMONDAPI CabCWrite(__in INT_PTR hf, __in_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback long FAR DIAMONDAPI CabCSeek(__in INT_PTR hf, __in long dist, __in int seektype, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback int FAR DIAMONDAPI CabCClose(__in INT_PTR hf, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback int DIAMONDAPI CabCDelete(__in_z PSTR szFile, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetTempFile(__out_bcount_z(cbFile) char *szFile, __in int cbFile, __inout_bcount(CABC_HANDLE_BYTES) void *pv); +__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetNextCabinet(__in PCCAB pccab, __in ULONG ul, __out_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback INT_PTR DIAMONDAPI CabCGetOpenInfo(__in_z PSTR pszName, __out USHORT *pdate, __out USHORT *ptime, __out USHORT *pattribs, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv); +static __callback long DIAMONDAPI CabCStatus(__in UINT uiTypeStatus, __in ULONG cb1, __in ULONG cb2, __inout_bcount(CABC_HANDLE_BYTES) void *pv); + + +/******************************************************************** +CabcBegin - begins creating a cabinet + +NOTE: phContext must be the same handle used in AddFile and Finish. + wzCabDir can be L"", but not NULL. + dwMaxSize and dwMaxThresh can be 0. A large default value will be used in that case. + +********************************************************************/ +extern "C" HRESULT DAPI CabCBegin( + __in_z LPCWSTR wzCab, + __in_z LPCWSTR wzCabDir, + __in DWORD dwMaxFiles, + __in DWORD dwMaxSize, + __in DWORD dwMaxThresh, + __in COMPRESSION_TYPE ct, + __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext + ) +{ + Assert(wzCab && *wzCab && phContext); + + HRESULT hr = S_OK; + CABC_DATA *pcd = NULL; + WCHAR wzTempPath[MAX_PATH] = { }; + + C_ASSERT(sizeof(MSIFILEHASHINFO) == 20); + + WCHAR wzPathBuffer [MAX_PATH] = L""; + size_t cchPathBuffer; + if (wzCabDir) + { + hr = ::StringCchLengthW(wzCabDir, MAX_PATH, &cchPathBuffer); + CabcExitOnFailure(hr, "Failed to get length of cab directory"); + + // Need room to terminate with L'\\' and L'\0' + if((MAX_PATH - 1) <= cchPathBuffer || 0 == cchPathBuffer) + { + hr = E_INVALIDARG; + CabcExitOnFailure(hr, "Cab directory had invalid length: %u", cchPathBuffer); + } + + hr = ::StringCchCopyW(wzPathBuffer, countof(wzPathBuffer), wzCabDir); + CabcExitOnFailure(hr, "Failed to copy cab directory to buffer"); + + if (L'\\' != wzPathBuffer[cchPathBuffer - 1]) + { + hr = ::StringCchCatW(wzPathBuffer, countof(wzPathBuffer), L"\\"); + CabcExitOnFailure(hr, "Failed to cat \\ to end of buffer"); + ++cchPathBuffer; + } + } + + pcd = static_cast(MemAlloc(sizeof(CABC_DATA), TRUE)); + CabcExitOnNull(pcd, hr, E_OUTOFMEMORY, "failed to allocate cab creation data structure"); + + pcd->hrLastError = S_OK; + pcd->fGoodCab = TRUE; + pcd->llFlushThreshhold = MINFLUSHTHRESHHOLD; + + pcd->hEmptyFile = INVALID_HANDLE_VALUE; + + pcd->fileSplitCabNamesCallback = NULL; + + if (NULL == dwMaxSize) + { + pcd->ccab.cb = CAB_MAX_SIZE; + pcd->fCabinetSplittingEnabled = FALSE; // If no max cab size is supplied, cabinet splitting is not desired + } + else + { + pcd->ccab.cb = dwMaxSize * 1024 * 1024; + pcd->fCabinetSplittingEnabled = TRUE; + } + + if (0 == dwMaxThresh) + { + // Subtract 16 to magically make cabbing of uncompressed data larger than 2GB work. + pcd->ccab.cbFolderThresh = CAB_MAX_SIZE - 16; + } + else + { + pcd->ccab.cbFolderThresh = dwMaxThresh; + } + + // Translate the compression type + if (COMPRESSION_TYPE_NONE == ct) + { + pcd->tc = tcompTYPE_NONE; + } + else if (COMPRESSION_TYPE_LOW == ct) + { + pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_LO; + } + else if (COMPRESSION_TYPE_MEDIUM == ct) + { + pcd->tc = TCOMPfromLZXWindow(18); + } + else if (COMPRESSION_TYPE_HIGH == ct) + { + pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_HI; + } + else if (COMPRESSION_TYPE_MSZIP == ct) + { + pcd->tc = tcompTYPE_MSZIP; + } + else + { + hr = E_INVALIDARG; + CabcExitOnFailure(hr, "Invalid compression type specified."); + } + + if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzCab, -1, pcd->ccab.szCab, sizeof(pcd->ccab.szCab), NULL, NULL)) + { + CabcExitWithLastError(hr, "failed to convert cab name to multi-byte"); + } + + if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzPathBuffer, -1, pcd->ccab.szCabPath, sizeof(pcd->ccab.szCab), NULL, NULL)) + { + CabcExitWithLastError(hr, "failed to convert cab dir to multi-byte"); + } + + // Remember the path to the cabinet. + hr= ::StringCchCopyW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzPathBuffer); + CabcExitOnFailure(hr, "Failed to copy cabinet path from path: %ls", wzPathBuffer); + + hr = ::StringCchCatW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzCab); + CabcExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab); + + // Get the empty file to use as the blank marker for duplicates. + if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) + { + CabcExitWithLastError(hr, "Failed to get temp path."); + } + + if (!::GetTempFileNameW(wzTempPath, L"WSC", 0, pcd->wzEmptyFile)) + { + CabcExitWithLastError(hr, "Failed to create a temp file name."); + } + + // Try to open the newly created empty file (remember, GetTempFileName() is kind enough to create a file for us) + // with a handle to automatically delete the file on close. Ignore any failure that might happen, since the worst + // case is we'll leave a zero byte file behind in the temp folder. + pcd->hEmptyFile = ::CreateFileW(pcd->wzEmptyFile, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); + + hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_CASEINSENSITIVE); + CabcExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files"); + + // Make sure to allocate at least some space, or we won't be able to realloc later if they "lied" about having zero files + if (1 > dwMaxFiles) + { + dwMaxFiles = 1; + } + + pcd->cMaxFilePaths = dwMaxFiles; + size_t cbFileAllocSize = 0; + + hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &(cbFileAllocSize)); + CabcExitOnFailure(hr, "Maximum allocation exceeded on initialization."); + + pcd->prgFiles = static_cast(MemAlloc(cbFileAllocSize, TRUE)); + CabcExitOnNull(pcd->prgFiles, hr, E_OUTOFMEMORY, "Failed to allocate memory for files."); + + // Tell cabinet API about our configuration. + pcd->hfci = ::FCICreate(&(pcd->erf), CabCFilePlaced, CabCAlloc, CabCFree, CabCOpen, CabCRead, CabCWrite, CabCClose, CabCSeek, CabCDelete, CabCGetTempFile, &(pcd->ccab), pcd); + if (NULL == pcd->hfci || pcd->erf.fError) + { + // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error + if (FAILED(pcd->hrLastError)) + { + hr = pcd->hrLastError; + } + else + { + CabcExitWithLastError(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); + } + + pcd->fGoodCab = FALSE; + + CabcExitOnFailure(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); // TODO: can these be converted to HRESULTS? + } + + *phContext = pcd; + +LExit: + if (FAILED(hr) && pcd && pcd->hfci) + { + ::FCIDestroy(pcd->hfci); + } + + return hr; +} + + +/******************************************************************** +CabCNextCab - This will be useful when creating multiple cabs. +Haven't needed it yet. +********************************************************************/ +extern "C" HRESULT DAPI CabCNextCab( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ) +{ + UNREFERENCED_PARAMETER(hContext); + // TODO: Make the appropriate FCIFlushCabinet and FCIFlushFolder calls + return E_NOTIMPL; +} + + +/******************************************************************** +CabcAddFile - adds a file to a cabinet + +NOTE: hContext must be the same used in Begin and Finish +if wzToken is null, the file's original name is used within the cab +********************************************************************/ +extern "C" HRESULT DAPI CabCAddFile( + __in_z LPCWSTR wzFile, + __in_z_opt LPCWSTR wzToken, + __in_opt PMSIFILEHASHINFO pmfHash, + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ) +{ + Assert(wzFile && *wzFile && hContext); + + HRESULT hr = S_OK; + CABC_DATA *pcd = reinterpret_cast(hContext); + CABC_FILE *pcfDuplicate = NULL; + LONGLONG llFileSize = 0; + PMSIFILEHASHINFO pmfLocalHash = pmfHash; + + // Use Smart Cabbing if there are duplicates and if Cabinet Splitting is not desired + // For Cabinet Spliting avoid hashing as Smart Cabbing is disabled + if(!pcd->fCabinetSplittingEnabled) + { + // Store file size, primarily used to determine which files to hash for duplicates + hr = FileSize(wzFile, &llFileSize); + CabcExitOnFailure(hr, "Failed to check size of file %ls", wzFile); + + hr = CheckForDuplicateFile(pcd, &pcfDuplicate, wzFile, &pmfLocalHash, llFileSize); + CabcExitOnFailure(hr, "Failed while checking for duplicate of file: %ls", wzFile); + } + + if (pcfDuplicate) // This will be null for smart cabbing case + { + DWORD index; + hr = ::PtrdiffTToDWord(pcfDuplicate - pcd->prgFiles, &index); + CabcExitOnFailure(hr, "Failed to calculate index of file name: %ls", pcfDuplicate->pwzSourcePath); + + hr = AddDuplicateFile(pcd, index, wzFile, wzToken, pcd->dwLastFileIndex); + CabcExitOnFailure(hr, "Failed to add duplicate of file name: %ls", pcfDuplicate->pwzSourcePath); + } + else + { + hr = AddNonDuplicateFile(pcd, wzFile, wzToken, pmfLocalHash, llFileSize, pcd->dwLastFileIndex); + CabcExitOnFailure(hr, "Failed to add non-duplicated file: %ls", wzFile); + } + + ++pcd->dwLastFileIndex; + +LExit: + // If we allocated a hash struct ourselves, free it + if (pmfHash != pmfLocalHash) + { + ReleaseMem(pmfLocalHash); + } + + return hr; +} + + +/******************************************************************** +CabcFinish - finishes making a cabinet + +NOTE: hContext must be the same used in Begin and AddFile +*********************************************************************/ +extern "C" HRESULT DAPI CabCFinish( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext, + __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback + ) +{ + Assert(hContext); + + HRESULT hr = S_OK; + CABC_DATA *pcd = reinterpret_cast(hContext); + CABC_INTERNAL_ADDFILEINFO fileInfo = { }; + DWORD dwCabFileIndex; // Total file index, counts up to pcd->dwLastFileIndex + DWORD dwArrayFileIndex = 0; // Index into pcd->prgFiles[] array + DWORD dwDupeArrayFileIndex = 0; // Index into pcd->prgDuplicates[] array + LPSTR pszFileToken = NULL; + LONGLONG llFileSize = 0; + + pcd->fileSplitCabNamesCallback = fileSplitCabNamesCallback; + + // These are used to determine whether to call FciFlushFolder() before or after the next call to FciAddFile() + // doing so at appropriate times results in install-time performance benefits in the case of duplicate files. + // Basically, when MSI has to extract files out of order (as it does due to our smart cabbing), it can't just jump + // exactly to the out of order file, it must begin extracting all over again, starting from that file's CAB folder + // (this is not the same as a regular folder, and is a concept unique to CABs). + + // This means MSI spends a lot of time extracting the same files twice, especially if the duplicate file has many files + // before it in the CAB folder. To avoid this, we want to make sure whenever MSI jumps to another file in the CAB, that + // file is at the beginning of its own folder, so no extra files need to be extracted. FciFlushFolder() causes the CAB + // to close the current folder, and create a new folder for the next file to be added. + + // So to maximize our performance benefit, we must call FciFlushFolder() at every place MSI will jump "to" in the CAB sequence. + // So, we call FciFlushFolder() before adding the original version of a duplicated file (as this will be jumped "to") + // And we call FciFlushFolder() after adding the duplicate versions of files (as this will be jumped back "to" to get back in the regular sequence) + BOOL fFlushBefore = FALSE; + BOOL fFlushAfter = FALSE; + + ReleaseDict(pcd->shDictHandle); + + // We need to go through all the files, duplicates and non-duplicates, sequentially in the order they were added + for (dwCabFileIndex = 0; dwCabFileIndex < pcd->dwLastFileIndex; ++dwCabFileIndex) + { + if (dwArrayFileIndex < pcd->cMaxFilePaths && pcd->prgFiles[dwArrayFileIndex].dwCabFileIndex == dwCabFileIndex) // If it's a non-duplicate file + { + // Just a normal, non-duplicated file. We'll add it to the list for later checking of + // duplicates. + fileInfo.wzSourcePath = pcd->prgFiles[dwArrayFileIndex].pwzSourcePath; + fileInfo.wzEmptyPath = NULL; + + // Use the provided token, otherwise default to the source file name. + if (pcd->prgFiles[dwArrayFileIndex].pwzToken) + { + LPCWSTR pwzTemp = pcd->prgFiles[dwArrayFileIndex].pwzToken; + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); + CabcExitOnFailure(hr, "failed to convert file token to ANSI: %ls", pwzTemp); + } + else + { + LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath); + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); + CabcExitOnFailure(hr, "failed to convert file name to ANSI: %ls", pwzTemp); + } + + if (pcd->prgFiles[dwArrayFileIndex].fHasDuplicates) + { + fFlushBefore = TRUE; + } + + llFileSize = pcd->prgFiles[dwArrayFileIndex].llFileSize; + + ++dwArrayFileIndex; // Increment into the non-duplicate array + } + else if (dwDupeArrayFileIndex < pcd->cMaxDuplicates && pcd->prgDuplicates[dwDupeArrayFileIndex].dwDuplicateCabFileIndex == dwCabFileIndex) // If it's a duplicate file + { + // For duplicate files, we point them at our empty (zero-byte) file so it takes up no space + // in the resultant cabinet. Later on (CabCFinish) we'll go through and change all the zero + // byte files to point at their duplicated file index. + // + // Notice that duplicate files are not added to the list of file paths because all duplicate + // files point at the same path (the empty file) so there is no point in tracking them with + // their path. + fileInfo.wzSourcePath = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzSourcePath; + fileInfo.wzEmptyPath = pcd->wzEmptyFile; + + // Use the provided token, otherwise default to the source file name. + if (pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken) + { + LPCWSTR pwzTemp = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken; + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); + CabcExitOnFailure(hr, "failed to convert duplicate file token to ANSI: %ls", pwzTemp); + } + else + { + LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath); + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP); + CabcExitOnFailure(hr, "failed to convert duplicate file name to ANSI: %ls", pwzTemp); + } + + // Flush afterward only if this isn't a duplicate of the previous file, and at least one non-duplicate file remains to be added to the cab + if (!(dwCabFileIndex - 1 == pcd->prgFiles[pcd->prgDuplicates[dwDupeArrayFileIndex].dwFileArrayIndex].dwCabFileIndex) && + !(dwDupeArrayFileIndex > 0 && dwCabFileIndex - 1 == pcd->prgDuplicates[dwDupeArrayFileIndex - 1].dwDuplicateCabFileIndex) && + dwArrayFileIndex < pcd->cFilePaths) + { + fFlushAfter = TRUE; + } + + // We're just adding a 0-byte file, so set it appropriately + llFileSize = 0; + + ++dwDupeArrayFileIndex; // Increment into the duplicate array + } + else // If it's neither duplicate nor non-duplicate, throw an error + { + hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT); + CabcExitOnRootFailure(hr, "Internal inconsistency in data structures while creating CAB file - a non-standard, non-duplicate file was encountered"); + } + + if (fFlushBefore && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold) + { + if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus)) + { + CabcExitWithLastError(hr, "failed to flush FCI folder before adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); + } + pcd->llBytesSinceLastFlush = 0; + } + + pcd->llBytesSinceLastFlush += llFileSize; + + // Add the file to the cab. Notice that we are passing our CABC_INTERNAL_ADDFILEINFO struct + // through the pointer to an ANSI string. This is neccessary so we can smuggle through the + // path to the empty file (should this be a duplicate file). +#pragma prefast(push) +#pragma prefast(disable:6387) // OACR is silly, pszFileToken can't be false here + if (!::FCIAddFile(pcd->hfci, reinterpret_cast(&fileInfo), pszFileToken, FALSE, CabCGetNextCabinet, CabCStatus, CabCGetOpenInfo, pcd->tc)) +#pragma prefast(pop) + { + pcd->fGoodCab = FALSE; + + // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error + if (FAILED(pcd->hrLastError)) + { + hr = pcd->hrLastError; + } + else + { + CabcExitWithLastError(hr, "failed to add file to FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); + } + + CabcExitOnFailure(hr, "failed to add file to FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); // TODO: can these be converted to HRESULTS? + } + + // For Cabinet Splitting case, check for pcd->hrLastError that may be set as result of Error in CabCGetNextCabinet + // This is required as returning False in CabCGetNextCabinet is not aborting cabinet creation and is reporting success instead + if (pcd->fCabinetSplittingEnabled && FAILED(pcd->hrLastError)) + { + hr = pcd->hrLastError; + CabcExitOnFailure(hr, "Failed to create next cabinet name while splitting cabinet."); + } + + if (fFlushAfter && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold) + { + if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus)) + { + CabcExitWithLastError(hr, "failed to flush FCI folder after adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); + } + pcd->llBytesSinceLastFlush = 0; + } + + fFlushAfter = FALSE; + fFlushBefore = FALSE; + } + + if (!pcd->fGoodCab) + { + // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error + if (FAILED(pcd->hrLastError)) + { + hr = pcd->hrLastError; + } + else + { + CabcExitWithLastError(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); + } + + CabcExitOnFailure(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); // TODO: can these be converted to HRESULTS? + } + + // Only flush the cabinet if we actually succeeded in previous calls - otherwise we just waste time (a lot on big cabs) + if (!::FCIFlushCabinet(pcd->hfci, FALSE, CabCGetNextCabinet, CabCStatus)) + { + // If we have a last error, use that, otherwise return the useless error + hr = FAILED(pcd->hrLastError) ? pcd->hrLastError : E_FAIL; + CabcExitOnFailure(hr, "failed to flush FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); // TODO: can these be converted to HRESULTS? + } + + if (pcd->fGoodCab && pcd->cDuplicates) + { + hr = UpdateDuplicateFiles(pcd); + CabcExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->wzCabinetPath); + } + +LExit: + ::FCIDestroy(pcd->hfci); + FreeCabCData(pcd); + ReleaseNullStr(pszFileToken); + + return hr; +} + + +/******************************************************************** +CabCCancel - cancels making a cabinet + +NOTE: hContext must be the same used in Begin and AddFile +*********************************************************************/ +extern "C" void DAPI CabCCancel( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ) +{ + Assert(hContext); + + CABC_DATA* pcd = reinterpret_cast(hContext); + ::FCIDestroy(pcd->hfci); + FreeCabCData(pcd); +} + + +// +// private +// + +static void FreeCabCData( + __in CABC_DATA* pcd + ) +{ + if (pcd) + { + ReleaseFileHandle(pcd->hEmptyFile); + + for (DWORD i = 0; i < pcd->cFilePaths; ++i) + { + ReleaseStr(pcd->prgFiles[i].pwzSourcePath); + ReleaseMem(pcd->prgFiles[i].pmfHash); + } + ReleaseMem(pcd->prgFiles); + ReleaseMem(pcd->prgDuplicates); + + ReleaseMem(pcd); + } +} + +/******************************************************************** + SmartCab functions + +********************************************************************/ + +static HRESULT CheckForDuplicateFile( + __in CABC_DATA *pcd, + __out CABC_FILE **ppcf, + __in LPCWSTR wzFileName, + __in PMSIFILEHASHINFO *ppmfHash, + __in LONGLONG llFileSize + ) +{ + DWORD i; + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + CabcExitOnNull(ppcf, hr, E_INVALIDARG, "No file structure sent while checking for duplicate file"); + CabcExitOnNull(ppmfHash, hr, E_INVALIDARG, "No file hash structure pointer sent while checking for duplicate file"); + + *ppcf = NULL; // By default, we'll set our output to NULL + + hr = DictGetValue(pcd->shDictHandle, wzFileName, reinterpret_cast(ppcf)); + // If we found it in the hash of previously added source paths, return our match immediately + if (SUCCEEDED(hr)) + { + ExitFunction1(hr = S_OK); + } + else if (E_NOTFOUND == hr) + { + hr = S_OK; + } + CabcExitOnFailure(hr, "Failed while searching for file in dictionary of previously added files"); + + for (i = 0; i < pcd->cFilePaths; ++i) + { + // If two files have the same size, use hashing to check if they're a match + if (llFileSize == pcd->prgFiles[i].llFileSize) + { + // If pcd->prgFiles[i], our potential match, hasn't been hashed yet, hash it + if (pcd->prgFiles[i].pmfHash == NULL) + { + pcd->prgFiles[i].pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); + CabcExitOnNull(pcd->prgFiles[i].pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for candidate duplicate file's MSI file hash"); + + pcd->prgFiles[i].pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + er = ::MsiGetFileHashW(pcd->prgFiles[i].pwzSourcePath, 0, pcd->prgFiles[i].pmfHash); + CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of candidate duplicate file: %ls", pcd->prgFiles[i].pwzSourcePath); + } + + // If our own file hasn't yet been hashed, hash it + if (NULL == *ppmfHash) + { + *ppmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); + CabcExitOnNull(*ppmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for file's MSI file hash"); + + (*ppmfHash)->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + er = ::MsiGetFileHashW(wzFileName, 0, *ppmfHash); + CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of file: %ls", pcd->prgFiles[i].pwzSourcePath); + } + + // If the two file hashes are both of the expected size, and they match, we've got a match, so return it! + if (pcd->prgFiles[i].pmfHash->dwFileHashInfoSize == (*ppmfHash)->dwFileHashInfoSize && + sizeof(MSIFILEHASHINFO) == (*ppmfHash)->dwFileHashInfoSize && + pcd->prgFiles[i].pmfHash->dwData[0] == (*ppmfHash)->dwData[0] && + pcd->prgFiles[i].pmfHash->dwData[1] == (*ppmfHash)->dwData[1] && + pcd->prgFiles[i].pmfHash->dwData[2] == (*ppmfHash)->dwData[2] && + pcd->prgFiles[i].pmfHash->dwData[3] == (*ppmfHash)->dwData[3]) + { + *ppcf = pcd->prgFiles + i; + ExitFunction1(hr = S_OK); + } + } + } + +LExit: + + return hr; +} + + +static HRESULT AddDuplicateFile( + __in CABC_DATA *pcd, + __in DWORD dwFileArrayIndex, + __in_z LPCWSTR wzSourcePath, + __in_opt LPCWSTR wzToken, + __in DWORD dwDuplicateCabFileIndex + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + + // Ensure there is enough memory to store this duplicate file index. + if (pcd->cDuplicates == pcd->cMaxDuplicates) + { + pcd->cMaxDuplicates += 20; // grow by a reasonable number (20 is reasonable, right?) + size_t cbDuplicates = 0; + + hr = ::SizeTMult(pcd->cMaxDuplicates, sizeof(CABC_DUPLICATEFILE), &cbDuplicates); + CabcExitOnFailure(hr, "Maximum allocation exceeded."); + + if (pcd->cDuplicates) + { + pv = MemReAlloc(pcd->prgDuplicates, cbDuplicates, FALSE); + CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for duplicate file."); + } + else + { + pv = MemAlloc(cbDuplicates, FALSE); + CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for duplicate file."); + } + + ZeroMemory(reinterpret_cast(pv) + (pcd->cDuplicates * sizeof(CABC_DUPLICATEFILE)), (pcd->cMaxDuplicates - pcd->cDuplicates) * sizeof(CABC_DUPLICATEFILE)); + + pcd->prgDuplicates = static_cast(pv); + pv = NULL; + } + + // Store the duplicate file index. + pcd->prgDuplicates[pcd->cDuplicates].dwFileArrayIndex = dwFileArrayIndex; + pcd->prgDuplicates[pcd->cDuplicates].dwDuplicateCabFileIndex = dwDuplicateCabFileIndex; + pcd->prgFiles[dwFileArrayIndex].fHasDuplicates = TRUE; // Mark original file as having duplicates + + hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzSourcePath, wzSourcePath, 0); + CabcExitOnFailure(hr, "Failed to copy duplicate file path: %ls", wzSourcePath); + + if (wzToken && *wzToken) + { + hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzToken, wzToken, 0); + CabcExitOnFailure(hr, "Failed to copy duplicate file token: %ls", wzToken); + } + + ++pcd->cDuplicates; + +LExit: + ReleaseMem(pv); + return hr; +} + + +static HRESULT AddNonDuplicateFile( + __in CABC_DATA *pcd, + __in LPCWSTR wzFile, + __in_opt LPCWSTR wzToken, + __in_opt const MSIFILEHASHINFO* pmfHash, + __in LONGLONG llFileSize, + __in DWORD dwCabFileIndex + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + + // Ensure there is enough memory to store this file index. + if (pcd->cFilePaths == pcd->cMaxFilePaths) + { + pcd->cMaxFilePaths += 100; // grow by a reasonable number (100 is reasonable, right?) + size_t cbFilePaths = 0; + + hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &cbFilePaths); + CabcExitOnFailure(hr, "Maximum allocation exceeded."); + + pv = MemReAlloc(pcd->prgFiles, cbFilePaths, FALSE); + CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for file."); + + ZeroMemory(reinterpret_cast(pv) + (pcd->cFilePaths * sizeof(CABC_FILE)), (pcd->cMaxFilePaths - pcd->cFilePaths) * sizeof(CABC_FILE)); + + pcd->prgFiles = static_cast(pv); + pv = NULL; + } + + // Store the file index information. + // TODO: add this to a sorted list so we can do a binary search later. + CABC_FILE *pcf = pcd->prgFiles + pcd->cFilePaths; + pcf->dwCabFileIndex = dwCabFileIndex; + pcf->llFileSize = llFileSize; + + if (pmfHash && sizeof(MSIFILEHASHINFO) == pmfHash->dwFileHashInfoSize) + { + pcf->pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE); + CabcExitOnNull(pcf->pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for individual file's MSI file hash"); + + pcf->pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + pcf->pmfHash->dwData[0] = pmfHash->dwData[0]; + pcf->pmfHash->dwData[1] = pmfHash->dwData[1]; + pcf->pmfHash->dwData[2] = pmfHash->dwData[2]; + pcf->pmfHash->dwData[3] = pmfHash->dwData[3]; + } + + hr = StrAllocString(&pcf->pwzSourcePath, wzFile, 0); + CabcExitOnFailure(hr, "Failed to copy file path: %ls", wzFile); + + if (wzToken && *wzToken) + { + hr = StrAllocString(&pcf->pwzToken, wzToken, 0); + CabcExitOnFailure(hr, "Failed to copy file token: %ls", wzToken); + } + + ++pcd->cFilePaths; + + hr = DictAddValue(pcd->shDictHandle, pcf); + CabcExitOnFailure(hr, "Failed to add file to dictionary of added files"); + +LExit: + ReleaseMem(pv); + return hr; +} + + +static HRESULT UpdateDuplicateFiles( + __in const CABC_DATA *pcd + ) +{ + HRESULT hr = S_OK; + DWORD cbCabinet = 0; + LARGE_INTEGER liCabinetSize = { }; + HANDLE hCabinet = INVALID_HANDLE_VALUE; + HANDLE hCabinetMapping = NULL; + LPVOID pv = NULL; + MS_CABINET_HEADER *pCabinetHeader = NULL; + + hCabinet = ::CreateFileW(pcd->wzCabinetPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hCabinet) + { + CabcExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->wzCabinetPath); + } + + // Shouldn't need more than 16 MB to get the whole cabinet header into memory so use that as + // the upper bound for the memory map. + if (!::GetFileSizeEx(hCabinet, &liCabinetSize)) + { + CabcExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->wzCabinetPath); + } + + if (0 == liCabinetSize.HighPart && liCabinetSize.LowPart < MAX_CABINET_HEADER_SIZE) + { + cbCabinet = liCabinetSize.LowPart; + } + else + { + cbCabinet = MAX_CABINET_HEADER_SIZE; + } + + // CreateFileMapping() returns NULL on failure, not INVALID_HANDLE_VALUE + hCabinetMapping = ::CreateFileMappingW(hCabinet, NULL, PAGE_READWRITE | SEC_COMMIT, 0, cbCabinet, NULL); + if (NULL == hCabinetMapping || INVALID_HANDLE_VALUE == hCabinetMapping) + { + CabcExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->wzCabinetPath); + } + + pv = ::MapViewOfFile(hCabinetMapping, FILE_MAP_WRITE, 0, 0, 0); + CabcExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->wzCabinetPath); + + pCabinetHeader = static_cast(pv); + + for (DWORD i = 0; i < pcd->cDuplicates; ++i) + { + const CABC_DUPLICATEFILE *pDuplicateFile = pcd->prgDuplicates + i; + + hr = DuplicateFile(pCabinetHeader, pcd, pDuplicateFile); + CabcExitOnFailure(hr, "Failed to find cabinet file items at index: %d and %d", pDuplicateFile->dwFileArrayIndex, pDuplicateFile->dwDuplicateCabFileIndex); + } + +LExit: + if (pv) + { + ::UnmapViewOfFile(pv); + } + if (hCabinetMapping) + { + ::CloseHandle(hCabinetMapping); + } + ReleaseFileHandle(hCabinet); + + return hr; +} + + +static HRESULT DuplicateFile( + __in MS_CABINET_HEADER *pHeader, + __in const CABC_DATA *pcd, + __in const CABC_DUPLICATEFILE *pDuplicate + ) +{ + HRESULT hr = S_OK; + BYTE *pbHeader = reinterpret_cast(pHeader); + BYTE* pbItem = pbHeader + pHeader->coffFiles; + const MS_CABINET_ITEM *pOriginalItem = NULL; + MS_CABINET_ITEM *pDuplicateItem = NULL; + + if (pHeader->cFiles <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex || + pHeader->cFiles <= pDuplicate->dwDuplicateCabFileIndex || + pDuplicate->dwDuplicateCabFileIndex <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex) + { + hr = E_UNEXPECTED; + CabcExitOnFailure(hr, "Unexpected duplicate file indices, header cFiles: %d, file index: %d, duplicate index: %d", pHeader->cFiles, pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex, pDuplicate->dwDuplicateCabFileIndex); + } + + // Step through each cabinet items until we get to the original + // file's index. Notice that the name of the cabinet item is + // appended to the end of the MS_CABINET_INFO, that's why we can't + // index straight to the data we want. + for (DWORD i = 0; i < pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; ++i) + { + LPCSTR szItemName = reinterpret_cast(pbItem + sizeof(MS_CABINET_ITEM)); + pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1; + } + + pOriginalItem = reinterpret_cast(pbItem); + + // Now pick up where we left off after the original file's index + // was found and loop until we find the duplicate file's index. + for (DWORD i = pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; i < pDuplicate->dwDuplicateCabFileIndex; ++i) + { + LPCSTR szItemName = reinterpret_cast(pbItem + sizeof(MS_CABINET_ITEM)); + pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1; + } + + pDuplicateItem = reinterpret_cast(pbItem); + + if (0 != pDuplicateItem->cbFile) + { + hr = E_UNEXPECTED; + CabcExitOnFailure(hr, "Failed because duplicate file does not have a file size of zero: %d", pDuplicateItem->cbFile); + } + + pDuplicateItem->cbFile = pOriginalItem->cbFile; + pDuplicateItem->uoffFolderStart = pOriginalItem->uoffFolderStart; + pDuplicateItem->iFolder = pOriginalItem->iFolder; + // Note: we do *not* duplicate the date/time and attributes metadata from + // the original item to the duplicate. The following lines are commented + // so people are not tempted to put them back. + //pDuplicateItem->date = pOriginalItem->date; + //pDuplicateItem->time = pOriginalItem->time; + //pDuplicateItem->attribs = pOriginalItem->attribs; + +LExit: + return hr; +} + + +static HRESULT UtcFileTimeToLocalDosDateTime( + __in const FILETIME* pFileTime, + __out USHORT* pDate, + __out USHORT* pTime + ) +{ + HRESULT hr = S_OK; + FILETIME ftLocal = { }; + + if (!::FileTimeToLocalFileTime(pFileTime, &ftLocal)) + { + CabcExitWithLastError(hr, "Filed to convert file time to local file time."); + } + + if (!::FileTimeToDosDateTime(&ftLocal, pDate, pTime)) + { + CabcExitWithLastError(hr, "Filed to convert file time to DOS date time."); + } + +LExit: + return hr; +} + + +/******************************************************************** + FCI callback functions + +*********************************************************************/ +static __callback int DIAMONDAPI CabCFilePlaced( + __in PCCAB pccab, + __in_z PSTR szFile, + __in long cbFile, + __in BOOL fContinuation, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + UNREFERENCED_PARAMETER(pccab); + UNREFERENCED_PARAMETER(szFile); + UNREFERENCED_PARAMETER(cbFile); + UNREFERENCED_PARAMETER(fContinuation); + UNREFERENCED_PARAMETER(pv); + return 0; +} + + +static __callback void * DIAMONDAPI CabCAlloc( + __in ULONG cb + ) +{ + return MemAlloc(cb, FALSE); +} + + +static __callback void DIAMONDAPI CabCFree( + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + MemFree(pv); +} + +static __callback INT_PTR DIAMONDAPI CabCOpen( + __in_z PSTR pszFile, + __in int oflag, + __in int pmode, + __out int *err, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + INT_PTR pFile = -1; + DWORD dwAccess = 0; + DWORD dwDisposition = 0; + DWORD dwAttributes = 0; + + // + // Translate flags for CreateFile + // + if (oflag & _O_CREAT) + { + if (pmode == _S_IREAD) + dwAccess |= GENERIC_READ; + else if (pmode == _S_IWRITE) + dwAccess |= GENERIC_WRITE; + else if (pmode == (_S_IWRITE | _S_IREAD)) + dwAccess |= GENERIC_READ | GENERIC_WRITE; + + if (oflag & _O_SHORT_LIVED) + dwDisposition = FILE_ATTRIBUTE_TEMPORARY; + else if (oflag & _O_TEMPORARY) + dwAttributes |= FILE_FLAG_DELETE_ON_CLOSE; + else if (oflag & _O_EXCL) + dwDisposition = CREATE_NEW; + } + if (oflag & _O_TRUNC) + dwDisposition = CREATE_ALWAYS; + + if (!dwAccess) + dwAccess = GENERIC_READ; + if (!dwDisposition) + dwDisposition = OPEN_EXISTING; + if (!dwAttributes) + dwAttributes = FILE_ATTRIBUTE_NORMAL; + + // Check to see if we were passed the magic character that says 'Unicode string follows'. + if (pszFile && CABC_MAGIC_UNICODE_STRING_MARKER == *pszFile) + { + pFile = reinterpret_cast(::CreateFileW(reinterpret_cast(pszFile + 1), dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL)); + } + else + { +#pragma prefast(push) +#pragma prefast(disable:25068) // We intentionally don't use the unicode API here + pFile = reinterpret_cast(::CreateFileA(pszFile, dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL)); +#pragma prefast(pop) + } + + if (INVALID_HANDLE_VALUE == reinterpret_cast(pFile)) + { + CabcExitOnLastError(hr, "failed to open file: %s", pszFile); + } + +LExit: + if (FAILED(hr)) + pcd->hrLastError = *err = hr; + + return FAILED(hr) ? -1 : pFile; +} + + +static __callback UINT FAR DIAMONDAPI CabCRead( + __in INT_PTR hf, + __out_bcount(cb) void FAR *memory, + __in UINT cb, + __out int *err, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + DWORD cbRead = 0; + + CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to read during cabinet extraction because no file handle was provided"); + if (!::ReadFile(reinterpret_cast(hf), memory, cb, &cbRead, NULL)) + { + *err = ::GetLastError(); + CabcExitOnLastError(hr, "failed to read during cabinet extraction"); + } + +LExit: + if (FAILED(hr)) + { + pcd->hrLastError = *err = hr; + } + + return FAILED(hr) ? -1 : cbRead; +} + + +static __callback UINT FAR DIAMONDAPI CabCWrite( + __in INT_PTR hf, + __in_bcount(cb) void FAR *memory, + __in UINT cb, + __out int *err, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + DWORD cbWrite = 0; + + CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to write during cabinet extraction because no file handle was provided"); + if (!::WriteFile(reinterpret_cast(hf), memory, cb, &cbWrite, NULL)) + { + *err = ::GetLastError(); + CabcExitOnLastError(hr, "failed to write during cabinet extraction"); + } + +LExit: + if (FAILED(hr)) + pcd->hrLastError = *err = hr; + + return FAILED(hr) ? -1 : cbWrite; +} + + +static __callback long FAR DIAMONDAPI CabCSeek( + __in INT_PTR hf, + __in long dist, + __in int seektype, + __out int *err, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + DWORD dwMoveMethod; + LONG lMove = 0; + + switch (seektype) + { + case 0: // SEEK_SET + dwMoveMethod = FILE_BEGIN; + break; + case 1: /// SEEK_CUR + dwMoveMethod = FILE_CURRENT; + break; + case 2: // SEEK_END + dwMoveMethod = FILE_END; + break; + default : + dwMoveMethod = 0; + hr = E_UNEXPECTED; + CabcExitOnFailure(hr, "unexpected seektype in FCISeek(): %d", seektype); + } + + // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. + // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) + // Todo: update these comments for FCI (are they accurate for FCI as well?) + lMove = ::SetFilePointer(reinterpret_cast(hf), dist, NULL, dwMoveMethod); + if (DWORD_MAX == lMove) + { + *err = ::GetLastError(); + CabcExitOnLastError(hr, "failed to move file pointer %d bytes", dist); + } + +LExit: + if (FAILED(hr)) + { + pcd->hrLastError = *err = hr; + } + + return FAILED(hr) ? -1 : lMove; +} + + +static __callback int FAR DIAMONDAPI CabCClose( + __in INT_PTR hf, + __out int *err, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + + if (!::CloseHandle(reinterpret_cast(hf))) + { + *err = ::GetLastError(); + CabcExitOnLastError(hr, "failed to close file during cabinet extraction"); + } + +LExit: + if (FAILED(hr)) + { + pcd->hrLastError = *err = hr; + } + + return FAILED(hr) ? -1 : 0; +} + +static __callback int DIAMONDAPI CabCDelete( + __in_z PSTR szFile, + __out int *err, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + UNREFERENCED_PARAMETER(err); + UNREFERENCED_PARAMETER(pv); + +#pragma prefast(push) +#pragma prefast(disable:25068) // We intentionally don't use the unicode API here + ::DeleteFileA(szFile); +#pragma prefast(pop) + + return 0; +} + + +__success(return != FALSE) +static __callback BOOL DIAMONDAPI CabCGetTempFile( + __out_bcount_z(cbFile) char *szFile, + __in int cbFile, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + CABC_DATA *pcd = reinterpret_cast(pv); + static volatile DWORD dwIndex = 0; + + HRESULT hr = S_OK; + char szTempPath[MAX_PATH] = { }; + DWORD cchTempPath = MAX_PATH; + DWORD dwProcessId = ::GetCurrentProcessId(); + HANDLE hTempFile = INVALID_HANDLE_VALUE; + + if (MAX_PATH < ::GetTempPathA(cchTempPath, szTempPath)) + { + CabcExitWithLastError(hr, "Failed to get temp path during cabinet creation."); + } + + for (DWORD i = 0; i < DWORD_MAX; ++i) + { + LONG dwTempIndex = ::InterlockedIncrement(reinterpret_cast(&dwIndex)); + + hr = ::StringCbPrintfA(szFile, cbFile, "%s\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId); + CabcExitOnFailure(hr, "failed to format log file path."); + + hTempFile = ::CreateFileA(szFile, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); + if (INVALID_HANDLE_VALUE != hTempFile) + { + // we found one that doesn't exist + hr = S_OK; + break; + } + else + { + hr = E_FAIL; // this file was taken so be pessimistic and assume we're not going to find one. + } + } + CabcExitOnFailure(hr, "failed to find temporary file."); + +LExit: + ReleaseFileHandle(hTempFile); + + if (FAILED(hr)) + { + pcd->hrLastError = hr; + } + + return FAILED(hr)? FALSE : TRUE; +} + + +__success(return != FALSE) +static __callback BOOL DIAMONDAPI CabCGetNextCabinet( + __in PCCAB pccab, + __in ULONG ul, + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + UNREFERENCED_PARAMETER(ul); + + // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........ + CABC_DATA *pcd = reinterpret_cast(pv); + HRESULT hr = S_OK; + LPWSTR pwzFileToken = NULL; + WCHAR wzNewCabName[MAX_PATH] = L""; + + if (pccab->iCab == 1) + { + pcd->wzFirstCabinetName[0] = '\0'; + LPCWSTR pwzCabinetName = FileFromPath(pcd->wzCabinetPath); + size_t len = wcsnlen(pwzCabinetName, sizeof(pwzCabinetName)); + if (len > 4) + { + len -= 4; // remove Extention ".cab" of 8.3 Format + } + hr = ::StringCchCatNW(pcd->wzFirstCabinetName, countof(pcd->wzFirstCabinetName), pwzCabinetName, len); + CabcExitOnFailure(hr, "Failed to remove extension to create next Cabinet File Name"); + } + + const int nAlphabets = 26; // Number of Alphabets from a to z + if (pccab->iCab <= nAlphabets) + { + // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........ + hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + pccab->iCab)); + CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); + hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + pccab->iCab)); + CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); + } + else if (pccab->iCab <= nAlphabets*nAlphabets) + { + // Construct next cab names like cab1aa.cab, cab1ab.cab, cab1ac.cab, ......, cabaz.cab, cabaa.cab, cabab.cab, cabac.cab, ...... + int char2 = (pccab->iCab) % nAlphabets; + int char1 = (pccab->iCab - char2)/nAlphabets; + if (char2 == 0) + { + // e.g. when iCab = 52, we want az + char2 = nAlphabets; // Second char must be 'z' in this case + char1--; // First Char must be decremented by 1 + } + hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + char1), char(((int)('a') - 1) + char2)); + CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); + hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + char1), WCHAR(((int)('a') - 1) + char2)); + CabcExitOnFailure(hr, "Failed to create next Cabinet File Name"); + } + else + { + hr = DISP_E_BADINDEX; // Value 0x8002000B stands for Invalid index. + CabcExitOnFailure(hr, "Cannot Split Cabinet more than 26*26 = 676 times. Failed to create next Cabinet File Name"); + } + + // Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method + if(pcd->fileSplitCabNamesCallback != 0) + { + // In following if/else block, getting the Token for the First File in the Cabinets that are getting Split + // This code will need updation if we need to send all file tokens for the splitting Cabinets + if (pcd->prgFiles[0].pwzToken) + { + pwzFileToken = pcd->prgFiles[0].pwzToken; + } + else + { + LPCWSTR wzSourcePath = pcd->prgFiles[0].pwzSourcePath; + pwzFileToken = FileFromPath(wzSourcePath); + } + + // The call back to Binder to Add File Transfer for new Cab and add new Cab to Media table + pcd->fileSplitCabNamesCallback(pcd->wzFirstCabinetName, wzNewCabName, pwzFileToken); + } + +LExit: + if (FAILED(hr)) + { + // Returning False in case of error here as stated by Documentation, However It fails to Abort Cab Creation!!! + // So Using separate check for pcd->hrLastError after ::FCIAddFile for Cabinet Splitting + pcd->hrLastError = hr; + return FALSE; + } + else + { + return TRUE; + } +} + + +static __callback INT_PTR DIAMONDAPI CabCGetOpenInfo( + __in_z PSTR pszName, + __out USHORT *pdate, + __out USHORT *ptime, + __out USHORT *pattribs, + __out int *err, + __out_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + HRESULT hr = S_OK; + CABC_INTERNAL_ADDFILEINFO* pFileInfo = reinterpret_cast(pszName); + LPCWSTR wzFile = NULL; + DWORD cbFile = 0; + LPSTR pszFilePlusMagic = NULL; + DWORD cbFilePlusMagic = 0; + WIN32_FILE_ATTRIBUTE_DATA fad = { }; + INT_PTR iResult = -1; + + // If there is an empty file provided, use that as the source path to cab (since we + // must be dealing with a duplicate file). Otherwise, use the source path you'd expect. + wzFile = pFileInfo->wzEmptyPath ? pFileInfo->wzEmptyPath : pFileInfo->wzSourcePath; + cbFile = (lstrlenW(wzFile) + 1) * sizeof(WCHAR); + + // Convert the source file path into an Ansi string that our APIs will recognize as + // a Unicode string (due to the magic character). + cbFilePlusMagic = cbFile + 1; // add one for the magic. + pszFilePlusMagic = reinterpret_cast(MemAlloc(cbFilePlusMagic, TRUE)); + + *pszFilePlusMagic = CABC_MAGIC_UNICODE_STRING_MARKER; + memcpy_s(pszFilePlusMagic + 1, cbFilePlusMagic - 1, wzFile, cbFile); + + if (!::GetFileAttributesExW(pFileInfo->wzSourcePath, GetFileExInfoStandard, &fad)) + { + CabcExitWithLastError(hr, "Failed to get file attributes on '%ls'.", pFileInfo->wzSourcePath); + } + + // Set the attributes but only allow the few attributes that CAB supports. + *pattribs = static_cast(fad.dwFileAttributes) & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE); + + hr = UtcFileTimeToLocalDosDateTime(&fad.ftLastWriteTime, pdate, ptime); + if (FAILED(hr)) + { + // NOTE: Changed this from ftLastWriteTime to ftCreationTime because of issues around how different OSs were + // handling the access of the FILETIME structure and how it would fail conversion to DOS time if it wasn't + // found. This would create further problems if the file was written to the CAB without this value. Windows + // Installer would then fail to extract the file. + hr = UtcFileTimeToLocalDosDateTime(&fad.ftCreationTime, pdate, ptime); + CabcExitOnFailure(hr, "Filed to read a valid file time stucture on file '%s'.", pszName); + } + + iResult = CabCOpen(pszFilePlusMagic, _O_BINARY|_O_RDONLY, 0, err, pv); + +LExit: + ReleaseMem(pszFilePlusMagic); + if (FAILED(hr)) + { + *err = (int)hr; + } + + return FAILED(hr) ? -1 : iResult; +} + + +static __callback long DIAMONDAPI CabCStatus( + __in UINT ui, + __in ULONG cb1, + __in ULONG cb2, + __inout_bcount(CABC_HANDLE_BYTES) void *pv + ) +{ + UNREFERENCED_PARAMETER(ui); + UNREFERENCED_PARAMETER(cb1); + UNREFERENCED_PARAMETER(cb2); + UNREFERENCED_PARAMETER(pv); + return 0; +} diff --git a/src/libs/dutil/WixToolset.DUtil/cabutil.cpp b/src/libs/dutil/WixToolset.DUtil/cabutil.cpp new file mode 100644 index 00000000..5d77e483 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/cabutil.cpp @@ -0,0 +1,617 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define CabExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__) +#define CabExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__) +#define CabExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__) +#define CabExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__) +#define CabExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__) +#define CabExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABUTIL, e, x, s, __VA_ARGS__) +#define CabExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABUTIL, g, x, s, __VA_ARGS__) + + +// external prototypes +typedef BOOL (FAR DIAMONDAPI *PFNFDIDESTROY)(VOID*); +typedef HFDI (FAR DIAMONDAPI *PFNFDICREATE)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF); +typedef BOOL (FAR DIAMONDAPI *PFNFDIISCABINET)(HFDI, INT_PTR, PFDICABINETINFO); +typedef BOOL (FAR DIAMONDAPI *PFNFDICOPY)(HFDI, char *, char *, int, PFNFDINOTIFY, PFNFDIDECRYPT, void *); + + +// +// static globals +// +static HMODULE vhCabinetDll = NULL; + +static HFDI vhfdi = NULL; +static PFNFDICREATE vpfnFDICreate = NULL; +static PFNFDICOPY vpfnFDICopy = NULL; +static PFNFDIISCABINET vpfnFDIIsCabinet = NULL; +static PFNFDIDESTROY vpfnFDIDestroy = NULL; +static ERF verf; + +static DWORD64 vdw64EmbeddedOffset = 0; + +// +// structs +// +struct CAB_CALLBACK_STRUCT +{ + BOOL fStopExtracting; // flag set when no more files are needed + LPCWSTR pwzExtract; // file to extract ("*" means extract all) + LPCWSTR pwzExtractDir; // directory to extract files to + + // possible user data + CAB_CALLBACK_PROGRESS pfnProgress; + LPVOID pvContext; +}; + +// +// prototypes +// +static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize); +static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData); +static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode); +static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb); +static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb); +static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf); +static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype); +static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify); +static HRESULT DAPI CabOperation(__in LPCWSTR wzCabinet, __in LPCWSTR wzExtractFile, __in_opt LPCWSTR wzExtractDir, __in_opt CAB_CALLBACK_PROGRESS pfnProgress, __in_opt LPVOID pvContext, __in_opt STDCALL_PFNFDINOTIFY pfnNotify, __in DWORD64 dw64EmbeddedOffset); + +static STDCALL_PFNFDINOTIFY v_pfnNetFx11Notify = NULL; + + +inline HRESULT LoadCabinetDll() +{ + HRESULT hr = S_OK; + if (!vhCabinetDll) + { + hr = LoadSystemLibrary(L"cabinet.dll", &vhCabinetDll); + CabExitOnFailure(hr, "failed to load cabinet.dll"); + + // retrieve all address functions + vpfnFDICreate = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDICreate")); + CabExitOnNullWithLastError(vpfnFDICreate, hr, "failed to import FDICreate from CABINET.DLL"); + vpfnFDICopy = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDICopy")); + CabExitOnNullWithLastError(vpfnFDICopy, hr, "failed to import FDICopy from CABINET.DLL"); + vpfnFDIIsCabinet = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDIIsCabinet")); + CabExitOnNullWithLastError(vpfnFDIIsCabinet, hr, "failed to import FDIIsCabinetfrom CABINET.DLL"); + vpfnFDIDestroy = reinterpret_cast(::GetProcAddress(vhCabinetDll, "FDIDestroy")); + CabExitOnNullWithLastError(vpfnFDIDestroy, hr, "failed to import FDIDestroyfrom CABINET.DLL"); + + vhfdi = vpfnFDICreate(CabExtractAlloc, CabExtractFree, CabExtractOpen, CabExtractRead, CabExtractWrite, CabExtractClose, CabExtractSeek, cpuUNKNOWN, &verf); + CabExitOnNull(vhfdi, hr, E_FAIL, "failed to initialize cabinet.dll"); + } + +LExit: + if (FAILED(hr) && vhCabinetDll) + { + ::FreeLibrary(vhCabinetDll); + vhCabinetDll = NULL; + } + + return hr; +} + + +static HANDLE OpenFileWithRetry( + __in LPCWSTR wzPath, + __in DWORD dwDesiredAccess, + __in DWORD dwCreationDisposition +) +{ + HANDLE hFile = INVALID_HANDLE_VALUE; + + for (DWORD i = 0; i < 30; ++i) + { + hFile = ::CreateFileW(wzPath, dwDesiredAccess, FILE_SHARE_READ, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE != hFile) + { + break; + } + + ::Sleep(100); + } + + return hFile; +} + + +/******************************************************************** + CabInitialize - initializes internal static variables + +********************************************************************/ +extern "C" HRESULT DAPI CabInitialize( + __in BOOL fDelayLoad + ) +{ + HRESULT hr = S_OK; + + if (!fDelayLoad) + { + hr = LoadCabinetDll(); + CabExitOnFailure(hr, "failed to load CABINET.DLL"); + } + +LExit: + return hr; +} + + +/******************************************************************** + CabUninitialize - initializes internal static variables + +********************************************************************/ +extern "C" void DAPI CabUninitialize( + ) +{ + if (vhfdi) + { + if (vpfnFDIDestroy) + { + vpfnFDIDestroy(vhfdi); + } + vhfdi = NULL; + } + + vpfnFDICreate = NULL; + vpfnFDICopy =NULL; + vpfnFDIIsCabinet = NULL; + vpfnFDIDestroy = NULL; + + if (vhCabinetDll) + { + ::FreeLibrary(vhCabinetDll); + vhCabinetDll = NULL; + } +} + +/******************************************************************** + CabEnumerate - list files inside cabinet + + NOTE: wzCabinet must be full path to cabinet file + pfnNotify is callback function to get notified for each file + in the cabinet +********************************************************************/ +extern "C" HRESULT DAPI CabEnumerate( + __in_z LPCWSTR wzCabinet, + __in_z LPCWSTR wzEnumerateFile, + __in STDCALL_PFNFDINOTIFY pfnNotify, + __in DWORD64 dw64EmbeddedOffset + ) +{ + return CabOperation(wzCabinet, wzEnumerateFile, NULL, NULL, NULL, pfnNotify, dw64EmbeddedOffset); +} + +/******************************************************************** + CabExtract - extracts one or all files from a cabinet + + NOTE: wzCabinet must be full path to cabinet file + wzExtractFile can be a single file id or "*" to extract all files + wzExttractDir must be normalized (end in a "\") + if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa +********************************************************************/ +extern "C" HRESULT DAPI CabExtract( + __in_z LPCWSTR wzCabinet, + __in_z LPCWSTR wzExtractFile, + __in_z LPCWSTR wzExtractDir, + __in_opt CAB_CALLBACK_PROGRESS pfnProgress, + __in_opt LPVOID pvContext, + __in DWORD64 dw64EmbeddedOffset + ) +{ + return CabOperation(wzCabinet, wzExtractFile, wzExtractDir, pfnProgress, pvContext, NULL, dw64EmbeddedOffset); +} + +// +// private +// +/******************************************************************** + FDINotify -- wrapper that converts call convention from __cdecl to __stdcall. + + NOTE: Since netfx 1.1 supports only function pointers (delegates) + with __stdcall calling convention and cabinet api uses + __cdecl calling convention, we need this wrapper function. + netfx 2.0 will work with [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute on the delegate. + TODO: remove this when upgrading to netfx 2.0. +********************************************************************/ +static __callback INT_PTR DIAMONDAPI FDINotify( + __in FDINOTIFICATIONTYPE iNotification, + __inout FDINOTIFICATION *pFDINotify + ) +{ + if (NULL != v_pfnNetFx11Notify) + { + return v_pfnNetFx11Notify(iNotification, pFDINotify); + } + else + { + return (INT_PTR)0; + } +} + + +/******************************************************************** + CabOperation - helper function that enumerates or extracts files + from cabinet + + NOTE: wzCabinet must be full path to cabinet file + wzExtractFile can be a single file id or "*" to extract all files + wzExttractDir must be normalized (end in a "\") + if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa + pfnNotify is callback function to get notified for each file + in the cabinet. If it's NULL, files will be extracted. +********************************************************************/ +static HRESULT DAPI CabOperation( + __in LPCWSTR wzCabinet, + __in LPCWSTR wzExtractFile, + __in_opt LPCWSTR wzExtractDir, + __in_opt CAB_CALLBACK_PROGRESS pfnProgress, + __in_opt LPVOID pvContext, + __in_opt STDCALL_PFNFDINOTIFY pfnNotify, + __in DWORD64 dw64EmbeddedOffset + ) +{ + HRESULT hr = S_OK; + BOOL fResult; + + LPWSTR sczCabinet = NULL; + LPWSTR pwz = NULL; + CHAR szCabDirectory[MAX_PATH * 4]; // Make sure these are big enough for UTF-8 strings + CHAR szCabFile[MAX_PATH * 4]; + + CAB_CALLBACK_STRUCT ccs; + PFNFDINOTIFY pfnFdiNotify; + + // + // ensure the cabinet.dll is loaded + // + if (!vhfdi) + { + hr = LoadCabinetDll(); + CabExitOnFailure(hr, "failed to load CABINET.DLL"); + } + + hr = StrAllocString(&sczCabinet, wzCabinet, 0); + CabExitOnFailure(hr, "Failed to make copy of cabinet name:%ls", wzCabinet); + + // + // split the cabinet full path into directory and filename and convert to multi-byte (ick!) + // + pwz = FileFromPath(sczCabinet); + CabExitOnNull(pwz, hr, E_INVALIDARG, "failed to process cabinet path: %ls", wzCabinet); + + if (!::WideCharToMultiByte(CP_UTF8, 0, pwz, -1, szCabFile, countof(szCabFile), NULL, NULL)) + { + CabExitWithLastError(hr, "failed to convert cabinet filename to ASCII: %ls", pwz); + } + + *pwz = '\0'; + + // If a full path was not provided, use the relative current directory. + if (wzCabinet == pwz) + { + hr = ::StringCchCopyA(szCabDirectory, countof(szCabDirectory), ".\\"); + CabExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory."); + } + else + { + if (!::WideCharToMultiByte(CP_UTF8, 0, sczCabinet, -1, szCabDirectory, countof(szCabDirectory), NULL, NULL)) + { + CabExitWithLastError(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet); + } + } + + // + // iterate through files in cabinet extracting them to the callback function + // + ccs.fStopExtracting = FALSE; + ccs.pwzExtract = wzExtractFile; + ccs.pwzExtractDir = wzExtractDir; + ccs.pfnProgress = pfnProgress; + ccs.pvContext = pvContext; + + vdw64EmbeddedOffset = dw64EmbeddedOffset; + + // if pfnNotify is given, use it, otherwise use default callback + if (NULL == pfnNotify) + { + pfnFdiNotify = CabExtractCallback; + } + else + { + v_pfnNetFx11Notify = pfnNotify; + pfnFdiNotify = FDINotify; + } + fResult = vpfnFDICopy(vhfdi, szCabFile, szCabDirectory, 0, pfnFdiNotify, NULL, static_cast(&ccs)); + if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure + { + CabExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet); + } + +LExit: + ReleaseStr(sczCabinet); + v_pfnNetFx11Notify = NULL; + + return hr; +} + +/**************************************************************************** + default extract routines + +****************************************************************************/ +static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize) +{ + return MemAlloc(dwSize, FALSE); +} + + +static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData) +{ + MemFree(pvData); +} + + +static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + INT_PTR pFile = -1; + LPWSTR sczCabFile = NULL; + + // if FDI asks for some unusual mode (in low memory situation it could ask for a scratch file) fail + if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) + { + hr = E_OUTOFMEMORY; + CabExitOnFailure(hr, "FDI asked for a scratch file to be created, which is unsupported"); + } + + hr = StrAllocStringAnsi(&sczCabFile, pszFile, 0, CP_UTF8); + CabExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string"); + + hFile = OpenFileWithRetry(sczCabFile, GENERIC_READ, OPEN_EXISTING); + if (INVALID_HANDLE_VALUE == hFile) + { + CabExitWithLastError(hr, "failed to open file: %ls", sczCabFile); + } + + pFile = reinterpret_cast(hFile); + + if (vdw64EmbeddedOffset) + { + hr = CabExtractSeek(pFile, 0, 0); + CabExitOnFailure(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset); + } + + hFile = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hFile); + ReleaseStr(sczCabFile); + + return FAILED(hr) ? -1 : pFile; +} + + +static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb) +{ + HRESULT hr = S_OK; + DWORD cbRead = 0; + + CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to read file during cabinet extraction - no file given to read"); + if (!::ReadFile(reinterpret_cast(hf), pv, cb, &cbRead, NULL)) + { + CabExitWithLastError(hr, "failed to read during cabinet extraction"); + } + +LExit: + return FAILED(hr) ? -1 : cbRead; +} + + +static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb) +{ + HRESULT hr = S_OK; + DWORD cbWrite = 0; + + CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to write file during cabinet extraction - no file given to write"); + if (!::WriteFile(reinterpret_cast(hf), pv, cb, &cbWrite, NULL)) + { + CabExitWithLastError(hr, "failed to write during cabinet extraction"); + } + +LExit: + return FAILED(hr) ? -1 : cbWrite; +} + + +static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype) +{ + HRESULT hr = S_OK; + DWORD dwMoveMethod; + LONG lMove = 0; + + switch (seektype) + { + case 0: // SEEK_SET + dwMoveMethod = FILE_BEGIN; + dist += static_cast(vdw64EmbeddedOffset); + break; + case 1: /// SEEK_CUR + dwMoveMethod = FILE_CURRENT; + break; + case 2: // SEEK_END + dwMoveMethod = FILE_END; + break; + default : + dwMoveMethod = 0; + hr = E_UNEXPECTED; + CabExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); + } + + // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. + // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) + lMove = ::SetFilePointer(reinterpret_cast(hf), dist, NULL, dwMoveMethod); + if (0xFFFFFFFF == lMove) + { + CabExitWithLastError(hr, "failed to move file pointer %d bytes", dist); + } + +LExit: + return FAILED(hr) ? -1 : lMove - static_cast(vdw64EmbeddedOffset); +} + + +static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf) +{ + HRESULT hr = S_OK; + + if (!::CloseHandle(reinterpret_cast(hf))) + { + CabExitWithLastError(hr, "failed to close file during cabinet extraction"); + } + +LExit: + return FAILED(hr) ? -1 : 0; +} + + +static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify) +{ + Assert(pFDINotify->pv); + + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + INT_PTR ipResult = 0; // result to return on success + + CAB_CALLBACK_STRUCT* pccs = static_cast(pFDINotify->pv); + LPCSTR sz; + WCHAR wz[MAX_PATH]; + FILETIME ft; + + switch (iNotification) + { + case fdintCOPY_FILE: // begin extracting a resource from cabinet + CabExitOnNull(pFDINotify->psz1, hr, E_INVALIDARG, "No cabinet file ID given to convert"); + CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); + + if (pccs->fStopExtracting) + { + ExitFunction1(hr = S_FALSE); // no more extracting + } + + // convert params to useful variables + sz = static_cast(pFDINotify->psz1); + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) + { + CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + } + + if (pccs->pfnProgress) + { + hr = pccs->pfnProgress(TRUE, wz, pccs->pvContext); + if (S_OK != hr) + { + ExitFunction(); + } + } + + if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, wz)) + { + // get the created date for the resource in the cabinet + FILETIME ftLocal; + if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) + { + CabExitWithLastError(hr, "failed to get time for resource: %ls", wz); + } + ::LocalFileTimeToFileTime(&ftLocal, &ft); + + WCHAR wzPath[MAX_PATH]; + hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir); + CabExitOnFailure(hr, "failed to copy in extract directory: %ls for file: %ls", pccs->pwzExtractDir, wz); + hr = ::StringCchCatW(wzPath, countof(wzPath), wz); + CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); + + hFile = OpenFileWithRetry(wzPath, GENERIC_WRITE, CREATE_ALWAYS); + if (INVALID_HANDLE_VALUE == hFile) + { + CabExitWithLastError(hr, "failed to create file: %ls", wzPath); + } + + ::SetFileTime(hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails) + + if (::SetFilePointer(hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) + { + if (::SetEndOfFile(hFile)) + { + ::SetFilePointer(hFile, 0, NULL, FILE_BEGIN); // reset the file pointer + } + } + + ipResult = reinterpret_cast(hFile); + hFile = INVALID_HANDLE_VALUE; + } + else // resource wasn't requested, skip it + { + hr = S_OK; + ipResult = 0; + } + + break; + case fdintCLOSE_FILE_INFO: // resource extraction complete + Assert(pFDINotify->hf && pFDINotify->psz1); + CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); + + // convert params to useful variables + sz = static_cast(pFDINotify->psz1); + CabExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided"); + + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) + { + CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + } + + if (NULL != pFDINotify->hf) // just close the file + { + ::CloseHandle(reinterpret_cast(pFDINotify->hf)); + } + + if (pccs->pfnProgress) + { + hr = pccs->pfnProgress(FALSE, wz, pccs->pvContext); + } + + if (S_OK == hr && L'*' == *pccs->pwzExtract) // if everything is okay and we're extracting all files, keep going + { + ipResult = TRUE; + } + else // something went wrong or we only needed to extract one file + { + hr = S_OK; + ipResult = FALSE; + pccs->fStopExtracting = TRUE; + } + + break; + case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through + case fdintNEXT_CABINET: __fallthrough; + case fdintENUMERATE: __fallthrough; + case fdintCABINET_INFO: + break; + default: + AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command"); + }; + +LExit: + ReleaseFileHandle(hFile); + + return (S_OK == hr) ? ipResult : -1; +} diff --git a/src/libs/dutil/WixToolset.DUtil/certutil.cpp b/src/libs/dutil/WixToolset.DUtil/certutil.cpp new file mode 100644 index 00000000..69897b9e --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/certutil.cpp @@ -0,0 +1,342 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define CertExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__) +#define CertExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CERTUTIL, p, x, e, s, __VA_ARGS__) +#define CertExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, p, x, s, __VA_ARGS__) +#define CertExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CERTUTIL, p, x, e, s, __VA_ARGS__) +#define CertExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, p, x, s, __VA_ARGS__) +#define CertExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CERTUTIL, e, x, s, __VA_ARGS__) +#define CertExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CERTUTIL, g, x, s, __VA_ARGS__) + +/******************************************************************** +CertReadProperty - reads a property from the certificate. + +NOTE: call MemFree() on the returned pvValue. +********************************************************************/ +extern "C" HRESULT DAPI CertReadProperty( + __in PCCERT_CONTEXT pCertContext, + __in DWORD dwProperty, + __deref_out_bound LPVOID* ppvValue, + __out_opt DWORD* pcbValue + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + DWORD cb = 0; + + if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, NULL, &cb)) + { + CertExitWithLastError(hr, "Failed to get size of certificate property."); + } + + pv = MemAlloc(cb, TRUE); + CertExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for certificate property."); + + if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, pv, &cb)) + { + CertExitWithLastError(hr, "Failed to get certificate property."); + } + + *ppvValue = pv; + pv = NULL; + + if (pcbValue) + { + *pcbValue = cb; + } + +LExit: + ReleaseMem(pv); + return hr; +} + + +extern "C" HRESULT DAPI CertGetAuthenticodeSigningTimestamp( + __in CMSG_SIGNER_INFO* pSignerInfo, + __out FILETIME* pftSigningTimestamp + ) +{ + HRESULT hr = S_OK; + CRYPT_INTEGER_BLOB* pBlob = NULL; + PCMSG_SIGNER_INFO pCounterSignerInfo = NULL; + DWORD cbSigningTimestamp = sizeof(FILETIME); + + // Find the countersigner blob. The countersigner in Authenticode contains the time + // that signing took place. It's a "countersigner" because the signing time was sent + // off to the certificate authority in the sky to return the verified time signed. + for (DWORD i = 0; i < pSignerInfo->UnauthAttrs.cAttr; ++i) + { + if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_counterSign, -1, pSignerInfo->UnauthAttrs.rgAttr[i].pszObjId, -1)) + { + pBlob = pSignerInfo->UnauthAttrs.rgAttr[i].rgValue; + break; + } + } + + if (!pBlob) + { + hr = TRUST_E_FAIL; + CertExitOnFailure(hr, "Failed to find countersigner in signer information."); + } + + hr = CrypDecodeObject(PKCS7_SIGNER_INFO, pBlob->pbData, pBlob->cbData, 0, reinterpret_cast(&pCounterSignerInfo), NULL); + CertExitOnFailure(hr, "Failed to decode countersigner information."); + + pBlob = NULL; // reset the blob before searching for the signing time. + + // Find the signing time blob in the countersigner. + for (DWORD i = 0; i < pCounterSignerInfo->AuthAttrs.cAttr; ++i) + { + if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_signingTime, -1, pCounterSignerInfo->AuthAttrs.rgAttr[i].pszObjId, -1)) + { + pBlob = pCounterSignerInfo->AuthAttrs.rgAttr[i].rgValue; + break; + } + } + + if (!pBlob) + { + hr = TRUST_E_FAIL; + CertExitOnFailure(hr, "Failed to find signing time in countersigner information."); + } + + if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_RSA_signingTime, pBlob->pbData, pBlob->cbData, 0, pftSigningTimestamp, &cbSigningTimestamp)) + { + CertExitWithLastError(hr, "Failed to decode countersigner signing timestamp."); + } + +LExit: + ReleaseMem(pCounterSignerInfo); + + return hr; +} + + +extern "C" HRESULT DAPI GetCryptProvFromCert( + __in_opt HWND hwnd, + __in PCCERT_CONTEXT pCert, + __out HCRYPTPROV *phCryptProv, + __out DWORD *pdwKeySpec, + __in BOOL *pfDidCryptAcquire, + __deref_opt_out LPWSTR *ppwszTmpContainer, + __deref_opt_out LPWSTR *ppwszProviderName, + __out DWORD *pdwProviderType + ) +{ + HRESULT hr = S_OK; + HMODULE hMsSign32 = NULL; + + typedef BOOL (WINAPI *GETCRYPTPROVFROMCERTPTR)(HWND, PCCERT_CONTEXT, HCRYPTPROV*, DWORD*,BOOL*,LPWSTR*,LPWSTR*,DWORD*); + GETCRYPTPROVFROMCERTPTR pGetCryptProvFromCert = NULL; + + hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32); + CertExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); + + pGetCryptProvFromCert = (GETCRYPTPROVFROMCERTPTR)::GetProcAddress(hMsSign32, "GetCryptProvFromCert"); + CertExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); + + if (!pGetCryptProvFromCert(hwnd, + pCert, + phCryptProv, + pdwKeySpec, + pfDidCryptAcquire, + ppwszTmpContainer, + ppwszProviderName, + pdwProviderType)) + { + CertExitWithLastError(hr, "Failed to get CSP from cert."); + } +LExit: + return hr; +} + +extern "C" HRESULT DAPI FreeCryptProvFromCert( + __in BOOL fAcquired, + __in HCRYPTPROV hProv, + __in_opt LPWSTR pwszCapiProvider, + __in DWORD dwProviderType, + __in_opt LPWSTR pwszTmpContainer + ) +{ + HRESULT hr = S_OK; + HMODULE hMsSign32 = NULL; + + typedef void (WINAPI *FREECRYPTPROVFROMCERT)(BOOL, HCRYPTPROV, LPWSTR, DWORD, LPWSTR); + FREECRYPTPROVFROMCERT pFreeCryptProvFromCert = NULL; + + hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32); + CertExitOnFailure(hr, "Failed to get handle to MsSign32.dll"); + + pFreeCryptProvFromCert = (FREECRYPTPROVFROMCERT)::GetProcAddress(hMsSign32, "FreeCryptProvFromCert"); + CertExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll"); + + pFreeCryptProvFromCert(fAcquired, hProv, pwszCapiProvider, dwProviderType, pwszTmpContainer); +LExit: + return hr; +} + +extern "C" HRESULT DAPI GetProvSecurityDesc( + __in HCRYPTPROV hProv, + __deref_out SECURITY_DESCRIPTOR** ppSecurity) +{ + HRESULT hr = S_OK; + ULONG ulSize = 0; + SECURITY_DESCRIPTOR* pSecurity = NULL; + + // Get the size of the security descriptor. + if (!::CryptGetProvParam( + hProv, + PP_KEYSET_SEC_DESCR, + NULL, + &ulSize, + DACL_SECURITY_INFORMATION)) + { + CertExitWithLastError(hr, "Error getting security descriptor size for CSP."); + } + + // Allocate the memory for the security descriptor. + pSecurity = static_cast(MemAlloc(ulSize, TRUE)); + CertExitOnNullWithLastError(pSecurity, hr, "Error allocating memory for CSP DACL"); + + // Get the security descriptor. + if (!::CryptGetProvParam( + hProv, + PP_KEYSET_SEC_DESCR, + (BYTE*)pSecurity, + &ulSize, + DACL_SECURITY_INFORMATION)) + { + MemFree(pSecurity); + CertExitWithLastError(hr, "Error getting security descriptor for CSP."); + } + *ppSecurity = pSecurity; + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI SetProvSecurityDesc( + __in HCRYPTPROV hProv, + __in SECURITY_DESCRIPTOR* pSecurity) +{ + HRESULT hr = S_OK; + + // Set the new security descriptor. + if (!::CryptSetProvParam( + hProv, + PP_KEYSET_SEC_DESCR, + (BYTE*)pSecurity, + DACL_SECURITY_INFORMATION)) + { + CertExitWithLastError(hr, "Error setting security descriptor for CSP."); + } +LExit: + return hr; +} + +extern "C" BOOL DAPI CertHasPrivateKey( + __in PCCERT_CONTEXT pCertContext, + __out_opt DWORD* pdwKeySpec) +{ + HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hPrivateKey = NULL; + DWORD dwKeySpec = 0; + // set CRYPT_ACQUIRE_CACHE_FLAG so that we don't have to release the private key handle + BOOL fResult = ::CryptAcquireCertificatePrivateKey( + pCertContext, + CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_CACHE_FLAG, + 0, //pvReserved + &hPrivateKey, + &dwKeySpec, + NULL + ); + if (pdwKeySpec) + { + *pdwKeySpec = dwKeySpec; + } + return fResult; +} + + +extern "C" HRESULT DAPI CertInstallSingleCertificate( + __in HCERTSTORE hStore, + __in PCCERT_CONTEXT pCertContext, + __in LPCWSTR wzName + ) +{ + HRESULT hr = S_OK; + CERT_BLOB blob = { }; + + DWORD dwKeySpec = 0; + + HCRYPTPROV hCsp = NULL; + LPWSTR pwszTmpContainer = NULL; + LPWSTR pwszProviderName = NULL; + DWORD dwProviderType = 0; + BOOL fAcquired = TRUE; + + SECURITY_DESCRIPTOR* pSecurity = NULL; + SECURITY_DESCRIPTOR* pSecurityNew = NULL; + + // Update the friendly name of the certificate to be configured. + blob.pbData = (BYTE*)wzName; + blob.cbData = (lstrlenW(wzName) + 1) * sizeof(WCHAR); // including terminating null + + if (!::CertSetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, 0, &blob)) + { + CertExitWithLastError(hr, "Failed to set the friendly name of the certificate: %ls", wzName); + } + + if (!::CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) + { + CertExitWithLastError(hr, "Failed to add certificate to the store."); + } + + // if the certificate has a private key, grant Administrators access + if (CertHasPrivateKey(pCertContext, &dwKeySpec)) + { + if (AT_KEYEXCHANGE == dwKeySpec || AT_SIGNATURE == dwKeySpec) + { + // We added a CSP key + hr = GetCryptProvFromCert(NULL, pCertContext, &hCsp, &dwKeySpec, &fAcquired, &pwszTmpContainer, &pwszProviderName, &dwProviderType); + CertExitOnFailure(hr, "Failed to get handle to CSP"); + + hr = GetProvSecurityDesc(hCsp, &pSecurity); + CertExitOnFailure(hr, "Failed to get security descriptor of CSP"); + + hr = AclAddAdminToSecurityDescriptor(pSecurity, &pSecurityNew); + CertExitOnFailure(hr, "Failed to create new security descriptor"); + + hr = SetProvSecurityDesc(hCsp, pSecurityNew); + CertExitOnFailure(hr, "Failed to set Admin ACL on CSP"); + } + + if (CERT_NCRYPT_KEY_SPEC == dwKeySpec) + { + // We added a CNG key + // TODO change ACL on CNG key + } + } +LExit: + if (hCsp) + { + FreeCryptProvFromCert(fAcquired, hCsp, NULL, dwProviderType, NULL); + } + + ReleaseMem(pSecurity); + + if (pSecurityNew) + { + AclFreeSecurityDescriptor(pSecurityNew); + } + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/conutil.cpp b/src/libs/dutil/WixToolset.DUtil/conutil.cpp new file mode 100644 index 00000000..33e1b59a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/conutil.cpp @@ -0,0 +1,673 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ConExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__) +#define ConExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__) +#define ConExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__) +#define ConExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__) +#define ConExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__) +#define ConExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CONUTIL, e, x, s, __VA_ARGS__) +#define ConExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CONUTIL, g, x, s, __VA_ARGS__) + + +static HANDLE vhStdIn = INVALID_HANDLE_VALUE; +static HANDLE vhStdOut = INVALID_HANDLE_VALUE; +static BOOL vfConsoleIn = FALSE; +static BOOL vfConsoleOut = FALSE; +static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo; + + +extern "C" HRESULT DAPI ConsoleInitialize() +{ + Assert(INVALID_HANDLE_VALUE == vhStdOut); + HRESULT hr = S_OK; + UINT er; + + vhStdIn = ::GetStdHandle(STD_INPUT_HANDLE); + if (INVALID_HANDLE_VALUE == vhStdIn) + { + ConExitOnLastError(hr, "failed to open stdin"); + } + + vhStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); + if (INVALID_HANDLE_VALUE == vhStdOut) + { + ConExitOnLastError(hr, "failed to open stdout"); + } + + // check if we have a std in on the console + if (::GetConsoleScreenBufferInfo(vhStdIn, &vcsbiInfo)) + { + vfConsoleIn = TRUE; + } + else + { + er = ::GetLastError(); + if (ERROR_INVALID_HANDLE == er) + { + vfConsoleIn= FALSE; + hr = S_OK; + } + else + { + ConExitOnWin32Error(er, hr, "failed to get input console screen buffer info"); + } + } + + if (::GetConsoleScreenBufferInfo(vhStdOut, &vcsbiInfo)) + { + vfConsoleOut = TRUE; + } + else // no console + { + memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); + er = ::GetLastError(); + if (ERROR_INVALID_HANDLE == er) + { + vfConsoleOut = FALSE; + hr = S_OK; + } + else + { + ConExitOnWin32Error(er, hr, "failed to get output console screen buffer info"); + } + } + +LExit: + if (FAILED(hr)) + { + if (INVALID_HANDLE_VALUE != vhStdOut) + { + ::CloseHandle(vhStdOut); + } + + if (INVALID_HANDLE_VALUE != vhStdIn && vhStdOut != vhStdIn) + { + ::CloseHandle(vhStdIn); + } + + vhStdOut = INVALID_HANDLE_VALUE; + vhStdIn = INVALID_HANDLE_VALUE; + } + + return hr; +} + + +extern "C" void DAPI ConsoleUninitialize() +{ + BOOL fOutEqualsIn = vhStdOut == vhStdIn; + + memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); + + if (INVALID_HANDLE_VALUE != vhStdOut) + { + ::CloseHandle(vhStdOut); + } + + if (INVALID_HANDLE_VALUE != vhStdIn && !fOutEqualsIn) + { + ::CloseHandle(vhStdIn); + } + + vhStdOut = INVALID_HANDLE_VALUE; + vhStdIn = INVALID_HANDLE_VALUE; +} + + +extern "C" void DAPI ConsoleGreen() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_GREEN | FOREGROUND_INTENSITY); + } +} + + +extern "C" void DAPI ConsoleRed() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_INTENSITY); + } +} + + +extern "C" void DAPI ConsoleYellow() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + } +} + + +extern "C" void DAPI ConsoleNormal() +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + if (vfConsoleOut) + { + ::SetConsoleTextAttribute(vhStdOut, vcsbiInfo.wAttributes); + } +} + + +/******************************************************************** + ConsoleWrite - full color printfA without libc + + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d") + assumes already in normal color and resets the screen to normal color +********************************************************************/ +extern "C" HRESULT DAPI ConsoleWrite( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + LPSTR pszOutput = NULL; + DWORD cchOutput = 0; + DWORD cbWrote = 0; + DWORD cbTotal = 0; + + // set the color + switch (cc) + { + case CONSOLE_COLOR_NORMAL: break; // do nothing + case CONSOLE_COLOR_RED: ConsoleRed(); break; + case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break; + case CONSOLE_COLOR_GREEN: ConsoleGreen(); break; + } + + va_list args; + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); + va_end(args); + ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); + + cchOutput = lstrlenA(pszOutput); + while (cbTotal < (sizeof(*pszOutput) * cchOutput)) + { + if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) + { + ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput); + } + + cbTotal += cbWrote; + } + + // reset the color to normal + if (CONSOLE_COLOR_NORMAL != cc) + { + ConsoleNormal(); + } + +LExit: + ReleaseStr(pszOutput); + return hr; +} + + +/******************************************************************** + ConsoleWriteLine - full color printfA plus newline without libc + + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d") + assumes already in normal color and resets the screen to normal color +********************************************************************/ +extern "C" HRESULT DAPI ConsoleWriteLine( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + LPSTR pszOutput = NULL; + DWORD cchOutput = 0; + DWORD cbWrote = 0; + DWORD cbTotal = 0; + LPCSTR szNewLine = "\r\n"; + + // set the color + switch (cc) + { + case CONSOLE_COLOR_NORMAL: break; // do nothing + case CONSOLE_COLOR_RED: ConsoleRed(); break; + case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break; + case CONSOLE_COLOR_GREEN: ConsoleGreen(); break; + } + + va_list args; + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args); + va_end(args); + ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat); + + // + // write the string + // + cchOutput = lstrlenA(pszOutput); + while (cbTotal < (sizeof(*pszOutput) * cchOutput)) + { + if (!::WriteFile(vhStdOut, reinterpret_cast(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) + ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput); + + cbTotal += cbWrote; + } + + // + // write the newline + // + if (!::WriteFile(vhStdOut, reinterpret_cast(szNewLine), 2, &cbWrote, NULL)) + { + ConExitOnLastError(hr, "failed to write newline to console"); + } + + // reset the color to normal + if (CONSOLE_COLOR_NORMAL != cc) + { + ConsoleNormal(); + } + +LExit: + ReleaseStr(pszOutput); + return hr; +} + + +/******************************************************************** + ConsoleWriteError - display an error to the screen + + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%s" or "%d") +********************************************************************/ +HRESULT ConsoleWriteError( + HRESULT hrError, + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + LPSTR pszMessage = NULL; + + va_list args; + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(&pszMessage, szFormat, args); + va_end(args); + ConExitOnFailure(hr, "failed to format error message: \"%s\"", szFormat); + + if (FAILED(hrError)) + { + hr = ConsoleWriteLine(cc, "Error 0x%x: %s", hrError, pszMessage); + } + else + { + hr = ConsoleWriteLine(cc, "Error: %s", pszMessage); + } + +LExit: + ReleaseStr(pszMessage); + return hr; +} + + +/******************************************************************** + ConsoleReadW - get console input without libc + + NOTE: only supports reading ANSI characters +********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadW( + __deref_out_z LPWSTR* ppwzBuffer + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + Assert(ppwzBuffer); + + HRESULT hr = S_OK; + LPSTR psz = NULL; + DWORD cch = 0; + DWORD cchRead = 0; + DWORD cchTotalRead = 0; + + cch = 64; + hr = StrAnsiAlloc(&psz, cch); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); + + // loop until we read the \r\n from the console + for (;;) + { + // read one character at a time, since that seems to be the only way to make this work + if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) + ConExitOnLastError(hr, "failed to read string from console"); + + cchTotalRead += cchRead; + if (1 < cchTotalRead && '\r' == psz[cchTotalRead - 2] || '\n' == psz[cchTotalRead - 1]) + { + psz[cchTotalRead - 2] = '\0'; // chop off the \r\n + break; + } + else if (0 == cchRead) // nothing more was read + { + psz[cchTotalRead] = '\0'; // null termintate and bail + break; + } + + if (cchTotalRead == cch) + { + cch *= 2; // double everytime we run out of space + hr = StrAnsiAlloc(&psz, cch); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); + } + } + + hr = StrAllocStringAnsi(ppwzBuffer, psz, 0, CP_ACP); + +LExit: + ReleaseStr(psz); + return hr; +} + + +/******************************************************************** + ConsoleReadNonBlockingW - Read from the console without blocking + Won't work for redirected files (exe < txtfile), but will work for stdin redirected to + an anonymous or named pipe + + if (fReadLine), stop reading immediately when \r\n is found +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadNonBlockingW( + __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, + __out DWORD* pcchSize, + BOOL fReadLine + ) +{ + Assert(INVALID_HANDLE_VALUE != vhStdIn && pcchSize); + HRESULT hr = S_OK; + + LPSTR psz = NULL; + + ConExitOnNull(ppwzBuffer, hr, E_INVALIDARG, "Failed to read from console because buffer was not provided"); + + DWORD dwRead; + DWORD dwNumInput; + + DWORD cchTotal = 0; + DWORD cch = 8; + + DWORD cchRead = 0; + DWORD cchTotalRead = 0; + + DWORD dwIndex = 0; + DWORD er; + + INPUT_RECORD ir; + WCHAR chIn; + + *ppwzBuffer = NULL; + *pcchSize = 0; + + // If we really have a handle to stdin, and not the end of a pipe + if (!PeekNamedPipe(vhStdIn, NULL, 0, NULL, &dwRead, NULL)) + { + er = ::GetLastError(); + if (ERROR_INVALID_HANDLE != er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + + if (!GetNumberOfConsoleInputEvents(vhStdIn, &dwRead)) + { + ConExitOnLastError(hr, "failed to peek at console input"); + } + + if (0 == dwRead) + { + ExitFunction1(hr = S_FALSE); + } + + for (/* dwRead from num of input events */; dwRead > 0; dwRead--) + { + if (!ReadConsoleInputW(vhStdIn, &ir, 1, &dwNumInput)) + { + ConExitOnLastError(hr, "Failed to read input from console"); + } + + // If what we have is a KEY_EVENT, and that event signifies keyUp, we're interested + if (KEY_EVENT == ir.EventType && FALSE == ir.Event.KeyEvent.bKeyDown) + { + chIn = ir.Event.KeyEvent.uChar.UnicodeChar; + + if (0 == cchTotal) + { + cchTotal = cch; + cch *= 2; + StrAlloc(ppwzBuffer, cch); + } + + (*ppwzBuffer)[dwIndex] = chIn; + + if (fReadLine && (L'\r' == (*ppwzBuffer)[dwIndex - 1] && L'\n' == (*ppwzBuffer)[dwIndex])) + { + *ppwzBuffer[dwIndex - 1] = L'\0'; + dwIndex -= 1; + break; + } + + ++dwIndex; + cchTotal--; + } + } + + *pcchSize = dwIndex; + } + else + { + // otherwise, the peek worked, and we have the end of a pipe + if (0 == dwRead) + ExitFunction1(hr = S_FALSE); + + cch = 8; + hr = StrAnsiAlloc(&psz, cch); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); + + for (/*dwRead from PeekNamedPipe*/; dwRead > 0; dwRead--) + { + // read one character at a time, since that seems to be the only way to make this work + if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + + cchTotalRead += cchRead; + if (fReadLine && '\r' == psz[cchTotalRead - 1] && '\n' == psz[cchTotalRead]) + { + psz[cchTotalRead - 1] = '\0'; // chop off the \r\n + cchTotalRead -= 1; + break; + } + else if (0 == cchRead) // nothing more was read + { + psz[cchTotalRead] = '\0'; // null termintate and bail + break; + } + + if (cchTotalRead == cch) + { + cch *= 2; // double everytime we run out of space + hr = StrAnsiAlloc(&psz, cch); + ConExitOnFailure(hr, "failed to allocate memory to read from console"); + } + } + + *pcchSize = cchTotalRead; + hr = StrAllocStringAnsi(ppwzBuffer, psz, cchTotalRead, CP_ACP); + } + +LExit: + ReleaseStr(psz); + + return hr; +} + + +/******************************************************************** + ConsoleReadStringA - get console input without libc + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadStringA( + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer, + CONST DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + if (ppszCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2)) + { + DWORD iRead = 1; + DWORD iReadCharTotal = 0; + if (ppszCharBuffer && *ppszCharBuffer == NULL) + { + do + { + hr = StrAnsiAlloc(ppszCharBuffer, cchCharBuffer * iRead); + ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); + // ReadConsoleW will not return until , the last two chars are 13 and 10. + if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + iReadCharTotal += *pcchNumCharReturn; + iRead += 1; + } + while((*ppszCharBuffer)[iReadCharTotal - 1] != 10 || (*ppszCharBuffer)[iReadCharTotal - 2] != 13); + *pcchNumCharReturn = iReadCharTotal; + } + else + { + if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || + *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + if ((*ppszCharBuffer)[*pcchNumCharReturn - 1] != 10 || + (*ppszCharBuffer)[*pcchNumCharReturn - 2] != 13) + { + // need read more + hr = ERROR_MORE_DATA; + } + } + } + else + { + hr = E_INVALIDARG; + } + +LExit: + return hr; +} + +/******************************************************************** + ConsoleReadStringW - get console input without libc + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleReadStringW( + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer, + const DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + if (ppwzCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2)) + { + DWORD iRead = 1; + DWORD iReadCharTotal = 0; + if (*ppwzCharBuffer == NULL) + { + do + { + hr = StrAlloc(ppwzCharBuffer, cchCharBuffer * iRead); + ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); + // ReadConsoleW will not return until , the last two chars are 13 and 10. + if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + iReadCharTotal += *pcchNumCharReturn; + iRead += 1; + } + while((*ppwzCharBuffer)[iReadCharTotal - 1] != 10 || (*ppwzCharBuffer)[iReadCharTotal - 2] != 13); + *pcchNumCharReturn = iReadCharTotal; + } + else + { + if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) || + *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0) + { + ConExitOnLastError(hr, "failed to read string from console"); + } + if ((*ppwzCharBuffer)[*pcchNumCharReturn - 1] != 10 || + (*ppwzCharBuffer)[*pcchNumCharReturn - 2] != 13) + { + // need read more + hr = ERROR_MORE_DATA; + } + } + } + else + { + hr = E_INVALIDARG; + } + +LExit: + return hr; +} + +/******************************************************************** + ConsoleSetReadHidden - set console input no echo + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleSetReadHidden(void) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + ::FlushConsoleInputBuffer(vhStdIn); + if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT)) + { + ConExitOnLastError(hr, "failed to set console input mode to be hidden"); + } + +LExit: + return hr; +} + +/******************************************************************** + ConsoleSetReadNormal - reset to echo + +*********************************************************************/ +extern "C" HRESULT DAPI ConsoleSetReadNormal(void) +{ + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); + HRESULT hr = S_OK; + if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT)) + { + ConExitOnLastError(hr, "failed to set console input mode to be normal"); + } + +LExit: + return hr; +} + diff --git a/src/libs/dutil/WixToolset.DUtil/cryputil.cpp b/src/libs/dutil/WixToolset.DUtil/cryputil.cpp new file mode 100644 index 00000000..24bb83f1 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/cryputil.cpp @@ -0,0 +1,404 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define CrypExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__) +#define CrypExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CRYPUTIL, p, x, e, s, __VA_ARGS__) +#define CrypExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, p, x, s, __VA_ARGS__) +#define CrypExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, p, x, e, s, __VA_ARGS__) +#define CrypExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, p, x, s, __VA_ARGS__) +#define CrypExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CRYPUTIL, e, x, s, __VA_ARGS__) +#define CrypExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CRYPUTIL, g, x, s, __VA_ARGS__) + +static PFN_RTLENCRYPTMEMORY vpfnRtlEncryptMemory = NULL; +static PFN_RTLDECRYPTMEMORY vpfnRtlDecryptMemory = NULL; +static PFN_CRYPTPROTECTMEMORY vpfnCryptProtectMemory = NULL; +static PFN_CRYPTUNPROTECTMEMORY vpfnCryptUnprotectMemory = NULL; + +static HMODULE vhAdvApi32Dll = NULL; +static HMODULE vhCrypt32Dll = NULL; +static BOOL vfCrypInitialized = FALSE; + +// function definitions + +/******************************************************************** + CrypInitialize - initializes cryputil + +*********************************************************************/ +extern "C" HRESULT DAPI CrypInitialize( + ) +{ + HRESULT hr = S_OK; + + hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); + if (SUCCEEDED(hr)) + { + // Ignore failures - if these don't exist, we'll try the Crypt methods. + vpfnRtlEncryptMemory = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "SystemFunction040")); + vpfnRtlDecryptMemory = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "SystemFunction041")); + } + if (!vpfnRtlEncryptMemory || !vpfnRtlDecryptMemory) + { + hr = LoadSystemLibrary(L"Crypt32.dll", &vhCrypt32Dll); + CrypExitOnFailure(hr, "Failed to load Crypt32.dll"); + + vpfnCryptProtectMemory = reinterpret_cast(::GetProcAddress(vhCrypt32Dll, "CryptProtectMemory")); + if (!vpfnRtlEncryptMemory && !vpfnCryptProtectMemory) + { + CrypExitWithLastError(hr, "Failed to load an encryption method"); + } + vpfnCryptUnprotectMemory = reinterpret_cast(::GetProcAddress(vhCrypt32Dll, "CryptUnprotectMemory")); + if (!vpfnRtlDecryptMemory && !vpfnCryptUnprotectMemory) + { + CrypExitWithLastError(hr, "Failed to load a decryption method"); + } + } + + vfCrypInitialized = TRUE; + +LExit: + return hr; +} + + +/******************************************************************** + CrypUninitialize - uninitializes cryputil + +*********************************************************************/ +extern "C" void DAPI CrypUninitialize( + ) +{ + if (vhAdvApi32Dll) + { + ::FreeLibrary(vhAdvApi32Dll); + vhAdvApi32Dll = NULL; + vpfnRtlEncryptMemory = NULL; + vpfnRtlDecryptMemory = NULL; + } + + if (vhCrypt32Dll) + { + ::FreeLibrary(vhCrypt32Dll); + vhCrypt32Dll = NULL; + vpfnCryptProtectMemory = NULL; + vpfnCryptUnprotectMemory = NULL; + } + + vfCrypInitialized = FALSE; +} + +extern "C" HRESULT DAPI CrypDecodeObject( + __in_z LPCSTR szStructType, + __in_ecount(cbData) const BYTE* pbData, + __in DWORD cbData, + __in DWORD dwFlags, + __out LPVOID* ppvObject, + __out_opt DWORD* pcbObject + ) +{ + HRESULT hr = S_OK; + LPVOID pvObject = NULL; + DWORD cbObject = 0; + + if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, NULL, &cbObject)) + { + CrypExitWithLastError(hr, "Failed to decode object to determine size."); + } + + pvObject = MemAlloc(cbObject, TRUE); + CrypExitOnNull(pvObject, hr, E_OUTOFMEMORY, "Failed to allocate memory for decoded object."); + + if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, pvObject, &cbObject)) + { + CrypExitWithLastError(hr, "Failed to decode object."); + } + + *ppvObject = pvObject; + pvObject = NULL; + + if (pcbObject) + { + *pcbObject = cbObject; + } + +LExit: + ReleaseMem(pvObject); + + return hr; +} + + +extern "C" HRESULT DAPI CrypMsgGetParam( + __in HCRYPTMSG hCryptMsg, + __in DWORD dwType, + __in DWORD dwIndex, + __out LPVOID* ppvData, + __out_opt DWORD* pcbData + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + DWORD cb = 0; + + if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, NULL, &cb)) + { + CrypExitWithLastError(hr, "Failed to get crypt message parameter data size."); + } + + pv = MemAlloc(cb, TRUE); + CrypExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for crypt message parameter."); + + if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, pv, &cb)) + { + CrypExitWithLastError(hr, "Failed to get crypt message parameter."); + } + + *ppvData = pv; + pv = NULL; + + if (pcbData) + { + *pcbData = cb; + } + +LExit: + ReleaseMem(pv); + + return hr; +} + + +extern "C" HRESULT DAPI CrypHashFile( + __in_z LPCWSTR wzFilePath, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash, + __out_opt DWORD64* pqwBytesHashed + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // open input file + hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + CrypExitWithLastError(hr, "Failed to open input file."); + } + + hr = CrypHashFileHandle(hFile, dwProvType, algid, pbHash, cbHash, pqwBytesHashed); + CrypExitOnFailure(hr, "Failed to hash file: %ls", wzFilePath); + +LExit: + ReleaseFileHandle(hFile); + + return hr; +} + + +extern "C" HRESULT DAPI CrypHashFileHandle( + __in HANDLE hFile, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash, + __out_opt DWORD64* pqwBytesHashed + ) +{ + HRESULT hr = S_OK; + HCRYPTPROV hProv = NULL; + HCRYPTHASH hHash = NULL; + DWORD cbRead = 0; + BYTE rgbBuffer[4096] = { }; + const LARGE_INTEGER liZero = { }; + + // get handle to the crypto provider + if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + CrypExitWithLastError(hr, "Failed to acquire crypto context."); + } + + // initiate hash + if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash)) + { + CrypExitWithLastError(hr, "Failed to initiate hash."); + } + + for (;;) + { + // read data block + if (!::ReadFile(hFile, rgbBuffer, sizeof(rgbBuffer), &cbRead, NULL)) + { + CrypExitWithLastError(hr, "Failed to read data block."); + } + + if (!cbRead) + { + break; // end of file + } + + // hash data block + if (!::CryptHashData(hHash, rgbBuffer, cbRead, 0)) + { + CrypExitWithLastError(hr, "Failed to hash data block."); + } + } + + // get hash value + if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) + { + CrypExitWithLastError(hr, "Failed to get hash value."); + } + + if (pqwBytesHashed) + { + if (!::SetFilePointerEx(hFile, liZero, (LARGE_INTEGER*)pqwBytesHashed, FILE_CURRENT)) + { + CrypExitWithLastError(hr, "Failed to get file pointer."); + } + } + +LExit: + if (hHash) + { + ::CryptDestroyHash(hHash); + } + if (hProv) + { + ::CryptReleaseContext(hProv, 0); + } + + return hr; +} + +HRESULT DAPI CrypHashBuffer( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash + ) +{ + HRESULT hr = S_OK; + HCRYPTPROV hProv = NULL; + HCRYPTHASH hHash = NULL; + DWORD cbDataHashed = 0; + SIZE_T cbTotal = 0; + SIZE_T cbRemaining = 0; + + // get handle to the crypto provider + if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + CrypExitWithLastError(hr, "Failed to acquire crypto context."); + } + + // initiate hash + if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash)) + { + CrypExitWithLastError(hr, "Failed to initiate hash."); + } + + do + { + cbRemaining = cbBuffer - cbTotal; + cbDataHashed = (DWORD)min(DWORD_MAX, cbRemaining); + if (!::CryptHashData(hHash, pbBuffer + cbTotal, cbDataHashed, 0)) + { + CrypExitWithLastError(hr, "Failed to hash data."); + } + + cbTotal += cbDataHashed; + } while (cbTotal < cbBuffer); + + // get hash value + if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0)) + { + CrypExitWithLastError(hr, "Failed to get hash value."); + } + +LExit: + if (hHash) + { + ::CryptDestroyHash(hHash); + } + if (hProv) + { + ::CryptReleaseContext(hProv, 0); + } + + return hr; +} + +HRESULT DAPI CrypEncryptMemory( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ) +{ + HRESULT hr = E_FAIL; + + if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE) + { + hr = E_INVALIDARG; + } + else if (vpfnRtlEncryptMemory) + { + hr = static_cast(vpfnRtlEncryptMemory(pData, cbData, dwFlags)); + } + else if (vpfnCryptProtectMemory) + { + if (vpfnCryptProtectMemory(pData, cbData, dwFlags)) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + } + CrypExitOnFailure(hr, "Failed to encrypt memory"); +LExit: + return hr; +} + +HRESULT DAPI CrypDecryptMemory( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ) +{ + HRESULT hr = E_FAIL; + + if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE) + { + hr = E_INVALIDARG; + } + else if (vpfnRtlDecryptMemory) + { + hr = static_cast(vpfnRtlDecryptMemory(pData, cbData, dwFlags)); + } + else if (vpfnCryptUnprotectMemory) + { + if (vpfnCryptUnprotectMemory(pData, cbData, dwFlags)) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + } + CrypExitOnFailure(hr, "Failed to decrypt memory"); +LExit: + return hr; +} + diff --git a/src/libs/dutil/WixToolset.DUtil/deputil.cpp b/src/libs/dutil/WixToolset.DUtil/deputil.cpp new file mode 100644 index 00000000..2e6d6a6c --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/deputil.cpp @@ -0,0 +1,699 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define DepExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__) +#define DepExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DEPUTIL, p, x, e, s, __VA_ARGS__) +#define DepExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, p, x, s, __VA_ARGS__) +#define DepExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DEPUTIL, p, x, e, s, __VA_ARGS__) +#define DepExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, p, x, s, __VA_ARGS__) +#define DepExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEPUTIL, e, x, s, __VA_ARGS__) +#define DepExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DEPUTIL, g, x, s, __VA_ARGS__) + +#define ARRAY_GROWTH_SIZE 5 + +static LPCWSTR vcszVersionValue = L"Version"; +static LPCWSTR vcszDisplayNameValue = L"DisplayName"; +static LPCWSTR vcszMinVersionValue = L"MinVersion"; +static LPCWSTR vcszMaxVersionValue = L"MaxVersion"; +static LPCWSTR vcszAttributesValue = L"Attributes"; + +enum eRequiresAttributes { RequiresAttributesMinVersionInclusive = 256, RequiresAttributesMaxVersionInclusive = 512 }; + +// We write to Software\Classes explicitly based on the current security context instead of HKCR. +// See http://msdn.microsoft.com/en-us/library/ms724475(VS.85).aspx for more information. +static LPCWSTR vsczRegistryRoot = L"Software\\Classes\\Installer\\Dependencies\\"; +static LPCWSTR vsczRegistryDependents = L"Dependents"; + +static HRESULT AllocDependencyKeyName( + __in_z LPCWSTR wzName, + __deref_out_z LPWSTR* psczKeyName + ); + +static HRESULT GetDependencyNameFromKey( + __in HKEY hkHive, + __in LPCWSTR wzKey, + __deref_out_z LPWSTR* psczName + ); + +DAPI_(HRESULT) DepGetProviderInformation( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __deref_out_z_opt LPWSTR* psczId, + __deref_out_z_opt LPWSTR* psczName, + __deref_out_z_opt LPWSTR* psczVersion + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Try to open the dependency key. + hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); + + // Get the Id if requested and available. + if (psczId) + { + hr = RegReadString(hkKey, NULL, psczId); + if (E_FILENOTFOUND == hr) + { + hr = S_OK; + } + DepExitOnFailure(hr, "Failed to get the id for the dependency \"%ls\".", wzProviderKey); + } + + // Get the DisplayName if requested and available. + if (psczName) + { + hr = RegReadString(hkKey, vcszDisplayNameValue, psczName); + if (E_FILENOTFOUND == hr) + { + hr = S_OK; + } + DepExitOnFailure(hr, "Failed to get the name for the dependency \"%ls\".", wzProviderKey); + } + + // Get the Version if requested and available. + if (psczVersion) + { + hr = RegReadString(hkKey, vcszVersionValue, psczVersion); + if (E_FILENOTFOUND == hr) + { + hr = S_OK; + } + DepExitOnFailure(hr, "Failed to get the version for the dependency \"%ls\".", wzProviderKey); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepCheckDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __in_z_opt LPCWSTR wzMinVersion, + __in_z_opt LPCWSTR wzMaxVersion, + __in int iAttributes, + __in STRINGDICT_HANDLE sdDependencies, + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + DWORD64 dw64Version = 0; + DWORD64 dw64MinVersion = 0; + DWORD64 dw64MaxVersion = 0; + BOOL fAllowEqual = FALSE; + LPWSTR sczName = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Try to open the key. If that fails, add the missing dependency key to the dependency array if it doesn't already exist. + hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open the registry key for dependency \"%ls\".", wzProviderKey); + + // If there are no registry values, consider the key orphaned and treat it as missing. + hr = RegReadVersion(hkKey, vcszVersionValue, &dw64Version); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to read the %ls registry value for dependency \"%ls\".", vcszVersionValue, wzProviderKey); + } + } + + // If the key was not found or the Version value was not found, add the missing dependency key to the dependency array. + if (E_FILENOTFOUND == hr) + { + hr = DictKeyExists(sdDependencies, wzProviderKey); + if (E_NOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to check the dictionary for missing dependency \"%ls\".", wzProviderKey); + } + else + { + hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, NULL); + DepExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the array.", wzProviderKey); + + hr = DictAddKey(sdDependencies, wzProviderKey); + DepExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the dictionary.", wzProviderKey); + } + + // Exit since the check already failed. + ExitFunction1(hr = E_NOTFOUND); + } + + // Check MinVersion if provided. + if (wzMinVersion) + { + if (*wzMinVersion) + { + hr = FileVersionFromStringEx(wzMinVersion, 0, &dw64MinVersion); + DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMinVersion); + + fAllowEqual = iAttributes & RequiresAttributesMinVersionInclusive; + if (!(fAllowEqual && dw64MinVersion <= dw64Version || dw64MinVersion < dw64Version)) + { + hr = DictKeyExists(sdDependencies, wzProviderKey); + if (E_NOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to check the dictionary for the older dependency \"%ls\".", wzProviderKey); + } + else + { + hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName); + DepExitOnFailure(hr, "Failed to get the display name of the older dependency \"%ls\".", wzProviderKey); + + hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName); + DepExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the dependencies array.", wzProviderKey); + + hr = DictAddKey(sdDependencies, wzProviderKey); + DepExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the unique dependency string list.", wzProviderKey); + } + + // Exit since the check already failed. + ExitFunction1(hr = E_NOTFOUND); + } + } + } + + // Check MaxVersion if provided. + if (wzMaxVersion) + { + if (*wzMaxVersion) + { + hr = FileVersionFromStringEx(wzMaxVersion, 0, &dw64MaxVersion); + DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMaxVersion); + + fAllowEqual = iAttributes & RequiresAttributesMaxVersionInclusive; + if (!(fAllowEqual && dw64Version <= dw64MaxVersion || dw64Version < dw64MaxVersion)) + { + hr = DictKeyExists(sdDependencies, wzProviderKey); + if (E_NOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to check the dictionary for the newer dependency \"%ls\".", wzProviderKey); + } + else + { + hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName); + DepExitOnFailure(hr, "Failed to get the display name of the newer dependency \"%ls\".", wzProviderKey); + + hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName); + DepExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the dependencies array.", wzProviderKey); + + hr = DictAddKey(sdDependencies, wzProviderKey); + DepExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the unique dependency string list.", wzProviderKey); + } + + // Exit since the check already failed. + ExitFunction1(hr = E_NOTFOUND); + } + } + } + +LExit: + ReleaseStr(sczName); + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepCheckDependents( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __reserved int /*iAttributes*/, + __in C_STRINGDICT_HANDLE sdIgnoredDependents, + __deref_inout_ecount_opt(*pcDependents) DEPENDENCY** prgDependents, + __inout LPUINT pcDependents + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkProviderKey = NULL; + HKEY hkDependentsKey = NULL; + LPWSTR sczDependentKey = NULL; + LPWSTR sczDependentName = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Try to open the key. If that fails, the dependency information is corrupt. + hr = RegOpen(hkHive, sczKey, KEY_READ, &hkProviderKey); + DepExitOnFailure(hr, "Failed to open the registry key \"%ls\". The dependency store is corrupt.", sczKey); + + // Try to open the dependencies key. If that does not exist, there are no dependents. + hr = RegOpen(hkProviderKey, vsczRegistryDependents, KEY_READ, &hkDependentsKey); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open the registry key for dependents of \"%ls\".", wzProviderKey); + } + else + { + ExitFunction1(hr = S_OK); + } + + // Now enumerate the dependent keys. If they are not defined in the ignored list, add them to the array. + for (DWORD dwIndex = 0; ; ++dwIndex) + { + hr = RegKeyEnum(hkDependentsKey, dwIndex, &sczDependentKey); + if (E_NOMOREITEMS != hr) + { + DepExitOnFailure(hr, "Failed to enumerate the dependents key of \"%ls\".", wzProviderKey); + } + else + { + hr = S_OK; + break; + } + + // If the key isn't ignored, add it to the dependent array. + hr = DictKeyExists(sdIgnoredDependents, sczDependentKey); + if (E_NOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); + } + else + { + // Get the name of the dependent from the key. + hr = GetDependencyNameFromKey(hkHive, sczDependentKey, &sczDependentName); + DepExitOnFailure(hr, "Failed to get the name of the dependent from the key \"%ls\".", sczDependentKey); + + hr = DepDependencyArrayAlloc(prgDependents, pcDependents, sczDependentKey, sczDependentName); + DepExitOnFailure(hr, "Failed to add the dependent key \"%ls\" to the string array.", sczDependentKey); + } + } + +LExit: + ReleaseStr(sczDependentName); + ReleaseStr(sczDependentKey); + ReleaseRegKey(hkDependentsKey); + ReleaseRegKey(hkProviderKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepRegisterDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __in_z LPCWSTR wzVersion, + __in_z LPCWSTR wzDisplayName, + __in_z_opt LPCWSTR wzId, + __in int iAttributes + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + BOOL fCreated = FALSE; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Create the dependency key (or open it if it already exists). + hr = RegCreateEx(hkHive, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated); + DepExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczKey); + + // Set the id if it was provided. + if (wzId) + { + hr = RegWriteString(hkKey, NULL, wzId); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", L"default", wzId); + } + + // Set the version. + hr = RegWriteString(hkKey, vcszVersionValue, wzVersion); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszVersionValue, wzVersion); + + // Set the display name. + hr = RegWriteString(hkKey, vcszDisplayNameValue, wzDisplayName); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszDisplayNameValue, wzDisplayName); + + // Set the attributes if non-zero. + if (0 != iAttributes) + { + hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast(iAttributes)); + DepExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepDependentExists( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDependentKey = NULL; + HKEY hkDependentKey = NULL; + + // Format the provider dependents registry key. + hr = StrAllocFormatted(&sczDependentKey, L"%ls%ls\\%ls\\%ls", vsczRegistryRoot, wzDependencyProviderKey, vsczRegistryDependents, wzProviderKey); + DepExitOnFailure(hr, "Failed to format registry key to dependent."); + + hr = RegOpen(hkHive, sczDependentKey, KEY_READ, &hkDependentKey); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open the dependent registry key at: \"%ls\".", sczDependentKey); + } + +LExit: + ReleaseRegKey(hkDependentKey); + ReleaseStr(sczDependentKey); + + return hr; +} + +DAPI_(HRESULT) DepRegisterDependent( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey, + __in_z_opt LPCWSTR wzMinVersion, + __in_z_opt LPCWSTR wzMaxVersion, + __in int iAttributes + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDependencyKey = NULL; + HKEY hkDependencyKey = NULL; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + BOOL fCreated = FALSE; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzDependencyProviderKey, &sczDependencyKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzDependencyProviderKey); + + // Create the dependency key (or open it if it already exists). + hr = RegCreateEx(hkHive, sczDependencyKey, KEY_WRITE, FALSE, NULL, &hkDependencyKey, &fCreated); + DepExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczDependencyKey); + + // Create the subkey to register the dependent. + hr = StrAllocFormatted(&sczKey, L"%ls\\%ls", vsczRegistryDependents, wzProviderKey); + DepExitOnFailure(hr, "Failed to allocate dependent subkey \"%ls\" under dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); + + hr = RegCreateEx(hkDependencyKey, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated); + DepExitOnFailure(hr, "Failed to create the dependency subkey \"%ls\".", sczKey); + + // Set the minimum version if not NULL. + hr = RegWriteString(hkKey, vcszMinVersionValue, wzMinVersion); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMinVersionValue, wzMinVersion); + + // Set the maximum version if not NULL. + hr = RegWriteString(hkKey, vcszMaxVersionValue, wzMaxVersion); + DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMaxVersionValue, wzMaxVersion); + + // Set the attributes if non-zero. + if (0 != iAttributes) + { + hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast(iAttributes)); + DepExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + ReleaseRegKey(hkDependencyKey); + ReleaseStr(sczDependencyKey); + + return hr; +} + +DAPI_(HRESULT) DepUnregisterDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Delete the entire key including all sub-keys. + hr = RegDelete(hkHive, sczKey, REG_KEY_DEFAULT, TRUE); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to delete the key \"%ls\".", sczKey); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +DAPI_(HRESULT) DepUnregisterDependent( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey + ) +{ + HRESULT hr = S_OK; + HKEY hkRegistryRoot = NULL; + HKEY hkDependencyProviderKey = NULL; + HKEY hkRegistryDependents = NULL; + DWORD cSubKeys = 0; + DWORD cValues = 0; + + // Open the root key. We may delete the wzDependencyProviderKey during clean up. + hr = RegOpen(hkHive, vsczRegistryRoot, KEY_READ, &hkRegistryRoot); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open root registry key \"%ls\".", vsczRegistryRoot); + } + else + { + ExitFunction(); + } + + // Try to open the dependency key. If that does not exist, simply return. + hr = RegOpen(hkRegistryRoot, wzDependencyProviderKey, KEY_READ, &hkDependencyProviderKey); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzDependencyProviderKey); + } + else + { + ExitFunction(); + } + + // Try to open the dependents subkey to enumerate. + hr = RegOpen(hkDependencyProviderKey, vsczRegistryDependents, KEY_READ, &hkRegistryDependents); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); + } + else + { + ExitFunction(); + } + + // Delete the wzProviderKey dependent sub-key. + hr = RegDelete(hkRegistryDependents, wzProviderKey, REG_KEY_DEFAULT, TRUE); + DepExitOnFailure(hr, "Failed to delete the dependent \"%ls\" under the dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey); + + // If there are no remaining dependents, delete the Dependents subkey. + hr = RegQueryKey(hkRegistryDependents, &cSubKeys, NULL); + DepExitOnFailure(hr, "Failed to get the number of dependent subkeys under the dependency \"%ls\".", wzDependencyProviderKey); + + if (0 < cSubKeys) + { + ExitFunction(); + } + + // Release the handle to make sure it's deleted immediately. + ReleaseRegKey(hkRegistryDependents); + + // Fail if there are any subkeys since we just checked. + hr = RegDelete(hkDependencyProviderKey, vsczRegistryDependents, REG_KEY_DEFAULT, FALSE); + DepExitOnFailure(hr, "Failed to delete the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey); + + // If there are no values, delete the provider dependency key. + hr = RegQueryKey(hkDependencyProviderKey, NULL, &cValues); + DepExitOnFailure(hr, "Failed to get the number of values under the dependency \"%ls\".", wzDependencyProviderKey); + + if (0 == cValues) + { + // Release the handle to make sure it's deleted immediately. + ReleaseRegKey(hkDependencyProviderKey); + + // Fail if there are any subkeys since we just checked. + hr = RegDelete(hkRegistryRoot, wzDependencyProviderKey, REG_KEY_DEFAULT, FALSE); + DepExitOnFailure(hr, "Failed to delete the dependency \"%ls\".", wzDependencyProviderKey); + } + +LExit: + ReleaseRegKey(hkRegistryDependents); + ReleaseRegKey(hkDependencyProviderKey); + ReleaseRegKey(hkRegistryRoot); + + return hr; +} + +DAPI_(HRESULT) DepDependencyArrayAlloc( + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies, + __in_z LPCWSTR wzKey, + __in_z_opt LPCWSTR wzName + ) +{ + HRESULT hr = S_OK; + UINT cRequired = 0; + DEPENDENCY* pDependency = NULL; + + hr = ::UIntAdd(*pcDependencies, 1, &cRequired); + DepExitOnFailure(hr, "Failed to increment the number of elements required in the dependency array."); + + hr = MemEnsureArraySize(reinterpret_cast(prgDependencies), cRequired, sizeof(DEPENDENCY), ARRAY_GROWTH_SIZE); + DepExitOnFailure(hr, "Failed to allocate memory for the dependency array."); + + pDependency = static_cast(&(*prgDependencies)[*pcDependencies]); + DepExitOnNull(pDependency, hr, E_POINTER, "The dependency element in the array is invalid."); + + hr = StrAllocString(&(pDependency->sczKey), wzKey, 0); + DepExitOnFailure(hr, "Failed to allocate the string key in the dependency array."); + + if (wzName) + { + hr = StrAllocString(&(pDependency->sczName), wzName, 0); + DepExitOnFailure(hr, "Failed to allocate the string name in the dependency array."); + } + + // Update the number of current elements in the dependency array. + *pcDependencies = cRequired; + +LExit: + return hr; +} + +DAPI_(void) DepDependencyArrayFree( + __in_ecount(cDependencies) DEPENDENCY* rgDependencies, + __in UINT cDependencies + ) +{ + for (UINT i = 0; i < cDependencies; ++i) + { + ReleaseStr(rgDependencies[i].sczKey); + ReleaseStr(rgDependencies[i].sczName); + } + + ReleaseMem(rgDependencies); +} + +/*************************************************************************** + AllocDependencyKeyName - Allocates and formats the root registry key name. + +***************************************************************************/ +static HRESULT AllocDependencyKeyName( + __in_z LPCWSTR wzName, + __deref_out_z LPWSTR* psczKeyName + ) +{ + HRESULT hr = S_OK; + size_t cchName = 0; + size_t cchKeyName = 0; + + // Get the length of the static registry root once. + static size_t cchRegistryRoot = ::lstrlenW(vsczRegistryRoot); + + // Get the length of the dependency, and add to the length of the root. + hr = ::StringCchLengthW(wzName, STRSAFE_MAX_CCH, &cchName); + DepExitOnFailure(hr, "Failed to get string length of dependency name."); + + // Add the sizes together to allocate memory once (callee will add space for nul). + hr = ::SizeTAdd(cchRegistryRoot, cchName, &cchKeyName); + DepExitOnFailure(hr, "Failed to add the string lengths together."); + + // Allocate and concat the strings together. + hr = StrAllocString(psczKeyName, vsczRegistryRoot, cchKeyName); + DepExitOnFailure(hr, "Failed to allocate string for dependency registry root."); + + hr = StrAllocConcat(psczKeyName, wzName, cchName); + DepExitOnFailure(hr, "Failed to concatenate the dependency key name."); + +LExit: + return hr; +} + +/*************************************************************************** + GetDependencyNameFromKey - Attempts to name of the dependency from the key. + +***************************************************************************/ +static HRESULT GetDependencyNameFromKey( + __in HKEY hkHive, + __in LPCWSTR wzProviderKey, + __deref_out_z LPWSTR* psczName + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + + // Format the provider dependency registry key. + hr = AllocDependencyKeyName(wzProviderKey, &sczKey); + DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey); + + // Try to open the dependency key. + hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey); + } + else + { + ExitFunction1(hr = S_OK); + } + + // Get the DisplayName if available. + hr = RegReadString(hkKey, vcszDisplayNameValue, psczName); + if (E_FILENOTFOUND != hr) + { + DepExitOnFailure(hr, "Failed to get the dependency name for the dependency \"%ls\".", wzProviderKey); + } + else + { + ExitFunction1(hr = S_OK); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/dictutil.cpp b/src/libs/dutil/WixToolset.DUtil/dictutil.cpp new file mode 100644 index 00000000..0d0743eb --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/dictutil.cpp @@ -0,0 +1,784 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define DictExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__) +#define DictExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__) +#define DictExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__) +#define DictExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__) +#define DictExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__) +#define DictExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DICTUTIL, e, x, s, __VA_ARGS__) +#define DictExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DICTUTIL, g, x, s, __VA_ARGS__) + +// These should all be primes, and spaced reasonably apart (currently each is about 4x the last) +const DWORD MAX_BUCKET_SIZES[] = { + 503, + 2017, + 7937, + 32779, + 131111, + 524341, + 2097709, + 8390857, + 33563437, + 134253719, + 537014927, + 2148059509 + }; + +// However many items are in the cab, let's keep the buckets at least 8 times that to avoid collisions +#define MAX_BUCKETS_TO_ITEMS_RATIO 8 + +enum DICT_TYPE +{ + DICT_INVALID = 0, + DICT_EMBEDDED_KEY = 1, + DICT_STRING_LIST = 2 +}; + +struct STRINGDICT_STRUCT +{ + DICT_TYPE dtType; + + // Optional flags to control the behavior of the dictionary. + DICT_FLAG dfFlags; + + // Index into MAX_BUCKET_SIZES (array of primes), representing number of buckets we've allocated + DWORD dwBucketSizeIndex; + + // Number of items currently stored in the dict buckets + DWORD dwNumItems; + + // Byte offset of key within bucket value, for collision checking - see + // comments above DictCreateEmbeddedKey() implementation for further details + size_t cByteOffset; + + // The actual stored buckets + void **ppvBuckets; + + // The actual stored items in the order they were added (used for auto freeing or enumerating) + void **ppvItemList; + + // Pointer to the array of items, so the caller is free to resize the array of values out from under us without harm + void **ppvValueArray; +}; + +const int STRINGDICT_HANDLE_BYTES = sizeof(STRINGDICT_STRUCT); + +static HRESULT StringHash( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwNumBuckets, + __in_z LPCWSTR pszString, + __out DWORD *pdwHash + ); +static BOOL IsMatchExact( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwMatchIndex, + __in_z LPCWSTR wzOriginalString + ); +static HRESULT GetValue( + __in const STRINGDICT_STRUCT *psd, + __in_z LPCWSTR pszString, + __out_opt void **ppvValue + ); +static HRESULT GetInsertIndex( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwBucketCount, + __in void **ppvBuckets, + __in_z LPCWSTR pszString, + __out DWORD *pdwOutput + ); +static HRESULT GetIndex( + __in const STRINGDICT_STRUCT *psd, + __in_z LPCWSTR pszString, + __out DWORD *pdwOutput + ); +static LPCWSTR GetKey( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ); +static HRESULT GrowDictionary( + __inout STRINGDICT_STRUCT *psd + ); +// These 2 helper functions allow us to safely handle dictutil consumers resizing +// the value array by storing "offsets" instead of raw void *'s in our buckets. +static void * TranslateOffsetToValue( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ); +static void * TranslateValueToOffset( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ); + +// The dict will store a set of keys (as wide-char strings) and a set of values associated with those keys (as void *'s). +// However, to support collision checking, the key needs to be represented in the "value" object (pointed to +// by the void *). The "stByteOffset" parameter tells this dict the byte offset of the "key" string pointer +// within the "value" object. Use the offsetof() macro to fill this out. +// The "ppvArray" parameter gives dictutil the address of your value array. If you provide this parameter, +// dictutil will remember all pointer values provided as "offsets" against this array. It is only necessary to provide +// this parameter to dictutil if it is possible you will realloc the array. +// +// Use DictAddValue() and DictGetValue() with this dictionary type. +extern "C" HRESULT DAPI DictCreateWithEmbeddedKey( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in DWORD dwNumExpectedItems, + __in_opt void **ppvArray, + __in size_t cByteOffset, + __in DICT_FLAG dfFlags + ) +{ + HRESULT hr = S_OK; + + DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); + + // Allocate the handle + *psdHandle = static_cast(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE)); + DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); + + STRINGDICT_STRUCT *psd = static_cast(*psdHandle); + + // Fill out the new handle's values + psd->dtType = DICT_EMBEDDED_KEY; + psd->dfFlags = dfFlags; + psd->cByteOffset = cByteOffset; + psd->dwBucketSizeIndex = 0; + psd->dwNumItems = 0; + psd->ppvItemList = NULL; + psd->ppvValueArray = ppvArray; + + // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime + // array based on expected number of items and items to buckets ratio + // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end + // this loop past the end of the array! + while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) && + MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO) + { + ++psd->dwBucketSizeIndex; + } + + // Finally, allocate our initial buckets + psd->ppvBuckets = static_cast(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE)); + DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); + +LExit: + return hr; +} + +// The dict will store a set of keys, with no values associated with them. Use DictAddKey() and DictKeyExists() with this dictionary type. +extern "C" HRESULT DAPI DictCreateStringList( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in DWORD dwNumExpectedItems, + __in DICT_FLAG dfFlags + ) +{ + HRESULT hr = S_OK; + + DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict"); + + // Allocate the handle + *psdHandle = static_cast(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE)); + DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object"); + + STRINGDICT_STRUCT *psd = static_cast(*psdHandle); + + // Fill out the new handle's values + psd->dtType = DICT_STRING_LIST; + psd->dfFlags = dfFlags; + psd->cByteOffset = 0; + psd->dwBucketSizeIndex = 0; + psd->dwNumItems = 0; + psd->ppvItemList = NULL; + psd->ppvValueArray = NULL; + + // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime + // array based on expected number of items and items to buckets ratio + // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end + // this loop past the end of the array! + while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) && + MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO) + { + ++psd->dwBucketSizeIndex; + } + + // Finally, allocate our initial buckets + psd->ppvBuckets = static_cast(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE)); + DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI DictCreateStringListFromArray( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, + __in const DWORD cStringArray, + __in DICT_FLAG dfFlags + ) +{ + HRESULT hr = S_OK; + STRINGDICT_HANDLE sd = NULL; + + hr = DictCreateStringList(&sd, cStringArray, dfFlags); + DictExitOnFailure(hr, "Failed to create the string dictionary."); + + for (DWORD i = 0; i < cStringArray; ++i) + { + const LPCWSTR wzKey = rgwzStringArray[i]; + + hr = DictKeyExists(sd, wzKey); + if (E_NOTFOUND != hr) + { + DictExitOnFailure(hr, "Failed to check the string dictionary."); + } + else + { + hr = DictAddKey(sd, wzKey); + DictExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzKey); + } + } + + *psdHandle = sd; + sd = NULL; + +LExit: + ReleaseDict(sd); + + return hr; +} + +extern "C" HRESULT DAPI DictCompareStringListToArray( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList, + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, + __in const DWORD cStringArray + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cStringArray; ++i) + { + hr = DictKeyExists(sdStringList, rgwzStringArray[i]); + if (E_NOTFOUND != hr) + { + DictExitOnFailure(hr, "Failed to check the string dictionary."); + ExitFunction1(hr = S_OK); + } + } + + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_NO_MATCH)); + +LExit: + return hr; +} + +// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO) +extern "C" HRESULT DAPI DictAddKey( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR pszString + ) +{ + HRESULT hr = S_OK; + DWORD dwIndex = 0; + STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); + DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while adding value to dict"); + + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + } + + if (DICT_STRING_LIST != psd->dtType) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Tried to add key without value to wrong dictionary type! This dictionary type is: %d", psd->dtType); + } + + if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO) + { + hr = GrowDictionary(psd); + if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr) + { + // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full + if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) + { + hr = S_OK; + } + } + DictExitOnFailure(hr, "Failed to grow dictionary"); + } + + hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, pszString, &dwIndex); + DictExitOnFailure(hr, "Failed to get index to insert into"); + + hr = MemEnsureArraySize(reinterpret_cast(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000); + DictExitOnFailure(hr, "Failed to resize list of items in dictionary"); + ++psd->dwNumItems; + + hr = StrAllocString(reinterpret_cast(&(psd->ppvBuckets[dwIndex])), pszString, 0); + DictExitOnFailure(hr, "Failed to allocate copy of string"); + + psd->ppvItemList[psd->dwNumItems-1] = psd->ppvBuckets[dwIndex]; + +LExit: + return hr; +} + +// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO) +extern "C" HRESULT DAPI DictAddValue( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, + __in void *pvValue + ) +{ + HRESULT hr = S_OK; + void *pvOffset = NULL; + LPCWSTR wzKey = NULL; + DWORD dwIndex = 0; + STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict"); + DictExitOnNull(pvValue, hr, E_INVALIDARG, "Value not specified while adding value to dict"); + + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + } + + if (DICT_EMBEDDED_KEY != psd->dtType) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Tried to add key/value pair to wrong dictionary type! This dictionary type is: %d", psd->dtType); + } + + wzKey = GetKey(psd, pvValue); + DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified while adding value to dict"); + + if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO) + { + hr = GrowDictionary(psd); + if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr && psd->dwNumItems + 1 ) + { + // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full + if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) + { + hr = S_OK; + } + } + DictExitOnFailure(hr, "Failed to grow dictionary"); + } + + hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, wzKey, &dwIndex); + DictExitOnFailure(hr, "Failed to get index to insert into"); + + hr = MemEnsureArraySize(reinterpret_cast(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000); + DictExitOnFailure(hr, "Failed to resize list of items in dictionary"); + ++psd->dwNumItems; + + pvOffset = TranslateValueToOffset(psd, pvValue); + psd->ppvBuckets[dwIndex] = pvOffset; + psd->ppvItemList[psd->dwNumItems-1] = pvOffset; + +LExit: + return hr; +} + +extern "C" HRESULT DAPI DictGetValue( + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR pszString, + __out void **ppvValue + ) +{ + HRESULT hr = S_OK; + + DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); + DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); + + const STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + if (DICT_EMBEDDED_KEY != psd->dtType) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Tried to lookup value in wrong dictionary type! This dictionary type is: %d", psd->dtType); + } + + hr = GetValue(psd, pszString, ppvValue); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + DictExitOnFailure(hr, "Failed to call internal GetValue()"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI DictKeyExists( + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR pszString + ) +{ + HRESULT hr = S_OK; + + DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict"); + DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); + + const STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + // This works with either type of dictionary + hr = GetValue(psd, pszString, NULL); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + DictExitOnFailure(hr, "Failed to call internal GetValue()"); + +LExit: + return hr; +} + +extern "C" void DAPI DictDestroy( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle + ) +{ + DWORD i; + + STRINGDICT_STRUCT *psd = static_cast(sdHandle); + + if (DICT_STRING_LIST == psd->dtType) + { + for (i = 0; i < psd->dwNumItems; ++i) + { + ReleaseStr(reinterpret_cast(psd->ppvItemList[i])); + } + } + + ReleaseMem(psd->ppvItemList); + ReleaseMem(psd->ppvBuckets); + ReleaseMem(psd); +} + +static HRESULT StringHash( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwNumBuckets, + __in_z LPCWSTR pszString, + __out DWORD *pdwHash + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzKey = NULL; + LPWSTR sczNewKey = NULL; + DWORD result = 0; + + if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags) + { + hr = StrAllocStringToUpperInvariant(&sczNewKey, pszString, 0); + DictExitOnFailure(hr, "Failed to convert the string to upper-case."); + + wzKey = sczNewKey; + } + else + { + wzKey = pszString; + } + + while (*wzKey) + { + result = ~(*wzKey++ * 509) + result * 65599; + } + + *pdwHash = result % dwNumBuckets; + +LExit: + ReleaseStr(sczNewKey); + + return hr; +} + +static BOOL IsMatchExact( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwMatchIndex, + __in_z LPCWSTR wzOriginalString + ) +{ + LPCWSTR wzMatchString = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvBuckets[dwMatchIndex])); + DWORD dwFlags = 0; + + if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags) + { + dwFlags |= NORM_IGNORECASE; + } + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwFlags, wzOriginalString, -1, wzMatchString, -1)) + { + return TRUE; + } + + return FALSE; +} + +static HRESULT GetValue( + __in const STRINGDICT_STRUCT *psd, + __in_z LPCWSTR pszString, + __out_opt void **ppvValue + ) +{ + HRESULT hr = S_OK; + DWORD dwOriginalIndexCandidate = 0; + void *pvCandidateValue = NULL; + DWORD dwIndex = 0; + + DictExitOnNull(psd, hr, E_INVALIDARG, "Handle not specified while searching dict"); + DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict"); + + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + } + + hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate); + DictExitOnFailure(hr, "Failed to hash the string."); + + DWORD dwIndexCandidate = dwOriginalIndexCandidate; + + pvCandidateValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndexCandidate]); + + // If no match exists in the dict + if (NULL == pvCandidateValue) + { + if (NULL != ppvValue) + { + *ppvValue = NULL; + } + ExitFunction1(hr = E_NOTFOUND); + } + + hr = GetIndex(psd, pszString, &dwIndex); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + DictExitOnFailure(hr, "Failed to find index to get"); + + if (NULL != ppvValue) + { + *ppvValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndex]); + } + +LExit: + if (FAILED(hr) && NULL != ppvValue) + { + *ppvValue = NULL; + } + + return hr; +} + +static HRESULT GetInsertIndex( + __in const STRINGDICT_STRUCT *psd, + __in DWORD dwBucketCount, + __in void **ppvBuckets, + __in_z LPCWSTR pszString, + __out DWORD *pdwOutput + ) +{ + HRESULT hr = S_OK; + DWORD dwOriginalIndexCandidate = 0; + + hr = StringHash(psd, dwBucketCount, pszString, &dwOriginalIndexCandidate); + DictExitOnFailure(hr, "Failed to hash the string."); + + DWORD dwIndexCandidate = dwOriginalIndexCandidate; + + // If we collide, keep iterating forward from our intended position, even wrapping around to zero, until we find an empty bucket +#pragma prefast(push) +#pragma prefast(disable:26007) + while (NULL != ppvBuckets[dwIndexCandidate]) +#pragma prefast(pop) + { + ++dwIndexCandidate; + + // If we got to the end of the array, wrap around to zero index + if (dwIndexCandidate >= dwBucketCount) + { + dwIndexCandidate = 0; + } + + // If we wrapped all the way back around to our original index, the dict is full - throw an error + if (dwIndexCandidate == dwOriginalIndexCandidate) + { + // The dict table is full - this error seems to be a reasonably close match + hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL); + DictExitOnRootFailure(hr, "Failed to add item '%ls' to dict table because dict table is full of items", pszString); + } + } + + *pdwOutput = dwIndexCandidate; + +LExit: + return hr; +} + +static HRESULT GetIndex( + __in const STRINGDICT_STRUCT *psd, + __in_z LPCWSTR pszString, + __out DWORD *pdwOutput + ) +{ + HRESULT hr = S_OK; + DWORD dwOriginalIndexCandidate = 0; + + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + hr = E_INVALIDARG; + DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range"); + } + + hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate); + DictExitOnFailure(hr, "Failed to hash the string."); + + DWORD dwIndexCandidate = dwOriginalIndexCandidate; + + while (!IsMatchExact(psd, dwIndexCandidate, pszString)) + { + ++dwIndexCandidate; + + // If we got to the end of the array, wrap around to zero index + if (dwIndexCandidate >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex]) + { + dwIndexCandidate = 0; + } + + // If no match exists in the dict + if (NULL == psd->ppvBuckets[dwIndexCandidate]) + { + ExitFunction1(hr = E_NOTFOUND); + } + + // If we wrapped all the way back around to our original index, the dict is full and we found nothing, so return as such + if (dwIndexCandidate == dwOriginalIndexCandidate) + { + ExitFunction1(hr = E_NOTFOUND); + } + } + + *pdwOutput = dwIndexCandidate; + +LExit: + return hr; +} + +static LPCWSTR GetKey( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ) +{ + const BYTE *lpByte = reinterpret_cast(pvValue); + + if (DICT_EMBEDDED_KEY == psd->dtType) + { + void *pvKey = reinterpret_cast(reinterpret_cast(pvValue) + psd->cByteOffset); + +#pragma prefast(push) +#pragma prefast(disable:26010) + return *(reinterpret_cast(pvKey)); +#pragma prefast(pop) + } + else + { + return (reinterpret_cast(lpByte)); + } +} + +static HRESULT GrowDictionary( + __inout STRINGDICT_STRUCT *psd + ) +{ + HRESULT hr = S_OK; + DWORD dwInsertIndex = 0; + LPCWSTR wzKey = NULL; + DWORD dwNewBucketSizeIndex = 0; + size_t cbAllocSize = 0; + void **ppvNewBuckets = NULL; + + dwNewBucketSizeIndex = psd->dwBucketSizeIndex + 1; + + if (dwNewBucketSizeIndex >= countof(MAX_BUCKET_SIZES)) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL)); + } + + hr = ::SizeTMult(sizeof(void *), MAX_BUCKET_SIZES[dwNewBucketSizeIndex], &cbAllocSize); + DictExitOnFailure(hr, "Overflow while calculating allocation size to grow dictionary"); + + ppvNewBuckets = static_cast(MemAlloc(cbAllocSize, TRUE)); + DictExitOnNull(ppvNewBuckets, hr, E_OUTOFMEMORY, "Failed to allocate %u buckets while growing dictionary", MAX_BUCKET_SIZES[dwNewBucketSizeIndex]); + + for (DWORD i = 0; i < psd->dwNumItems; ++i) + { + wzKey = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvItemList[i])); + DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified in existing dict value"); + + hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[dwNewBucketSizeIndex], ppvNewBuckets, wzKey, &dwInsertIndex); + DictExitOnFailure(hr, "Failed to get index to insert into"); + + ppvNewBuckets[dwInsertIndex] = psd->ppvItemList[i]; + } + + psd->dwBucketSizeIndex = dwNewBucketSizeIndex; + ReleaseMem(psd->ppvBuckets); + psd->ppvBuckets = ppvNewBuckets; + ppvNewBuckets = NULL; + +LExit: + ReleaseMem(ppvNewBuckets); + + return hr; +} + +static void * TranslateOffsetToValue( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ) +{ + if (NULL == pvValue) + { + return NULL; + } + + // All offsets are stored as (real offset + 1), so subtract 1 to get back to the real value + if (NULL != psd->ppvValueArray) + { + return reinterpret_cast(reinterpret_cast(pvValue) + reinterpret_cast(*psd->ppvValueArray) - 1); + } + else + { + return pvValue; + } +} + +static void * TranslateValueToOffset( + __in const STRINGDICT_STRUCT *psd, + __in void *pvValue + ) +{ + if (NULL != psd->ppvValueArray) + { + // 0 has a special meaning - we don't want offset 0 into the array to have NULL for the offset - so add 1 to avoid this issue + return reinterpret_cast(reinterpret_cast(pvValue) - reinterpret_cast(*psd->ppvValueArray) + 1); + } + else + { + return pvValue; + } +} diff --git a/src/libs/dutil/WixToolset.DUtil/dirutil.cpp b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp new file mode 100644 index 00000000..ae2c5e1c --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp @@ -0,0 +1,441 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define DirExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) +#define DirExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__) +#define DirExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__) +#define DirExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__) +#define DirExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__) +#define DirExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DIRUTIL, e, x, s, __VA_ARGS__) +#define DirExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DIRUTIL, g, x, s, __VA_ARGS__) + + +/******************************************************************* + DirExists + +*******************************************************************/ +extern "C" BOOL DAPI DirExists( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ) +{ + Assert(wzPath); + + BOOL fExists = FALSE; + + DWORD dwAttributes = ::GetFileAttributesW(wzPath); + if (0xFFFFFFFF == dwAttributes) // TODO: figure out why "INVALID_FILE_ATTRIBUTES" can't be used here + { + ExitFunction(); + } + + if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (pdwAttributes) + { + *pdwAttributes = dwAttributes; + } + + fExists = TRUE; + } + +LExit: + return fExists; +} + + +/******************************************************************* + DirCreateTempPath + + *******************************************************************/ +extern "C" HRESULT DAPI DirCreateTempPath( + __in_z LPCWSTR wzPrefix, + __out_ecount_z(cchPath) LPWSTR wzPath, + __in DWORD cchPath + ) +{ + Assert(wzPrefix); + Assert(wzPath); + + HRESULT hr = S_OK; + + WCHAR wzDir[MAX_PATH]; + WCHAR wzFile[MAX_PATH]; + DWORD cch = 0; + + cch = ::GetTempPathW(countof(wzDir), wzDir); + if (!cch || cch >= countof(wzDir)) + { + DirExitWithLastError(hr, "Failed to GetTempPath."); + } + + if (!::GetTempFileNameW(wzDir, wzPrefix, 0, wzFile)) + { + DirExitWithLastError(hr, "Failed to GetTempFileName."); + } + + hr = ::StringCchCopyW(wzPath, cchPath, wzFile); + +LExit: + return hr; +} + + +/******************************************************************* + DirEnsureExists + +*******************************************************************/ +extern "C" HRESULT DAPI DirEnsureExists( + __in_z LPCWSTR wzPath, + __in_opt LPSECURITY_ATTRIBUTES psa + ) +{ + HRESULT hr = S_OK; + UINT er; + + // try to create this directory + if (!::CreateDirectoryW(wzPath, psa)) + { + // if the directory already exists, bail + er = ::GetLastError(); + if (ERROR_ALREADY_EXISTS == er) + { + ExitFunction1(hr = S_OK); + } + else if (ERROR_PATH_NOT_FOUND != er && DirExists(wzPath, NULL)) // if the directory happens to exist (don't check if CreateDirectory said it doesn't), declare success. + { + ExitFunction1(hr = S_OK); + } + + // get the parent path and try to create it + LPWSTR pwzLastSlash = NULL; + for (LPWSTR pwz = const_cast(wzPath); *pwz; ++pwz) + { + if (*pwz == L'\\') + { + pwzLastSlash = pwz; + } + } + + // if there is no parent directory fail + DirExitOnNullDebugTrace(pwzLastSlash, hr, HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), "cannot find parent path"); + + *pwzLastSlash = L'\0'; // null terminate the parent path + hr = DirEnsureExists(wzPath, psa); // recurse! + *pwzLastSlash = L'\\'; // put the slash back + DirExitOnFailureDebugTrace(hr, "failed to create path: %ls", wzPath); + + // try to create the directory now that all parents are created + if (!::CreateDirectoryW(wzPath, psa)) + { + // if the directory already exists for some reason no error + er = ::GetLastError(); + if (ERROR_ALREADY_EXISTS == er) + { + hr = S_FALSE; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + } + else + { + hr = S_OK; + } + } + +LExit: + return hr; +} + + +/******************************************************************* + DirEnsureDelete - removes an entire directory structure + +*******************************************************************/ +extern "C" HRESULT DAPI DirEnsureDelete( + __in_z LPCWSTR wzPath, + __in BOOL fDeleteFiles, + __in BOOL fRecurse + ) +{ + HRESULT hr = S_OK; + DWORD dwDeleteFlags = 0; + + dwDeleteFlags |= fDeleteFiles ? DIR_DELETE_FILES : 0; + dwDeleteFlags |= fRecurse ? DIR_DELETE_RECURSE : 0; + + hr = DirEnsureDeleteEx(wzPath, dwDeleteFlags); + return hr; +} + + +/******************************************************************* + DirEnsureDeleteEx - removes an entire directory structure + +*******************************************************************/ +extern "C" HRESULT DAPI DirEnsureDeleteEx( + __in_z LPCWSTR wzPath, + __in DWORD dwFlags + ) +{ + Assert(wzPath && *wzPath); + + HRESULT hr = S_OK; + DWORD er; + + DWORD dwAttrib; + HANDLE hFind = INVALID_HANDLE_VALUE; + LPWSTR sczDelete = NULL; + WIN32_FIND_DATAW wfd; + + BOOL fDeleteFiles = (DIR_DELETE_FILES == (dwFlags & DIR_DELETE_FILES)); + BOOL fRecurse = (DIR_DELETE_RECURSE == (dwFlags & DIR_DELETE_RECURSE)); + BOOL fScheduleDelete = (DIR_DELETE_SCHEDULE == (dwFlags & DIR_DELETE_SCHEDULE)); + WCHAR wzTempDirectory[MAX_PATH] = { }; + WCHAR wzTempPath[MAX_PATH] = { }; + + if (-1 == (dwAttrib = ::GetFileAttributesW(wzPath))) + { + er = ::GetLastError(); + if (ERROR_FILE_NOT_FOUND == er) // change "file not found" to "path not found" since we were looking for a directory. + { + er = ERROR_PATH_NOT_FOUND; + } + hr = HRESULT_FROM_WIN32(er); + DirExitOnRootFailure(hr, "Failed to get attributes for path: %ls", wzPath); + } + + if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) + { + if (dwAttrib & FILE_ATTRIBUTE_READONLY) + { + if (!::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL)) + { + DirExitWithLastError(hr, "Failed to remove read-only attribute from path: %ls", wzPath); + } + } + + // If we're deleting files and/or child directories loop through the contents of the directory. + if (fDeleteFiles || fRecurse) + { + if (fScheduleDelete) + { + if (!::GetTempPathW(countof(wzTempDirectory), wzTempDirectory)) + { + DirExitWithLastError(hr, "Failed to get temp directory."); + } + } + + // Delete everything in this directory. + hr = PathConcat(wzPath, L"*.*", &sczDelete); + DirExitOnFailure(hr, "Failed to concat wild cards to string: %ls", wzPath); + + hFind = ::FindFirstFileW(sczDelete, &wfd); + if (INVALID_HANDLE_VALUE == hFind) + { + DirExitWithLastError(hr, "failed to get first file in directory: %ls", wzPath); + } + + do + { + // Skip the dot directories. + if (L'.' == wfd.cFileName[0] && (L'\0' == wfd.cFileName[1] || (L'.' == wfd.cFileName[1] && L'\0' == wfd.cFileName[2]))) + { + continue; + } + + // For extra safety and to silence OACR. + wfd.cFileName[MAX_PATH - 1] = L'\0'; + + hr = PathConcat(wzPath, wfd.cFileName, &sczDelete); + DirExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath); + + if (fRecurse && wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + hr = PathBackslashTerminate(&sczDelete); + DirExitOnFailure(hr, "Failed to ensure path is backslash terminated: %ls", sczDelete); + + hr = DirEnsureDeleteEx(sczDelete, dwFlags); // recursive call + if (FAILED(hr)) + { + // if we failed to delete a subdirectory, keep trying to finish any remaining files + ExitTraceSource(DUTIL_SOURCE_DIRUTIL, hr, "Failed to delete subdirectory; continuing: %ls", sczDelete); + hr = S_OK; + } + } + else if (fDeleteFiles) // this is a file, just delete it + { + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY || wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN || wfd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) + { + if (!::SetFileAttributesW(sczDelete, FILE_ATTRIBUTE_NORMAL)) + { + DirExitWithLastError(hr, "Failed to remove attributes from file: %ls", sczDelete); + } + } + + if (!::DeleteFileW(sczDelete)) + { + if (fScheduleDelete) + { + if (!::GetTempFileNameW(wzTempDirectory, L"DEL", 0, wzTempPath)) + { + DirExitWithLastError(hr, "Failed to get temp file to move to."); + } + + // Try to move the file to the temp directory then schedule for delete, + // otherwise just schedule for delete. + if (::MoveFileExW(sczDelete, wzTempPath, MOVEFILE_REPLACE_EXISTING)) + { + ::MoveFileExW(wzTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); + } + else + { + ::MoveFileExW(sczDelete, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); + } + } + else + { + DirExitWithLastError(hr, "Failed to delete file: %ls", sczDelete); + } + } + } + } while (::FindNextFileW(hFind, &wfd)); + + er = ::GetLastError(); + if (ERROR_NO_MORE_FILES == er) + { + hr = S_OK; + } + else + { + DirExitWithLastError(hr, "Failed while looping through files in directory: %ls", wzPath); + } + } + + if (!::RemoveDirectoryW(wzPath)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr && fScheduleDelete) + { + if (::MoveFileExW(wzPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)) + { + hr = S_OK; + } + } + + DirExitOnRootFailure(hr, "Failed to remove directory: %ls", wzPath); + } + } + else + { + hr = E_UNEXPECTED; + DirExitOnFailure(hr, "Directory delete cannot delete file: %ls", wzPath); + } + + Assert(S_OK == hr); + +LExit: + ReleaseFileFindHandle(hFind); + ReleaseStr(sczDelete); + + return hr; +} + + +/******************************************************************* +DirDeleteEmptyDirectoriesToRoot - removes an empty directory and as many + of its parents as possible. + + Returns: count of directories deleted. +*******************************************************************/ +extern "C" DWORD DAPI DirDeleteEmptyDirectoriesToRoot( + __in_z LPCWSTR wzPath, + __in DWORD /*dwFlags*/ + ) +{ + DWORD cDeletedDirs = 0; + LPWSTR sczPath = NULL; + + while (wzPath && *wzPath && ::RemoveDirectoryW(wzPath)) + { + ++cDeletedDirs; + + HRESULT hr = PathGetParentPath(wzPath, &sczPath); + DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", wzPath); + + wzPath = sczPath; + } + +LExit: + ReleaseStr(sczPath); + + return cDeletedDirs; +} + + +/******************************************************************* + DirGetCurrent - gets the current directory. + +*******************************************************************/ +extern "C" HRESULT DAPI DirGetCurrent( + __deref_out_z LPWSTR* psczCurrentDirectory + ) +{ + HRESULT hr = S_OK; + SIZE_T cch = 0; + + if (psczCurrentDirectory && *psczCurrentDirectory) + { + hr = StrMaxLength(*psczCurrentDirectory, &cch); + DirExitOnFailure(hr, "Failed to determine size of current directory."); + } + + DWORD cchRequired = ::GetCurrentDirectoryW((DWORD)min(DWORD_MAX, cch), 0 == cch ? NULL : *psczCurrentDirectory); + if (0 == cchRequired) + { + DirExitWithLastError(hr, "Failed to get current directory."); + } + else if (cch < cchRequired) + { + hr = StrAlloc(psczCurrentDirectory, cchRequired); + DirExitOnFailure(hr, "Failed to allocate string for current directory."); + + if (!::GetCurrentDirectoryW(cchRequired, *psczCurrentDirectory)) + { + DirExitWithLastError(hr, "Failed to get current directory using allocated string."); + } + } + +LExit: + return hr; +} + + +/******************************************************************* + DirSetCurrent - sets the current directory. + +*******************************************************************/ +extern "C" HRESULT DAPI DirSetCurrent( + __in_z LPCWSTR wzDirectory + ) +{ + HRESULT hr = S_OK; + + if (!::SetCurrentDirectoryW(wzDirectory)) + { + DirExitWithLastError(hr, "Failed to set current directory to: %ls", wzDirectory); + } + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/dlutil.cpp b/src/libs/dutil/WixToolset.DUtil/dlutil.cpp new file mode 100644 index 00000000..70155e6f --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/dlutil.cpp @@ -0,0 +1,802 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#include +#include + + +// Exit macros +#define DlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__) +#define DlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DLUTIL, p, x, e, s, __VA_ARGS__) +#define DlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DLUTIL, p, x, s, __VA_ARGS__) +#define DlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DLUTIL, p, x, e, s, __VA_ARGS__) +#define DlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DLUTIL, p, x, s, __VA_ARGS__) +#define DlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DLUTIL, e, x, s, __VA_ARGS__) +#define DlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DLUTIL, g, x, s, __VA_ARGS__) + +static const DWORD64 DOWNLOAD_ENGINE_TWO_GIGABYTES = DWORD64(2) * 1024 * 1024 * 1024; +static LPCWSTR DOWNLOAD_ENGINE_ACCEPT_TYPES[] = { L"*/*", NULL }; + +// internal function declarations + +static HRESULT InitializeResume( + __in LPCWSTR wzDestinationPath, + __out LPWSTR* psczResumePath, + __out HANDLE* phResumeFile, + __out DWORD64* pdw64ResumeOffset + ); +static HRESULT GetResourceMetadata( + __in HINTERNET hSession, + __inout_z LPWSTR* psczUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out DWORD64* pdw64ResourceSize, + __out FILETIME* pftResourceCreated + ); +static HRESULT DownloadResource( + __in HINTERNET hSession, + __inout_z LPWSTR* psczUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_z LPCWSTR wzDestinationPath, + __in DWORD64 dw64AuthoredResourceLength, + __in DWORD64 dw64ResourceLength, + __in DWORD64 dw64ResumeOffset, + __in HANDLE hResumeFile, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate + ); +static HRESULT AllocateRangeRequestHeader( + __in DWORD64 dw64ResumeOffset, + __in DWORD64 dw64ResourceLength, + __deref_inout_z LPWSTR* psczHeader + ); +static HRESULT WriteToFile( + __in HINTERNET hUrl, + __in HANDLE hPayloadFile, + __inout DWORD64* pdw64ResumeOffset, + __in HANDLE hResumeFile, + __in DWORD64 dw64ResourceLength, + __in LPBYTE pbData, + __in DWORD cbData, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback + ); +static HRESULT UpdateResumeOffset( + __inout DWORD64* pdw64ResumeOffset, + __in HANDLE hResumeFile, + __in DWORD cbData + ); +static HRESULT MakeRequest( + __in HINTERNET hSession, + __inout_z LPWSTR* psczSourceUrl, + __in_z_opt LPCWSTR wzMethod, + __in_z_opt LPCWSTR wzHeaders, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out HINTERNET* phConnect, + __out HINTERNET* phUrl, + __out BOOL* pfRangeRequestsAccepted + ); +static HRESULT OpenRequest( + __in HINTERNET hConnect, + __in_z_opt LPCWSTR wzMethod, + __in INTERNET_SCHEME scheme, + __in_z LPCWSTR wzResource, + __in_z_opt LPCWSTR wzQueryString, + __in_z_opt LPCWSTR wzHeader, + __out HINTERNET* phUrl + ); +static HRESULT SendRequest( + __in HINTERNET hUrl, + __inout_z LPWSTR* psczUrl, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out BOOL* pfRetry, + __out BOOL* pfRangesAccepted + ); +static HRESULT AuthenticationRequired( + __in HINTERNET hUrl, + __in long lHttpCode, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ); +static HRESULT DownloadGetResumePath( + __in_z LPCWSTR wzPayloadWorkingPath, + __deref_out_z LPWSTR* psczResumePath + ); +static HRESULT DownloadSendProgressCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in HANDLE hDestinationFile + ); +// function definitions + +extern "C" HRESULT DAPI DownloadUrl( + __in DOWNLOAD_SOURCE* pDownloadSource, + __in DWORD64 dw64AuthoredDownloadSize, + __in LPCWSTR wzDestinationPath, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate + ) +{ + HRESULT hr = S_OK; + LPWSTR sczUrl = NULL; + HINTERNET hSession = NULL; + DWORD dwTimeout = 0; + LPWSTR sczResumePath = NULL; + HANDLE hResumeFile = INVALID_HANDLE_VALUE; + DWORD64 dw64ResumeOffset = 0; + DWORD64 dw64Size = 0; + FILETIME ftCreated = { }; + + // Copy the download source into a working variable to handle redirects then + // open the internet session. + hr = StrAllocString(&sczUrl, pDownloadSource->sczUrl, 0); + DlExitOnFailure(hr, "Failed to copy download source URL."); + + hSession = ::InternetOpenW(L"Burn", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); + DlExitOnNullWithLastError(hSession, hr, "Failed to open internet session"); + + // Make a best effort to set the download timeouts to 2 minutes or whatever policy says. + PolcReadNumber(POLICY_BURN_REGISTRY_PATH, L"DownloadTimeout", 2 * 60, &dwTimeout); + if (0 < dwTimeout) + { + dwTimeout *= 1000; // convert to milliseconds. + ::InternetSetOptionW(hSession, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); + ::InternetSetOptionW(hSession, INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); + ::InternetSetOptionW(hSession, INTERNET_OPTION_SEND_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); + } + + // Get the resource size and creation time from the internet. + hr = GetResourceMetadata(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, pAuthenticate, &dw64Size, &ftCreated); + DlExitOnFailure(hr, "Failed to get size and time for URL: %ls", sczUrl); + + // Ignore failure to initialize resume because we will fall back to full download then + // download. + InitializeResume(wzDestinationPath, &sczResumePath, &hResumeFile, &dw64ResumeOffset); + + hr = DownloadResource(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, wzDestinationPath, dw64AuthoredDownloadSize, dw64Size, dw64ResumeOffset, hResumeFile, pCache, pAuthenticate); + DlExitOnFailure(hr, "Failed to download URL: %ls", sczUrl); + + // Cleanup the resume file because we successfully downloaded the whole file. + if (sczResumePath && *sczResumePath) + { + ::DeleteFileW(sczResumePath); + } + +LExit: + ReleaseFileHandle(hResumeFile); + ReleaseStr(sczResumePath); + ReleaseInternet(hSession); + ReleaseStr(sczUrl); + + return hr; +} + + +// internal helper functions + +static HRESULT InitializeResume( + __in LPCWSTR wzDestinationPath, + __out LPWSTR* psczResumePath, + __out HANDLE* phResumeFile, + __out DWORD64* pdw64ResumeOffset + ) +{ + HRESULT hr = S_OK; + HANDLE hResumeFile = INVALID_HANDLE_VALUE; + DWORD cbTotalReadResumeData = 0; + DWORD cbReadData = 0; + + *pdw64ResumeOffset = 0; + + hr = DownloadGetResumePath(wzDestinationPath, psczResumePath); + DlExitOnFailure(hr, "Failed to calculate resume path from working path: %ls", wzDestinationPath); + + hResumeFile = ::CreateFileW(*psczResumePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hResumeFile) + { + DlExitWithLastError(hr, "Failed to create resume file: %ls", *psczResumePath); + } + + do + { + if (!::ReadFile(hResumeFile, reinterpret_cast(pdw64ResumeOffset) + cbTotalReadResumeData, sizeof(DWORD64) - cbTotalReadResumeData, &cbReadData, NULL)) + { + DlExitWithLastError(hr, "Failed to read resume file: %ls", *psczResumePath); + } + cbTotalReadResumeData += cbReadData; + } while (cbReadData && sizeof(DWORD64) > cbTotalReadResumeData); + + // Start over if we couldn't get a resume offset. + if (cbTotalReadResumeData != sizeof(DWORD64)) + { + *pdw64ResumeOffset = 0; + } + + *phResumeFile = hResumeFile; + hResumeFile = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hResumeFile); + return hr; +} + +static HRESULT GetResourceMetadata( + __in HINTERNET hSession, + __inout_z LPWSTR* psczUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out DWORD64* pdw64ResourceSize, + __out FILETIME* pftResourceCreated + ) +{ + HRESULT hr = S_OK; + BOOL fRangeRequestsAccepted = TRUE; + HINTERNET hConnect = NULL; + HINTERNET hUrl = NULL; + LONGLONG llLength = 0; + + hr = MakeRequest(hSession, psczUrl, L"HEAD", NULL, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted); + DlExitOnFailure(hr, "Failed to connect to URL: %ls", *psczUrl); + + hr = InternetGetSizeByHandle(hUrl, &llLength); + if (FAILED(hr)) + { + llLength = 0; + hr = S_OK; + } + + *pdw64ResourceSize = llLength; + + // Get the last modified time from the server, we'll use that as our downloaded time here. If + // the server time isn't available then use the local system time. + hr = InternetGetCreateTimeByHandle(hUrl, pftResourceCreated); + if (FAILED(hr)) + { + ::GetSystemTimeAsFileTime(pftResourceCreated); + hr = S_OK; + } + +LExit: + ReleaseInternet(hUrl); + ReleaseInternet(hConnect); + return hr; +} + +static HRESULT DownloadResource( + __in HINTERNET hSession, + __inout_z LPWSTR* psczUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_z LPCWSTR wzDestinationPath, + __in DWORD64 dw64AuthoredResourceLength, + __in DWORD64 dw64ResourceLength, + __in DWORD64 dw64ResumeOffset, + __in HANDLE hResumeFile, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate + ) +{ + HRESULT hr = S_OK; + HANDLE hPayloadFile = INVALID_HANDLE_VALUE; + DWORD cbMaxData = 64 * 1024; // 64 KB + BYTE* pbData = NULL; + BOOL fRangeRequestsAccepted = TRUE; + LPWSTR sczRangeRequestHeader = NULL; + HINTERNET hConnect = NULL; + HINTERNET hUrl = NULL; + LONGLONG llLength = 0; + + hPayloadFile = ::CreateFileW(wzDestinationPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hPayloadFile) + { + DlExitWithLastError(hr, "Failed to create download destination file: %ls", wzDestinationPath); + } + + // Allocate a memory block on a page boundary in case we want to do optimal writing. + pbData = static_cast(::VirtualAlloc(NULL, cbMaxData, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); + DlExitOnNullWithLastError(pbData, hr, "Failed to allocate buffer to download files into."); + + // Let's try downloading the file assuming that range requests are accepted. If range requests + // are not supported we'll have to start over and accept the fact that we only get one shot + // downloading the file however big it is. Hopefully, not more than 2 GB since wininet doesn't + // like files that big. + while (fRangeRequestsAccepted && (0 == dw64ResourceLength || dw64ResumeOffset < dw64ResourceLength)) + { + hr = AllocateRangeRequestHeader(dw64ResumeOffset, 0 == dw64ResourceLength ? dw64AuthoredResourceLength : dw64ResourceLength, &sczRangeRequestHeader); + DlExitOnFailure(hr, "Failed to allocate range request header."); + + ReleaseNullInternet(hConnect); + ReleaseNullInternet(hUrl); + + hr = MakeRequest(hSession, psczUrl, L"GET", sczRangeRequestHeader, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted); + DlExitOnFailure(hr, "Failed to request URL for download: %ls", *psczUrl); + + // If we didn't get the size of the resource from the initial "HEAD" request + // then let's try to get the size from this "GET" request. + if (0 == dw64ResourceLength) + { + hr = InternetGetSizeByHandle(hUrl, &llLength); + if (SUCCEEDED(hr)) + { + dw64ResourceLength = llLength; + } + else // server didn't tell us the resource length. + { + // Fallback to the authored size of the resource. However, since we + // don't really know the size on the server, don't try to use + // range requests either. + dw64ResourceLength = dw64AuthoredResourceLength; + fRangeRequestsAccepted = FALSE; + } + } + + // If we just tried to do a range request and found out that it isn't supported, start over. + if (!fRangeRequestsAccepted) + { + // TODO: log a message that the server did not accept range requests. + dw64ResumeOffset = 0; + } + + hr = WriteToFile(hUrl, hPayloadFile, &dw64ResumeOffset, hResumeFile, dw64ResourceLength, pbData, cbMaxData, pCache); + DlExitOnFailure(hr, "Failed while reading from internet and writing to: %ls", wzDestinationPath); + } + +LExit: + ReleaseInternet(hUrl); + ReleaseInternet(hConnect); + ReleaseStr(sczRangeRequestHeader); + if (pbData) + { + ::VirtualFree(pbData, 0, MEM_RELEASE); + } + ReleaseFileHandle(hPayloadFile); + + return hr; +} + +static HRESULT AllocateRangeRequestHeader( + __in DWORD64 dw64ResumeOffset, + __in DWORD64 dw64ResourceLength, + __deref_inout_z LPWSTR* psczHeader + ) +{ + HRESULT hr = S_OK; + + // If the remaining length is less that 2GB we'll be able to ask for everything. + DWORD64 dw64RemainingLength = dw64ResourceLength - dw64ResumeOffset; + if (DOWNLOAD_ENGINE_TWO_GIGABYTES > dw64RemainingLength) + { + // If we have a resume offset, let's download everything from there. Otherwise, we'll + // just get everything with no headers in the way. + if (0 < dw64ResumeOffset) + { + hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-", dw64ResumeOffset); + DlExitOnFailure(hr, "Failed to add range read header."); + } + else + { + ReleaseNullStr(*psczHeader); + } + } + else // we'll have to download in chunks. + { + hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-%I64u", dw64ResumeOffset, dw64ResumeOffset + dw64RemainingLength - 1); + DlExitOnFailure(hr, "Failed to add range read header."); + } + +LExit: + return hr; +} + +static HRESULT WriteToFile( + __in HINTERNET hUrl, + __in HANDLE hPayloadFile, + __inout DWORD64* pdw64ResumeOffset, + __in HANDLE hResumeFile, + __in DWORD64 dw64ResourceLength, + __in LPBYTE pbData, + __in DWORD cbData, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback + ) +{ + HRESULT hr = S_OK; + DWORD cbReadData = 0; + + hr = FileSetPointer(hPayloadFile, *pdw64ResumeOffset, NULL, FILE_BEGIN); + DlExitOnFailure(hr, "Failed to seek to start point in file."); + + do + { + // Read bits from the internet. + if (!::InternetReadFile(hUrl, static_cast(pbData), cbData, &cbReadData)) + { + DlExitWithLastError(hr, "Failed while reading from internet."); + } + + // Write bits to disk (if there are any). + if (cbReadData) + { + DWORD cbTotalWritten = 0; + DWORD cbWritten = 0; + do + { + if (!::WriteFile(hPayloadFile, pbData + cbTotalWritten, cbReadData - cbTotalWritten, &cbWritten, NULL)) + { + DlExitWithLastError(hr, "Failed to write data from internet."); + } + + cbTotalWritten += cbWritten; + } while (cbWritten && cbTotalWritten < cbReadData); + + // Ignore failure from updating resume file as this doesn't mean the download cannot succeed. + UpdateResumeOffset(pdw64ResumeOffset, hResumeFile, cbTotalWritten); + + if (pCallback && pCallback->pfnProgress) + { + hr = DownloadSendProgressCallback(pCallback, *pdw64ResumeOffset, dw64ResourceLength, hPayloadFile); + DlExitOnFailure(hr, "UX aborted on cache progress."); + } + } + } while (cbReadData); + +LExit: + return hr; +} + +static HRESULT UpdateResumeOffset( + __inout DWORD64* pdw64ResumeOffset, + __in HANDLE hResumeFile, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + + *pdw64ResumeOffset += cbData; + + if (INVALID_HANDLE_VALUE != hResumeFile) + { + DWORD cbTotalWrittenResumeData = 0; + DWORD cbWrittenResumeData = 0; + + hr = FileSetPointer(hResumeFile, 0, NULL, FILE_BEGIN); + DlExitOnFailure(hr, "Failed to seek to start point in file."); + + do + { + // Ignore failure to write to the resume file as that should not prevent the download from happening. + if (!::WriteFile(hResumeFile, pdw64ResumeOffset + cbTotalWrittenResumeData, sizeof(DWORD64) - cbTotalWrittenResumeData, &cbWrittenResumeData, NULL)) + { + DlExitOnFailure(hr, "Failed to seek to write to file."); + } + + cbTotalWrittenResumeData += cbWrittenResumeData; + } while (cbWrittenResumeData && sizeof(DWORD64) > cbTotalWrittenResumeData); + } + +LExit: + return hr; +} + +static HRESULT MakeRequest( + __in HINTERNET hSession, + __inout_z LPWSTR* psczSourceUrl, + __in_z_opt LPCWSTR wzMethod, + __in_z_opt LPCWSTR wzHeaders, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out HINTERNET* phConnect, + __out HINTERNET* phUrl, + __out BOOL* pfRangeRequestsAccepted + ) +{ + HRESULT hr = S_OK; + HINTERNET hConnect = NULL; + HINTERNET hUrl = NULL; + URI_INFO uri = { }; + + // Try to open the URL. + BOOL fRetry; + do + { + fRetry = FALSE; + + // If the URL was opened close it, so we can reopen it again. + ReleaseInternet(hUrl); + ReleaseInternet(hConnect); + + // Open the url. + hr = UriCrackEx(*psczSourceUrl, &uri); + DlExitOnFailure(hr, "Failed to break URL into server and resource parts."); + + hConnect = ::InternetConnectW(hSession, uri.sczHostName, uri.port, (wzUser && *wzUser) ? wzUser : uri.sczUser, (wzPassword && *wzPassword) ? wzPassword : uri.sczPassword, INTERNET_SCHEME_FTP == uri.scheme ? INTERNET_SERVICE_FTP : INTERNET_SERVICE_HTTP, 0, 0); + DlExitOnNullWithLastError(hConnect, hr, "Failed to connect to URL: %ls", *psczSourceUrl); + + // Best effort set the proxy username and password, if they were provided. + if ((wzUser && *wzUser) && (wzPassword && *wzPassword)) + { + if (::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_USERNAME, (LPVOID)wzUser, lstrlenW(wzUser))) + { + ::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_PASSWORD, (LPVOID)wzPassword, lstrlenW(wzPassword)); + } + } + + hr = OpenRequest(hConnect, wzMethod, uri.scheme, uri.sczPath, uri.sczQueryString, wzHeaders, &hUrl); + DlExitOnFailure(hr, "Failed to open internet URL: %ls", *psczSourceUrl); + + hr = SendRequest(hUrl, psczSourceUrl, pAuthenticate, &fRetry, pfRangeRequestsAccepted); + DlExitOnFailure(hr, "Failed to send request to URL: %ls", *psczSourceUrl); + } while (fRetry); + + // Okay, we're all ready to start downloading. Update the connection information. + *phConnect = hConnect; + hConnect = NULL; + *phUrl = hUrl; + hUrl = NULL; + +LExit: + UriInfoUninitialize(&uri); + ReleaseInternet(hUrl); + ReleaseInternet(hConnect); + + return hr; +} + +static HRESULT OpenRequest( + __in HINTERNET hConnect, + __in_z_opt LPCWSTR wzMethod, + __in INTERNET_SCHEME scheme, + __in_z LPCWSTR wzResource, + __in_z_opt LPCWSTR wzQueryString, + __in_z_opt LPCWSTR wzHeader, + __out HINTERNET* phUrl + ) +{ + HRESULT hr = S_OK; + DWORD dwRequestFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD; + LPWSTR sczResource = NULL; + HINTERNET hUrl = NULL; + + if (INTERNET_SCHEME_HTTPS == scheme) + { + dwRequestFlags |= INTERNET_FLAG_SECURE; + } + else if (INTERNET_SCHEME_HTTP == scheme) + { + dwRequestFlags |= INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS; + } + + // Allocate the resource name. + hr = StrAllocString(&sczResource, wzResource, 0); + DlExitOnFailure(hr, "Failed to allocate string for resource URI."); + + if (wzQueryString && *wzQueryString) + { + hr = StrAllocConcat(&sczResource, wzQueryString, 0); + DlExitOnFailure(hr, "Failed to append query strong to resource from URI."); + } + + // Open the request and add the header if provided. + hUrl = ::HttpOpenRequestW(hConnect, wzMethod, sczResource, NULL, NULL, DOWNLOAD_ENGINE_ACCEPT_TYPES, dwRequestFlags, NULL); + DlExitOnNullWithLastError(hUrl, hr, "Failed to open internet request."); + + if (wzHeader && *wzHeader) + { + if (!::HttpAddRequestHeadersW(hUrl, wzHeader, static_cast(-1), HTTP_ADDREQ_FLAG_COALESCE)) + { + DlExitWithLastError(hr, "Failed to add header to HTTP request."); + } + } + + *phUrl = hUrl; + hUrl = NULL; + +LExit: + ReleaseInternet(hUrl); + ReleaseStr(sczResource); + return hr; +} + +static HRESULT SendRequest( + __in HINTERNET hUrl, + __inout_z LPWSTR* psczUrl, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out BOOL* pfRetry, + __out BOOL* pfRangesAccepted + ) +{ + HRESULT hr = S_OK; + BOOL fRetrySend = FALSE; + LONG lCode = 0; + + do + { + fRetrySend = FALSE; + + if (!::HttpSendRequestW(hUrl, NULL, 0, NULL, 0)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); // remember the error that occurred and log it. + LogErrorString(hr, "Failed to send request to URL: %ls, trying to process HTTP status code anyway.", *psczUrl); + + // Try to get the HTTP status code and, if good, handle via the switch statement below but if it + // fails return the error code from the send request above as the result of the function. + HRESULT hrQueryStatusCode = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); + DlExitOnFailure(hrQueryStatusCode, "Failed to get HTTP status code for failed request to URL: %ls", *psczUrl); + } + else // get the http status code. + { + hr = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode); + DlExitOnFailure(hr, "Failed to get HTTP status code for request to URL: %ls", *psczUrl); + } + + switch (lCode) + { + case 200: // OK but range requests don't work. + *pfRangesAccepted = FALSE; + hr = S_OK; + break; + + case 206: // Partial content means that range requests work! + *pfRangesAccepted = TRUE; + hr = S_OK; + break; + + // redirection cases + case 301: __fallthrough; // file moved + case 302: __fallthrough; // temporary + case 303: // redirect method + hr = InternetQueryInfoString(hUrl, HTTP_QUERY_CONTENT_LOCATION, psczUrl); + DlExitOnFailure(hr, "Failed to get redirect url: %ls", *psczUrl); + + *pfRetry = TRUE; + break; + + // error cases + case 400: // bad request + hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME); + break; + + case 401: __fallthrough; // unauthorized + case 407: __fallthrough; // proxy unauthorized + hr = AuthenticationRequired(hUrl, lCode, pAuthenticate, &fRetrySend, pfRetry); + break; + + case 403: // forbidden + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + break; + + case 404: // file not found + case 410: // gone + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + break; + + case 405: // method not allowed + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + break; + + case 408: __fallthrough; // request timedout + case 504: // gateway timeout + hr = HRESULT_FROM_WIN32(WAIT_TIMEOUT); + break; + + case 414: // request URI too long + hr = CO_E_PATHTOOLONG; + break; + + case 502: __fallthrough; // server (through a gateway) was not found + case 503: // server unavailable + hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); + break; + + case 418: // I'm a teapot. + default: + // If the request failed and the HTTP status code was invalid (but wininet gave us a number anyway) + // do not overwrite the error code from the failed request. Otherwise, the error was unexpected. + if (SUCCEEDED(hr)) + { + hr = E_UNEXPECTED; + } + + LogErrorString(hr, "Unknown HTTP status code %d, returned from URL: %ls", lCode, *psczUrl); + break; + } + } while (fRetrySend); + +LExit: + return hr; +} + +static HRESULT AuthenticationRequired( + __in HINTERNET hUrl, + __in long lHttpCode, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ) +{ + Assert(401 == lHttpCode || 407 == lHttpCode); + + HRESULT hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + *pfRetrySend = FALSE; + *pfRetry = FALSE; + + if (pAuthenticate && pAuthenticate->pfnAuthenticate) + { + hr = (*pAuthenticate->pfnAuthenticate)(pAuthenticate->pv, hUrl, lHttpCode, pfRetrySend, pfRetry); + } + + return hr; +} + + +static HRESULT DownloadGetResumePath( + __in_z LPCWSTR wzPayloadWorkingPath, + __deref_out_z LPWSTR* psczResumePath + ) +{ + HRESULT hr = S_OK; + + hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath); + DlExitOnFailure(hr, "Failed to create resume path."); + +LExit: + return hr; +} + +static HRESULT DownloadSendProgressCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in HANDLE hDestinationFile + ) +{ + static LARGE_INTEGER LARGE_INTEGER_ZERO = { }; + + HRESULT hr = S_OK; + DWORD dwResult = PROGRESS_CONTINUE; + LARGE_INTEGER liTotalSize = { }; + LARGE_INTEGER liTotalTransferred = { }; + + if (pCallback->pfnProgress) + { + liTotalSize.QuadPart = dw64Total; + liTotalTransferred.QuadPart = dw64Progress; + + dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv); + switch (dwResult) + { + case PROGRESS_CONTINUE: + hr = S_OK; + break; + + case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently? + case PROGRESS_STOP: + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + DlExitOnRootFailure(hr, "UX aborted on download progress."); + + case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress. + pCallback->pfnProgress = NULL; + hr = S_OK; + break; + + default: + hr = E_UNEXPECTED; + DlExitOnRootFailure(hr, "Invalid return code from progress routine."); + } + } + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/dpiutil.cpp b/src/libs/dutil/WixToolset.DUtil/dpiutil.cpp new file mode 100644 index 00000000..4096c8d3 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/dpiutil.cpp @@ -0,0 +1,274 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define DpiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__) +#define DpiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__) +#define DpiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__) +#define DpiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__) +#define DpiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DPIUTIL, e, x, s, __VA_ARGS__) + +static PFN_ADJUSTWINDOWRECTEXFORDPI vpfnAdjustWindowRectExForDpi = NULL; +static PFN_GETDPIFORMONITOR vpfnGetDpiForMonitor = NULL; +static PFN_GETDPIFORWINDOW vpfnGetDpiForWindow = NULL; +static PFN_SETPROCESSDPIAWARE vpfnSetProcessDPIAware = NULL; +static PFN_SETPROCESSDPIAWARENESS vpfnSetProcessDpiAwareness = NULL; +static PFN_SETPROCESSDPIAWARENESSCONTEXT vpfnSetProcessDpiAwarenessContext = NULL; + +static HMODULE vhShcoreDll = NULL; +static HMODULE vhUser32Dll = NULL; +static BOOL vfDpiuInitialized = FALSE; + +DAPI_(void) DpiuInitialize() +{ + HRESULT hr = S_OK; + + hr = LoadSystemLibrary(L"Shcore.dll", &vhShcoreDll); + if (SUCCEEDED(hr)) + { + // Ignore failures. + vpfnGetDpiForMonitor = reinterpret_cast(::GetProcAddress(vhShcoreDll, "GetDpiForMonitor")); + vpfnSetProcessDpiAwareness = reinterpret_cast(::GetProcAddress(vhShcoreDll, "SetProcessDpiAwareness")); + } + + hr = LoadSystemLibrary(L"User32.dll", &vhUser32Dll); + if (SUCCEEDED(hr)) + { + // Ignore failures. + vpfnAdjustWindowRectExForDpi = reinterpret_cast(::GetProcAddress(vhUser32Dll, "AdjustWindowRectExForDpi")); + vpfnGetDpiForWindow = reinterpret_cast(::GetProcAddress(vhUser32Dll, "GetDpiForWindow")); + vpfnSetProcessDPIAware = reinterpret_cast(::GetProcAddress(vhUser32Dll, "SetProcessDPIAware")); + vpfnSetProcessDpiAwarenessContext = reinterpret_cast(::GetProcAddress(vhUser32Dll, "SetProcessDpiAwarenessContext")); + } + + vfDpiuInitialized = TRUE; +} + +DAPI_(void) DpiuUninitialize() +{ + if (vhShcoreDll) + { + ::FreeLibrary(vhShcoreDll); + } + + if (vhUser32Dll) + { + ::FreeLibrary(vhUser32Dll); + } + + vhShcoreDll = NULL; + vhUser32Dll = NULL; + vpfnAdjustWindowRectExForDpi = NULL; + vpfnGetDpiForMonitor = NULL; + vpfnGetDpiForWindow = NULL; + vfDpiuInitialized = FALSE; +} + +DAPI_(void) DpiuAdjustWindowRect( + __in RECT* pWindowRect, + __in DWORD dwStyle, + __in BOOL fMenu, + __in DWORD dwExStyle, + __in UINT nDpi + ) +{ + if (WS_SYSMENU & dwStyle) + { + dwStyle |= WS_CAPTION; // WS_CAPTION is required with WS_SYSMENU, AdjustWindowRect* won't work properly when it's not specified. + } + + if (vpfnAdjustWindowRectExForDpi) + { + vpfnAdjustWindowRectExForDpi(pWindowRect, dwStyle, fMenu, dwExStyle, nDpi); + } + else + { + ::AdjustWindowRectEx(pWindowRect, dwStyle, fMenu, dwExStyle); + } +} + +DAPI_(HRESULT) DpiuGetMonitorContextFromPoint( + __in const POINT* pt, + __out DPIU_MONITOR_CONTEXT** ppMonitorContext + ) +{ + HRESULT hr = S_OK; + DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; + HMONITOR hMonitor = NULL; + UINT dpiX = 0; + UINT dpiY = 0; + HDC hdc = NULL; + + pMonitorContext = reinterpret_cast(MemAlloc(sizeof(DPIU_MONITOR_CONTEXT), TRUE)); + DpiuExitOnNull(pMonitorContext, hr, E_OUTOFMEMORY, "Failed to allocate memory for DpiuMonitorContext."); + + hMonitor = ::MonitorFromPoint(*pt, MONITOR_DEFAULTTONEAREST); + DpiuExitOnNull(hMonitor, hr, E_FAIL, "Failed to get monitor from point."); + + pMonitorContext->mi.cbSize = sizeof(pMonitorContext->mi); + if (!::GetMonitorInfoW(hMonitor, &pMonitorContext->mi)) + { + DpiuExitOnFailure(hr = E_OUTOFMEMORY, "Failed to get monitor info for point."); + } + + if (vpfnGetDpiForMonitor) + { + hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + DpiuExitOnFailure(hr, "Failed to get DPI for monitor."); + + pMonitorContext->nDpi = dpiX; + } + else + { + hdc = ::CreateDCW(L"DISPLAY", pMonitorContext->mi.szDevice, NULL, NULL); + DpiuExitOnNull(hdc, hr, E_OUTOFMEMORY, "Failed to get device context for monitor."); + + pMonitorContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX); + } + + *ppMonitorContext = pMonitorContext; + pMonitorContext = NULL; + +LExit: + if (hdc) + { + ::ReleaseDC(NULL, hdc); + } + + MemFree(pMonitorContext); + + return hr; +} + +DAPI_(void) DpiuGetWindowContext( + __in HWND hWnd, + __in DPIU_WINDOW_CONTEXT* pWindowContext + ) +{ + HRESULT hr = S_OK; + HMONITOR hMonitor = NULL; + UINT dpiX = 0; + UINT dpiY = 0; + HDC hdc = NULL; + + pWindowContext->nDpi = USER_DEFAULT_SCREEN_DPI; + + if (vpfnGetDpiForWindow) + { + pWindowContext->nDpi = vpfnGetDpiForWindow(hWnd); + ExitFunction(); + } + + if (vpfnGetDpiForMonitor) + { + hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); + if (hMonitor) + { + hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + if (SUCCEEDED(hr)) + { + pWindowContext->nDpi = dpiX; + ExitFunction(); + } + } + } + + hdc = ::GetDC(hWnd); + if (hdc) + { + pWindowContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX); + } + +LExit: + if (hdc) + { + ::ReleaseDC(hWnd, hdc); + } +} + +DAPI_(int) DpiuScaleValue( + __in int nDefaultDpiValue, + __in UINT nTargetDpi + ) +{ + return ::MulDiv(nDefaultDpiValue, nTargetDpi, USER_DEFAULT_SCREEN_DPI); +} + +DAPI_(HRESULT) DpiuSetProcessDpiAwareness( + __in DPIU_AWARENESS supportedAwareness, + __in_opt DPIU_AWARENESS* pSelectedAwareness + ) +{ + HRESULT hr = S_OK; + DPIU_AWARENESS selectedAwareness = DPIU_AWARENESS_NONE; + DPI_AWARENESS_CONTEXT awarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE; + PROCESS_DPI_AWARENESS awareness = PROCESS_DPI_UNAWARE; + + if (vpfnSetProcessDpiAwarenessContext) + { + if (DPIU_AWARENESS_PERMONITORV2 & supportedAwareness) + { + awarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; + selectedAwareness = DPIU_AWARENESS_PERMONITORV2; + } + else if (DPIU_AWARENESS_PERMONITOR & supportedAwareness) + { + awarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE; + selectedAwareness = DPIU_AWARENESS_PERMONITOR; + } + else if (DPIU_AWARENESS_SYSTEM & supportedAwareness) + { + awarenessContext = DPI_AWARENESS_CONTEXT_SYSTEM_AWARE; + selectedAwareness = DPIU_AWARENESS_SYSTEM; + } + else if (DPIU_AWARENESS_GDISCALED & supportedAwareness) + { + awarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED; + selectedAwareness = DPIU_AWARENESS_GDISCALED; + } + + if (!vpfnSetProcessDpiAwarenessContext(awarenessContext)) + { + DpiuExitOnLastError(hr, "Failed to set process DPI awareness context."); + } + } + else if (vpfnSetProcessDpiAwareness) + { + if (DPIU_AWARENESS_PERMONITOR & supportedAwareness) + { + awareness = PROCESS_PER_MONITOR_DPI_AWARE; + selectedAwareness = DPIU_AWARENESS_PERMONITOR; + } + else if (DPIU_AWARENESS_SYSTEM & supportedAwareness) + { + awareness = PROCESS_SYSTEM_DPI_AWARE; + selectedAwareness = DPIU_AWARENESS_SYSTEM; + } + + hr = vpfnSetProcessDpiAwareness(awareness); + DpiuExitOnFailure(hr, "Failed to set process DPI awareness."); + } + else if (vpfnSetProcessDPIAware && (DPIU_AWARENESS_SYSTEM & supportedAwareness)) + { + selectedAwareness = DPIU_AWARENESS_SYSTEM; + if (!vpfnSetProcessDPIAware()) + { + DpiuExitOnLastError(hr, "Failed to set process DPI aware."); + } + } + +LExit: + if (pSelectedAwareness) + { + *pSelectedAwareness = selectedAwareness; + } + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.cpp b/src/libs/dutil/WixToolset.DUtil/dutil.cpp new file mode 100644 index 00000000..56b85207 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/dutil.cpp @@ -0,0 +1,524 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define DExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) +#define DExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__) +#define DExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__) +#define DExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__) +#define DExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__) +#define DExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DUTIL, e, x, s, __VA_ARGS__) +#define DExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DUTIL, g, x, s, __VA_ARGS__) + +// No need for OACR to warn us about using non-unicode APIs in this file. +#pragma prefast(disable:25068) + +// Asserts & Tracing + +const int DUTIL_STRING_BUFFER = 1024; +static HMODULE Dutil_hAssertModule = NULL; +static DUTIL_ASSERTDISPLAYFUNCTION Dutil_pfnDisplayAssert = NULL; +static BOOL Dutil_fNoAsserts = FALSE; +static REPORT_LEVEL Dutil_rlCurrentTrace = REPORT_STANDARD; +static BOOL Dutil_fTraceFilenames = FALSE; +static DUTIL_CALLBACK_TRACEERROR vpfnTraceErrorCallback = NULL; + + +DAPI_(HRESULT) DutilInitialize( + __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback + ) +{ + HRESULT hr = S_OK; + + vpfnTraceErrorCallback = pfnTraceErrorCallback; + + return hr; +} + + +DAPI_(void) DutilUninitialize() +{ + vpfnTraceErrorCallback = NULL; +} + +/******************************************************************* +Dutil_SetAssertModule + +*******************************************************************/ +extern "C" void DAPI Dutil_SetAssertModule( + __in HMODULE hAssertModule + ) +{ + Dutil_hAssertModule = hAssertModule; +} + + +/******************************************************************* +Dutil_SetAssertDisplayFunction + +*******************************************************************/ +extern "C" void DAPI Dutil_SetAssertDisplayFunction( + __in DUTIL_ASSERTDISPLAYFUNCTION pfn + ) +{ + Dutil_pfnDisplayAssert = pfn; +} + + +/******************************************************************* +Dutil_AssertMsg + +*******************************************************************/ +extern "C" void DAPI Dutil_AssertMsg( + __in_z LPCSTR szMessage + ) +{ + static BOOL fInAssert = FALSE; // TODO: make this thread safe (this is a cheap hack to prevent re-entrant Asserts) + + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + int id = IDRETRY; + HKEY hkDebug = NULL; + HANDLE hAssertFile = INVALID_HANDLE_VALUE; + char szPath[MAX_PATH] = { }; + DWORD cch = 0; + + if (fInAssert) + { + return; + } + fInAssert = TRUE; + + char szMsg[DUTIL_STRING_BUFFER]; + hr = ::StringCchCopyA(szMsg, countof(szMsg), szMessage); + DExitOnFailure(hr, "failed to copy message while building assert message"); + + if (Dutil_pfnDisplayAssert) + { + // call custom function to display the assert string + if (!Dutil_pfnDisplayAssert(szMsg)) + { + ExitFunction(); + } + } + else + { + OutputDebugStringA(szMsg); + } + + if (!Dutil_fNoAsserts) + { + er = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Delivery\\Debug", 0, KEY_QUERY_VALUE, &hkDebug); + if (ERROR_SUCCESS == er) + { + cch = countof(szPath); + er = ::RegQueryValueExA(hkDebug, "DeliveryAssertsLog", NULL, NULL, reinterpret_cast(szPath), &cch); + szPath[countof(szPath) - 1] = '\0'; // ensure string is null terminated since registry won't guarantee that. + if (ERROR_SUCCESS == er) + { + hAssertFile = ::CreateFileA(szPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE != hAssertFile) + { + if (INVALID_SET_FILE_POINTER != ::SetFilePointer(hAssertFile, 0, 0, FILE_END)) + { + if (SUCCEEDED(::StringCchCatA(szMsg, countof(szMsg), "\r\n"))) + { + ::WriteFile(hAssertFile, szMsg, lstrlenA(szMsg), &cch, NULL); + } + } + } + } + } + + // if anything went wrong while fooling around with the registry, just show the usual assert dialog box + if (ERROR_SUCCESS != er) + { + hr = ::StringCchCatA(szMsg, countof(szMsg), "\nAbort=Debug, Retry=Skip, Ignore=Skip all"); + DExitOnFailure(hr, "failed to concat string while building assert message"); + + id = ::MessageBoxA(0, szMsg, "Debug Assert Message", + MB_SERVICE_NOTIFICATION | MB_TOPMOST | + MB_DEFBUTTON2 | MB_ABORTRETRYIGNORE); + } + } + + if (id == IDABORT) + { + if (Dutil_hAssertModule) + { + ::GetModuleFileNameA(Dutil_hAssertModule, szPath, countof(szPath)); + + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "Module is running from: %s\nIf you are not using pdb-stamping, place your PDB near the module and attach to process id: %d (0x%x)", szPath, ::GetCurrentProcessId(), ::GetCurrentProcessId()); + if (SUCCEEDED(hr)) + { + ::MessageBoxA(0, szMsg, "Debug Assert Message", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK); + } + } + + ::DebugBreak(); + } + else if (id == IDIGNORE) + { + Dutil_fNoAsserts = TRUE; + } + +LExit: + ReleaseFileHandle(hAssertFile); + ReleaseRegKey(hkDebug); + fInAssert = FALSE; +} + + +/******************************************************************* +Dutil_Assert + +*******************************************************************/ +extern "C" void DAPI Dutil_Assert( + __in_z LPCSTR szFile, + __in int iLine + ) +{ + HRESULT hr = S_OK; + char szMessage[DUTIL_STRING_BUFFER] = { }; + hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i", szFile, iLine); + if (SUCCEEDED(hr)) + { + Dutil_AssertMsg(szMessage); + } + else + { + Dutil_AssertMsg("Assert failed to build string"); + } +} + + +/******************************************************************* +Dutil_AssertSz + +*******************************************************************/ +extern "C" void DAPI Dutil_AssertSz( + __in_z LPCSTR szFile, + __in int iLine, + __in_z __format_string LPCSTR szMsg + ) +{ + HRESULT hr = S_OK; + char szMessage[DUTIL_STRING_BUFFER] = { }; + + hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i\n%s", szFile, iLine, szMsg); + if (SUCCEEDED(hr)) + { + Dutil_AssertMsg(szMessage); + } + else + { + Dutil_AssertMsg("Assert failed to build string"); + } +} + + +/******************************************************************* +Dutil_TraceSetLevel + +*******************************************************************/ +extern "C" void DAPI Dutil_TraceSetLevel( + __in REPORT_LEVEL rl, + __in BOOL fTraceFilenames + ) +{ + Dutil_rlCurrentTrace = rl; + Dutil_fTraceFilenames = fTraceFilenames; +} + + +/******************************************************************* +Dutil_TraceGetLevel + +*******************************************************************/ +extern "C" REPORT_LEVEL DAPI Dutil_TraceGetLevel() +{ + return Dutil_rlCurrentTrace; +} + + +/******************************************************************* +Dutil_Trace + +*******************************************************************/ +extern "C" void DAPIV Dutil_Trace( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid tracing level"); + + HRESULT hr = S_OK; + char szOutput[DUTIL_STRING_BUFFER] = { }; + char szMsg[DUTIL_STRING_BUFFER] = { }; + + if (Dutil_rlCurrentTrace < rl) + { + return; + } + + va_list args; + va_start(args, szFormat); + hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args); + va_end(args); + + if (SUCCEEDED(hr)) + { + LPCSTR szPrefix = "Trace/u"; + switch (rl) + { + case REPORT_STANDARD: + szPrefix = "Trace/s"; + break; + case REPORT_VERBOSE: + szPrefix = "Trace/v"; + break; + case REPORT_DEBUG: + szPrefix = "Trace/d"; + break; + } + + if (Dutil_fTraceFilenames) + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s [%s,%d]: %s\r\n", szPrefix, szFile, iLine, szOutput); + } + else + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s: %s\r\n", szPrefix, szOutput); + } + + if (SUCCEEDED(hr)) + { + OutputDebugStringA(szMsg); + } + // else fall through to the case below + } + + if (FAILED(hr)) + { + if (Dutil_fTraceFilenames) + { + ::StringCchPrintfA(szMsg, countof(szMsg), "Trace [%s,%d]: message too long, skipping\r\n", szFile, iLine); + } + else + { + ::StringCchPrintfA(szMsg, countof(szMsg), "Trace: message too long, skipping\r\n"); + } + + szMsg[countof(szMsg)-1] = '\0'; + OutputDebugStringA(szMsg); + } +} + + +/******************************************************************* +Dutil_TraceError + +*******************************************************************/ +extern "C" void DAPIV Dutil_TraceError( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + char szOutput[DUTIL_STRING_BUFFER] = { }; + char szMsg[DUTIL_STRING_BUFFER] = { }; + + // if this is NOT an error report and we're not logging at this level, bail + if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl) + { + return; + } + + va_list args; + va_start(args, szFormat); + hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args); + va_end(args); + + if (SUCCEEDED(hr)) + { + if (Dutil_fTraceFilenames) + { + if (FAILED(hrError)) + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: %s\r\n", hrError, szFile, iLine, szOutput); + } + else + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: %s\r\n", szFile, iLine, szOutput); + } + } + else + { + if (FAILED(hrError)) + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: %s\r\n", hrError, szOutput); + } + else + { + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: %s\r\n", szOutput); + } + } + + if (SUCCEEDED(hr)) + { + OutputDebugStringA(szMsg); + } + // else fall through to the failure case below + } + + if (FAILED(hr)) + { + if (Dutil_fTraceFilenames) + { + if (FAILED(hrError)) + { + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: message too long, skipping\r\n", hrError, szFile, iLine); + } + else + { + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: message too long, skipping\r\n", szFile, iLine); + } + } + else + { + if (FAILED(hrError)) + { + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: message too long, skipping\r\n", hrError); + } + else + { + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: message too long, skipping\r\n"); + } + } + + szMsg[countof(szMsg)-1] = '\0'; + OutputDebugStringA(szMsg); + } +} + + +DAPIV_(void) Dutil_TraceErrorSource( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in UINT source, + __in HRESULT hr, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + // if this is NOT an error report and we're not logging at this level, bail + if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl) + { + return; + } + + if (DUTIL_SOURCE_UNKNOWN != source && vpfnTraceErrorCallback) + { + va_list args; + va_start(args, szFormat); + vpfnTraceErrorCallback(szFile, iLine, rl, source, hr, szFormat, args); + va_end(args); + } +} + + +/******************************************************************* +Dutil_RootFailure + +*******************************************************************/ +extern "C" void DAPI Dutil_RootFailure( + __in_z LPCSTR szFile, + __in int iLine, + __in HRESULT hrError + ) +{ +#ifndef DEBUG + UNREFERENCED_PARAMETER(szFile); + UNREFERENCED_PARAMETER(iLine); + UNREFERENCED_PARAMETER(hrError); +#endif // DEBUG + + TraceError(hrError, "Root failure at %s:%d", szFile, iLine); +} + +/******************************************************************* + LoadSystemLibrary - Fully qualifies the path to a module in the + Windows system directory and loads it. + + Returns + E_MODNOTFOUND - The module could not be found. + * - Another error occured. +********************************************************************/ +extern "C" HRESULT DAPI LoadSystemLibrary( + __in_z LPCWSTR wzModuleName, + __out HMODULE *phModule + ) +{ + HRESULT hr = LoadSystemLibraryWithPath(wzModuleName, phModule, NULL); + return hr; +} + +/******************************************************************* + LoadSystemLibraryWithPath - Fully qualifies the path to a module in + the Windows system directory and loads it + and returns the path + + Returns + E_MODNOTFOUND - The module could not be found. + * - Another error occured. +********************************************************************/ +extern "C" HRESULT DAPI LoadSystemLibraryWithPath( + __in_z LPCWSTR wzModuleName, + __out HMODULE *phModule, + __deref_out_z_opt LPWSTR* psczPath + ) +{ + HRESULT hr = S_OK; + DWORD cch = 0; + WCHAR wzPath[MAX_PATH] = { }; + + cch = ::GetSystemDirectoryW(wzPath, MAX_PATH); + DExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory."); + + if (L'\\' != wzPath[cch - 1]) + { + hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1); + DExitOnRootFailure(hr, "Failed to terminate the string with a backslash."); + } + + hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName); + DExitOnRootFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName); + + *phModule = ::LoadLibraryW(wzPath); + DExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", wzModuleName); + + if (psczPath) + { + hr = StrAllocString(psczPath, wzPath, MAX_PATH); + DExitOnFailure(hr, "Failed to copy the path to library."); + } + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.nuspec b/src/libs/dutil/WixToolset.DUtil/dutil.nuspec new file mode 100644 index 00000000..3499a2d5 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/dutil.nuspec @@ -0,0 +1,27 @@ + + + + $id$ + $version$ + $authors$ + $authors$ + MS-RL + https://github.com/wixtoolset/dutil + false + $description$ + $copyright$ + + + + + + + + + + + + + + + diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj new file mode 100644 index 00000000..4e341438 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj @@ -0,0 +1,183 @@ + + + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + {1244E671-F108-4334-BA52-8A7517F26ECD} + StaticLibrary + dutil + true + v142 + MultiByte + WiX Toolset native library foundation + WixToolset.DUtil + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + 4091;4458 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters new file mode 100644 index 00000000..b93d166b --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters @@ -0,0 +1,372 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Header Files + + + + \ No newline at end of file diff --git a/src/libs/dutil/WixToolset.DUtil/eseutil.cpp b/src/libs/dutil/WixToolset.DUtil/eseutil.cpp new file mode 100644 index 00000000..b9455d4b --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/eseutil.cpp @@ -0,0 +1,1340 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define EseExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__) +#define EseExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__) +#define EseExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__) +#define EseExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__) +#define EseExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__) +#define EseExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ESEUTIL, e, x, s, __VA_ARGS__) +#define EseExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ESEUTIL, g, x, s, __VA_ARGS__) + +struct ESE_QUERY +{ + ESE_QUERY_TYPE qtQueryType; + BOOL fIndexRangeSet; + + JET_SESID jsSession; + JET_TABLEID jtTable; + + DWORD dwColumns; + void *pvData[10]; // The data queried for for this column + DWORD cbData[10]; // One for each column, describes the size of the corresponding entry in ppvData +}; + +// Todo: convert more JET_ERR to HRESULTS here +HRESULT HresultFromJetError(JET_ERR jEr) +{ + HRESULT hr = S_OK; + + switch (jEr) + { + case JET_errSuccess: + return S_OK; + + case JET_wrnNyi: + return E_NOTIMPL; + break; + + case JET_errOutOfMemory: + hr = E_OUTOFMEMORY; + break; + + case JET_errInvalidParameter: __fallthrough; + case JET_errInvalidInstance: + hr = E_INVALIDARG; + break; + + case JET_errDatabaseInUse: + hr = HRESULT_FROM_WIN32(ERROR_DEVICE_IN_USE); + break; + + case JET_errKeyDuplicate: + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); + break; + + case JET_errInvalidSystemPath: __fallthrough; + case JET_errInvalidLogDirectory: __fallthrough; + case JET_errInvalidPath: __fallthrough; + case JET_errDatabaseInvalidPath: + hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME); + break; + + case JET_errDatabaseLocked: + hr = HRESULT_FROM_WIN32(ERROR_FILE_CHECKED_OUT); + break; + + case JET_errInvalidDatabase: + hr = HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION); + break; + + case JET_errCallbackNotResolved: + hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); + break; + + case JET_errNoCurrentRecord: __fallthrough; + case JET_errRecordNotFound: __fallthrough; + case JET_errFileNotFound: __fallthrough; + case JET_errObjectNotFound: + hr = E_NOTFOUND; + break; + + case JET_wrnBufferTruncated: + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + break; + + case JET_errFileAccessDenied: + hr = E_ACCESSDENIED; + break; + + default: + hr = E_FAIL; + } + + // Log the actual Jet error code so we have record of it before it's morphed into an HRESULT to be compatible with the rest of our code + ExitTraceSource(DUTIL_SOURCE_ESEUTIL, hr, "Encountered Jet Error: 0x%08x", jEr); + + return hr; +} + +#define ExitOnJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { ExitTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__); goto LExit; }} +#define ExitOnRootJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__); goto LExit; }} + +HRESULT DAPI EseBeginSession( + __out JET_INSTANCE *pjiInstance, + __out JET_SESID *pjsSession, + __in_z LPCWSTR pszInstance, + __in_z LPCWSTR pszPath + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiInstance = NULL; + LPSTR pszAnsiPath = NULL; + + hr = DirEnsureExists(pszPath, NULL); + EseExitOnFailure(hr, "Failed to ensure database directory exists"); + + // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling, + // likely breaking everyone with unicode characters in their path. + hr = StrAnsiAllocString(&pszAnsiInstance, pszInstance, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting instance name to ansi"); + + hr = StrAnsiAllocString(&pszAnsiPath, pszPath, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting session path name to ansi"); + + jEr = JetCreateInstanceA(pjiInstance, pszAnsiInstance); + ExitOnJetFailure(jEr, hr, "Failed to create instance"); + + jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramSystemPath, NULL, pszAnsiPath); + ExitOnJetFailure(jEr, hr, "Failed to set jet system path to: %s", pszAnsiPath); + + // This makes sure log files that are created are created next to the database, not next to our EXE (note they last after execution) + jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramLogFilePath, NULL, pszAnsiPath); + ExitOnJetFailure(jEr, hr, "Failed to set jet log file path to: %s", pszAnsiPath); + + jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramMaxOpenTables, 10, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set jet max open tables parameter"); + + // TODO: Use callback hooks so that Jet Engine uses our memory allocation methods, etc.? (search docs for "JET_PFNREALLOC" - there are other callbacks too) + + jEr = JetInit(pjiInstance); + ExitOnJetFailure(jEr, hr, "Failed to initialize jet engine instance"); + + jEr = JetBeginSession(*pjiInstance, pjsSession, NULL, NULL); + ExitOnJetFailure(jEr, hr, "Failed to begin jet session"); + +LExit: + ReleaseStr(pszAnsiInstance); + ReleaseStr(pszAnsiPath); + + return hr; +} + +HRESULT DAPI EseEndSession( + __in JET_INSTANCE jiInstance, + __in JET_SESID jsSession + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetEndSession(jsSession, 0); + ExitOnJetFailure(jEr, hr, "Failed to end jet session"); + + jEr = JetTerm(jiInstance); + ExitOnJetFailure(jEr, hr, "Failed to uninitialize jet engine instance"); + +LExit: + return hr; +} + +// Utility function used by EnsureSchema() +HRESULT AllocColumnCreateStruct( + __in const ESE_TABLE_SCHEMA *ptsSchema, + __deref_out JET_COLUMNCREATE **ppjccColumnCreate + ) +{ + HRESULT hr = S_OK; + DWORD_PTR i; + size_t cbAllocSize = 0; + + hr = ::SizeTMult(ptsSchema->dwColumns, sizeof(JET_COLUMNCREATE), &(cbAllocSize)); + EseExitOnFailure(hr, "Maximum allocation exceeded."); + + *ppjccColumnCreate = static_cast(MemAlloc(cbAllocSize, TRUE)); + EseExitOnNull(*ppjccColumnCreate, hr, E_OUTOFMEMORY, "Failed to allocate column create structure for database"); + + for (i = 0; i < ptsSchema->dwColumns; ++i) + { + (*ppjccColumnCreate)[i].cbStruct = sizeof(JET_COLUMNCREATE); + + hr = StrAnsiAllocString(&(*ppjccColumnCreate)[i].szColumnName, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP); + EseExitOnFailure(hr, "Failed to allocate ansi column name: %ls", ptsSchema->pcsColumns[i].pszName); + + (*ppjccColumnCreate)[i].coltyp = ptsSchema->pcsColumns[i].jcColumnType; + + if (JET_coltypText == (*ppjccColumnCreate)[i].coltyp) + { + (*ppjccColumnCreate)[i].cbMax = 256; + } + else if (JET_coltypLongText == (*ppjccColumnCreate)[i].coltyp) + { + (*ppjccColumnCreate)[i].cbMax = 2147483648; + (*ppjccColumnCreate)[i].grbit = JET_bitColumnTagged; // LongText columns must be tagged + ptsSchema->pcsColumns[i].fNullable = TRUE; + } + else if (JET_coltypLong == (*ppjccColumnCreate)[i].coltyp) + { + (*ppjccColumnCreate)[i].cbMax = 4; + + if (ptsSchema->pcsColumns[i].fAutoIncrement) + { + (*ppjccColumnCreate)[i].grbit |= JET_bitColumnAutoincrement; + } + } + + if (!(ptsSchema->pcsColumns[i].fNullable)) + { + (*ppjccColumnCreate)[i].grbit |= JET_bitColumnNotNULL; + } + + (*ppjccColumnCreate)[i].pvDefault = NULL; + (*ppjccColumnCreate)[i].cbDefault = 0; + (*ppjccColumnCreate)[i].cp = 1200; + (*ppjccColumnCreate)[i].columnid = 0; + (*ppjccColumnCreate)[i].err = 0; + } + +LExit: + return hr; +} + +HRESULT FreeColumnCreateStruct( + __in_ecount(dwColumns) JET_COLUMNCREATE *pjccColumnCreate, + __in DWORD dwColumns + ) +{ + HRESULT hr = S_OK; + DWORD i; + + for (i = 0; i < dwColumns; ++i) + { + ReleaseStr((pjccColumnCreate[i]).szColumnName); + } + + hr = MemFree(pjccColumnCreate); + EseExitOnFailure(hr, "Failed to release core column create struct"); + +LExit: + return hr; +} + +// Utility function used by EnsureSchema() +HRESULT AllocIndexCreateStruct( + __in const ESE_TABLE_SCHEMA *ptsSchema, + __deref_out JET_INDEXCREATE **ppjicIndexCreate + ) +{ + HRESULT hr = S_OK; + LPSTR pszMultiSzKeys = NULL; + LPSTR pszIndexName = NULL; + LPSTR pszTempString = NULL; + BOOL fKeyColumns = FALSE; + DWORD_PTR i; + + for (i=0; i < ptsSchema->dwColumns; ++i) + { + if (ptsSchema->pcsColumns[i].fKey) + { + hr = StrAnsiAllocString(&pszTempString, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP); + EseExitOnFailure(hr, "Failed to convert string to ansi: %ls", ptsSchema->pcsColumns[i].pszName); + + hr = StrAnsiAllocConcat(&pszMultiSzKeys, "+", 0); + EseExitOnFailure(hr, "Failed to append plus sign to multisz string: %s", pszTempString); + + hr = StrAnsiAllocConcat(&pszMultiSzKeys, pszTempString, 0); + EseExitOnFailure(hr, "Failed to append column name to multisz string: %s", pszTempString); + + ReleaseNullStr(pszTempString); + + // All question marks will be converted to null characters later; this is just to trick dutil + // into letting us create an ansi, double-null-terminated list of single-null-terminated strings + hr = StrAnsiAllocConcat(&pszMultiSzKeys, "?", 0); + EseExitOnFailure(hr, "Failed to append placeholder character to multisz string: %hs", pszMultiSzKeys); + + // Record that at least one key column was found + fKeyColumns = TRUE; + } + } + + // If no key columns were found, don't create an index - just return + if (!fKeyColumns) + { + ExitFunction1(hr = S_OK); + } + + hr = StrAnsiAllocString(&pszIndexName, ptsSchema->pszName, 0, CP_ACP); + EseExitOnFailure(hr, "Failed to allocate ansi string version of %ls", ptsSchema->pszName); + + hr = StrAnsiAllocConcat(&pszIndexName, "_Index", 0); + EseExitOnFailure(hr, "Failed to append table name string version of %ls", ptsSchema->pszName); + + *ppjicIndexCreate = static_cast(MemAlloc(sizeof(JET_INDEXCREATE), TRUE)); + EseExitOnNull(*ppjicIndexCreate, hr, E_OUTOFMEMORY, "Failed to allocate index create structure for database"); + + // Record the size including both null terminators - the struct requires this + size_t cchSize = 0; + hr = ::StringCchLengthA(pszMultiSzKeys, STRSAFE_MAX_LENGTH, &cchSize); + EseExitOnRootFailure(hr, "Failed to get size of keys string"); + + ++cchSize; // add 1 to include null character at the end + + // At this point convert all question marks to null characters + for (i = 0; i < cchSize; ++i) + { + if ('?' == pszMultiSzKeys[i]) + { + pszMultiSzKeys[i] = '\0'; + } + } + + (*ppjicIndexCreate)->cbStruct = sizeof(JET_INDEXCREATE); + (*ppjicIndexCreate)->szIndexName = pszIndexName; + (*ppjicIndexCreate)->szKey = pszMultiSzKeys; + (*ppjicIndexCreate)->cbKey = (DWORD)cchSize; + (*ppjicIndexCreate)->grbit = JET_bitIndexUnique | JET_bitIndexPrimary; + (*ppjicIndexCreate)->ulDensity = 80; + (*ppjicIndexCreate)->lcid = 1033; + (*ppjicIndexCreate)->pidxunicode = NULL; + (*ppjicIndexCreate)->cbVarSegMac = 0; + (*ppjicIndexCreate)->rgconditionalcolumn = NULL; + (*ppjicIndexCreate)->cConditionalColumn = 0; + (*ppjicIndexCreate)->err = 0; + +LExit: + ReleaseStr(pszTempString); + + return hr; +} + +HRESULT EnsureSchema( + __in JET_DBID jdbDb, + __in JET_SESID jsSession, + __in ESE_DATABASE_SCHEMA *pdsSchema + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + BOOL fTransaction = FALSE; + DWORD dwTable; + DWORD dwColumn; + JET_TABLECREATE jtTableCreate = { }; + + // Set parameters which apply to all tables here + jtTableCreate.cbStruct = sizeof(jtTableCreate); + jtTableCreate.ulPages = 100; + jtTableCreate.ulDensity = 0; // per the docs, 0 means "use the default value" + jtTableCreate.cIndexes = 1; + + hr = EseBeginTransaction(jsSession); + EseExitOnFailure(hr, "Failed to begin transaction to create tables"); + fTransaction = TRUE; + + for (dwTable = 0;dwTable < pdsSchema->dwTables; ++dwTable) + { + // Don't free this pointer - it's just a shortcut to the current table's name within the struct + LPCWSTR pwzTableName = pdsSchema->ptsTables[dwTable].pszName; + + // Ensure table exists + hr = EseOpenTable(jsSession, jdbDb, pwzTableName, &pdsSchema->ptsTables[dwTable].jtTable); + if (E_NOTFOUND == hr) // if the table is missing, create it + { + // Fill out the JET_TABLECREATE struct + hr = StrAnsiAllocString(&jtTableCreate.szTableName, pdsSchema->ptsTables[dwTable].pszName, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting table name to ansi"); + + hr = AllocColumnCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgcolumncreate); + EseExitOnFailure(hr, "Failed to allocate column create struct"); + + hr = AllocIndexCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgindexcreate); + EseExitOnFailure(hr, "Failed to allocate index create struct"); + + jtTableCreate.cColumns = pdsSchema->ptsTables[dwTable].dwColumns; + jtTableCreate.tableid = NULL; + + // TODO: Investigate why we can't create a table without a key column? + // Actually create the table using our JET_TABLECREATE struct + jEr = JetCreateTableColumnIndex(jsSession, jdbDb, &jtTableCreate); + ExitOnJetFailure(jEr, hr, "Failed to create %ls table", pwzTableName); + + // Record the table ID in our cache + pdsSchema->ptsTables[dwTable].jtTable = jtTableCreate.tableid; + + // Record the column IDs in our cache + for (dwColumn = 0; dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn) + { + pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn].jcColumn = jtTableCreate.rgcolumncreate[dwColumn].columnid; + } + + // Free and NULL things we allocated in this struct + ReleaseNullStr(jtTableCreate.szTableName); + + hr = FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns); + EseExitOnFailure(hr, "Failed to free column create struct"); + jtTableCreate.rgcolumncreate = NULL; + } + else + { + // If the table already exists, grab the column ids and put them into our cache + for (dwColumn = 0;dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn) + { + // Don't free this - it's just a shortcut to the current column within the struct + ESE_COLUMN_SCHEMA *pcsColumn = &(pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn]); + ULONG ulColumnSize = 0; + BOOL fNullable = pcsColumn->fNullable; + + // Todo: this code is nearly duplicated from AllocColumnCreateStruct - factor it out! + if (JET_coltypText == pcsColumn->jcColumnType) + { + ulColumnSize = 256; + } + else if (JET_coltypLongText == pcsColumn->jcColumnType) + { + ulColumnSize = 2147483648; + fNullable = TRUE; + } + else if (JET_coltypLong == pcsColumn->jcColumnType) + { + ulColumnSize = 4; + fNullable = TRUE; + } + + hr = EseEnsureColumn(jsSession, pdsSchema->ptsTables[dwTable].jtTable, pcsColumn->pszName, pcsColumn->jcColumnType, ulColumnSize, pcsColumn->fFixed, fNullable, &pcsColumn->jcColumn); + EseExitOnFailure(hr, "Failed to create column %u of %ls table", dwColumn, pwzTableName); + } + } + } + +LExit: + ReleaseStr(jtTableCreate.szTableName); + + if (NULL != jtTableCreate.rgcolumncreate) + { + // Don't record the HRESULT here or it will override the return value of this function + FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns); + } + + if (fTransaction) + { + EseCommitTransaction(jsSession); + } + + return hr; +} + +// Todo: support overwrite flag? Unfortunately, requires WinXP and up +// Todo: Add version parameter, and a built-in dutil table that stores the version of the database schema on disk - then allow overriding the "migrate to new schema" functionality with a callback +HRESULT DAPI EseEnsureDatabase( + __in JET_SESID jsSession, + __in_z LPCWSTR pszFile, + __in ESE_DATABASE_SCHEMA *pdsSchema, + __out JET_DBID* pjdbDb, + __in BOOL fExclusive, + __in BOOL fReadonly + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + JET_GRBIT jgrOptions = 0; + LPWSTR pszDir = NULL; + LPSTR pszAnsiFile = NULL; + + // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling, + // likely breaking all those with unicode characters in their path. + hr = StrAnsiAllocString(&pszAnsiFile, pszFile, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting database name to ansi"); + + hr = PathGetDirectory(pszFile, &pszDir); + EseExitOnFailure(hr, "Failed to get directory that will contain database file"); + + hr = DirEnsureExists(pszDir, NULL); + EseExitOnFailure(hr, "Failed to ensure directory exists for database: %ls", pszDir); + + if (FileExistsEx(pszFile, NULL)) + { + if (fReadonly) + { + jgrOptions = jgrOptions | JET_bitDbReadOnly; + } + + jEr = JetAttachDatabaseA(jsSession, pszAnsiFile, jgrOptions); + ExitOnJetFailure(jEr, hr, "Failed to attach to database %s", pszAnsiFile); + + // This flag doesn't apply to attach, only applies to Open, so only set it after the attach + if (fExclusive) + { + jgrOptions = jgrOptions | JET_bitDbExclusive; + } + + jEr = JetOpenDatabaseA(jsSession, pszAnsiFile, NULL, pjdbDb, jgrOptions); + ExitOnJetFailure(jEr, hr, "Failed to open database %s", pszAnsiFile); + } + else + { + jEr = JetCreateDatabase2A(jsSession, pszAnsiFile, 0, pjdbDb, 0); + ExitOnJetFailure(jEr, hr, "Failed to create database %ls", pszFile); + } + + hr = EnsureSchema(*pjdbDb, jsSession, pdsSchema); + EseExitOnFailure(hr, "Failed to ensure database schema matches expectations"); + +LExit: + ReleaseStr(pszDir); + ReleaseStr(pszAnsiFile); + + return hr; +} + +HRESULT DAPI EseCloseDatabase( + __in JET_SESID jsSession, + __in JET_DBID jdbDb + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + JET_GRBIT jgrOptions = 0; + + jEr = JetCloseDatabase(jsSession, jdbDb, jgrOptions); + ExitOnJetFailure(jEr, hr, "Failed to close database"); + +LExit: + return hr; +} + +HRESULT DAPI EseCreateTable( + __in JET_SESID jsSession, + __in JET_DBID jdbDb, + __in_z LPCWSTR pszTable, + __out JET_TABLEID *pjtTable + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiTable = NULL; + + hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting table name to ansi"); + + jEr = JetCreateTableA(jsSession, jdbDb, pszAnsiTable, 100, 0, pjtTable); + ExitOnJetFailure(jEr, hr, "Failed to create table %s", pszAnsiTable); + +LExit: + ReleaseStr(pszAnsiTable); + + return hr; +} + +HRESULT DAPI EseOpenTable( + __in JET_SESID jsSession, + __in JET_DBID jdbDb, + __in_z LPCWSTR pszTable, + __out JET_TABLEID *pjtTable + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiTable = NULL; + + hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting table name to ansi"); + + jEr = JetOpenTableA(jsSession, jdbDb, pszAnsiTable, NULL, 0, 0, pjtTable); + ExitOnJetFailure(jEr, hr, "Failed to open table %s", pszAnsiTable); + +LExit: + ReleaseStr(pszAnsiTable); + + return hr; +} + +HRESULT DAPI EseCloseTable( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetCloseTable(jsSession, jtTable); + ExitOnJetFailure(jEr, hr, "Failed to close table"); + +LExit: + return hr; +} + +HRESULT DAPI EseEnsureColumn( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in_z LPCWSTR pszColumnName, + __in JET_COLTYP jcColumnType, + __in ULONG ulColumnSize, + __in BOOL fFixed, + __in BOOL fNullable, + __out_opt JET_COLUMNID *pjcColumn + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiColumnName = NULL; + JET_COLUMNDEF jcdColumnDef = { sizeof(JET_COLUMNDEF) }; + JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) }; + + hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting column name to ansi"); + + jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase); + if (JET_errSuccess == jEr) + { + // Return the found columnID + if (NULL != pjcColumn) + { + *pjcColumn = jcdTempBase.columnid; + } + + ExitFunction1(hr = S_OK); + } + else if (JET_errColumnNotFound == jEr) + { + jEr = JET_errSuccess; + } + ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName); + + jcdColumnDef.columnid = 0; + jcdColumnDef.coltyp = jcColumnType; + jcdColumnDef.wCountry = 0; + jcdColumnDef.langid = 0; + jcdColumnDef.cp = 1200; + jcdColumnDef.wCollate = 0; + jcdColumnDef.cbMax = ulColumnSize; + jcdColumnDef.grbit = 0; + + if (fFixed) + { + jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnFixed; + } + if (!fNullable) + { + jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnNotNULL; + } + + jEr = JetAddColumnA(jsSession, jtTable, pszAnsiColumnName, &jcdColumnDef, NULL, 0, pjcColumn); + ExitOnJetFailure(jEr, hr, "Failed to add column %ls", pszColumnName); + +LExit: + ReleaseStr(pszAnsiColumnName); + + return hr; +} + +HRESULT DAPI EseGetColumn( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in_z LPCWSTR pszColumnName, + __out JET_COLUMNID *pjcColumn + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + LPSTR pszAnsiColumnName = NULL; + JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) }; + + hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP); + EseExitOnFailure(hr, "Failed converting column name to ansi"); + + jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase); + if (JET_errSuccess == jEr) + { + // Return the found columnID + if (NULL != pjcColumn) + { + *pjcColumn = jcdTempBase.columnid; + } + + ExitFunction1(hr = S_OK); + } + ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName); + +LExit: + ReleaseStr(pszAnsiColumnName); + + return hr; +} + +HRESULT DAPI EseMoveCursor( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in LONG lRow + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetMove(jsSession, jtTable, lRow, 0); + ExitOnJetFailure(jEr, hr, "Failed to move jet cursor by amount: %d", lRow); + +LExit: + return hr; +} + +HRESULT DAPI EseDeleteRow( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetDelete(jsSession, jtTable); + ExitOnJetFailure(jEr, hr, "Failed to delete row"); + +LExit: + return hr; +} + +HRESULT DAPI EseBeginTransaction( + __in JET_SESID jsSession + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetBeginTransaction(jsSession); + ExitOnJetFailure(jEr, hr, "Failed to begin transaction"); + +LExit: + return hr; +} + +HRESULT DAPI EseRollbackTransaction( + __in JET_SESID jsSession, + __in BOOL fAll + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetRollback(jsSession, fAll ? JET_bitRollbackAll : 0); + ExitOnJetFailure(jEr, hr, "Failed to rollback transaction"); + +LExit: + return hr; +} + +HRESULT DAPI EseCommitTransaction( + __in JET_SESID jsSession + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetCommitTransaction(jsSession, 0); + ExitOnJetFailure(jEr, hr, "Failed to commit transaction"); + +LExit: + return hr; +} + +HRESULT DAPI EsePrepareUpdate( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in ULONG ulPrep + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetPrepareUpdate(jsSession, jtTable, ulPrep); + ExitOnJetFailure(jEr, hr, "Failed to prepare for update of type: %ul", ulPrep); + +LExit: + return hr; +} + +HRESULT DAPI EseFinishUpdate( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in BOOL fSeekToInsertedRecord + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + unsigned char rgbBookmark[JET_cbBookmarkMost + 1]; + DWORD cbBookmark; + + if (fSeekToInsertedRecord) + { + jEr = JetUpdate(jsSession, jtTable, rgbBookmark, sizeof(rgbBookmark), &cbBookmark); + ExitOnJetFailure(jEr, hr, "Failed to run update and retrieve bookmark"); + + jEr = JetGotoBookmark(jsSession, jtTable, rgbBookmark, cbBookmark); + ExitOnJetFailure(jEr, hr, "Failed to seek to recently updated record using bookmark"); + } + else + { + jEr = JetUpdate(jsSession, jtTable, NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to run update (without retrieving bookmark)"); + } + +LExit: + // If we fail, the caller won't expect that the update wasn't finished, so we'll cancel their entire update to leave them in a good state + if (FAILED(hr)) + { + JetPrepareUpdate(jsSession, jtTable, JET_prepCancel); + } + + return hr; +} + +HRESULT DAPI EseSetColumnBinary( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pbBuffer, static_cast(cbBuffer), 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set binary value into column of database"); + +LExit: + return hr; +} + +HRESULT DAPI EseSetColumnDword( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in DWORD dwValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &dwValue, sizeof(DWORD), 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set dword value into column of database: %u", dwValue); + +LExit: + return hr; +} + +HRESULT DAPI EseSetColumnBool( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in BOOL fValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + BYTE bValue = fValue ? 0xFF : 0x00; + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set bool value into column of database"); + +LExit: + return hr; +} + +HRESULT DAPI EseSetColumnString( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in_z LPCWSTR pwzValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + size_t cchValue = 0; + ULONG cbValueSize = 0; + + if (pwzValue) + { + hr = ::StringCchLengthW(pwzValue, STRSAFE_MAX_LENGTH, &cchValue); + EseExitOnRootFailure(hr, "Failed to get string length: %ls", pwzValue); + } + + cbValueSize = static_cast((cchValue + 1) * sizeof(WCHAR)); // add 1 for null character, then multiply by size of WCHAR to get bytes + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pwzValue, cbValueSize, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set string value into column of database: %ls", pwzValue); + +LExit: + return hr; +} + +HRESULT DAPI EseSetColumnEmpty( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to set empty value into column of database"); + +LExit: + return hr; +} + +HRESULT DAPI EseGetColumnBinary( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + ULONG ulActualSize = 0; + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL); + if (JET_wrnBufferTruncated == jEr) + { + jEr = JET_errSuccess; + } + ExitOnJetFailure(jEr, hr, "Failed to check size of binary value from record"); + + if (NULL == *ppbBuffer) + { + *ppbBuffer = reinterpret_cast(MemAlloc(ulActualSize, FALSE)); + EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for reading binary value column"); + } + else + { + *ppbBuffer = reinterpret_cast(MemReAlloc(*ppbBuffer, ulActualSize, FALSE)); + EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate memory for reading binary value column"); + } + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppbBuffer, ulActualSize, NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to retrieve binary value from record"); + + *piBuffer = static_cast(ulActualSize); + +LExit: + if (FAILED(hr)) + { + ReleaseNullMem(*ppbBuffer); + } + + return hr; +} + +HRESULT DAPI EseGetColumnDword( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out DWORD *pdwValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pdwValue, sizeof(DWORD), NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to retrieve dword value from record"); + +LExit: + return hr; +} + +HRESULT DAPI EseGetColumnBool( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out BOOL *pfValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + BYTE bValue = 0; + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to retrieve bool value from record"); + + if (bValue == 0) + { + *pfValue = FALSE; + } + else + { + *pfValue = TRUE; + } + +LExit: + return hr; +} + +HRESULT DAPI EseGetColumnString( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out LPWSTR *ppszValue + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + ULONG ulActualSize = 0; + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL); + if (JET_wrnBufferTruncated == jEr) + { + jEr = JET_errSuccess; + } + ExitOnJetFailure(jEr, hr, "Failed to check size of string value from record"); + + hr = StrAlloc(ppszValue, ulActualSize); + EseExitOnFailure(hr, "Failed to allocate string while retrieving column value"); + + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppszValue, ulActualSize, NULL, 0, NULL); + ExitOnJetFailure(jEr, hr, "Failed to retrieve string value from record"); + +LExit: + return hr; +} + +HRESULT DAPI EseBeginQuery( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in ESE_QUERY_TYPE qtQueryType, + __out ESE_QUERY_HANDLE *peqhHandle + ) +{ + UNREFERENCED_PARAMETER(jsSession); + UNREFERENCED_PARAMETER(jtTable); + + HRESULT hr = S_OK; + + *peqhHandle = static_cast(MemAlloc(sizeof(ESE_QUERY), TRUE)); + EseExitOnNull(*peqhHandle, hr, E_OUTOFMEMORY, "Failed to allocate new query"); + + ESE_QUERY *peqHandle = static_cast(*peqhHandle); + peqHandle->qtQueryType = qtQueryType; + peqHandle->jsSession = jsSession; + peqHandle->jtTable = jtTable; + +LExit: + return hr; +} + +// Utility function used by other functions to set a query column +HRESULT DAPI SetQueryColumn( + __in ESE_QUERY_HANDLE eqhHandle, + __in_bcount(cbData) const void *pvData, + __in DWORD cbData, + __in JET_GRBIT jGrb + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + ESE_QUERY *peqHandle = static_cast(eqhHandle); + + if (peqHandle->dwColumns == countof(peqHandle->pvData)) + { + hr = E_NOTIMPL; + EseExitOnFailure(hr, "Dutil hasn't implemented support for queries of more than %d columns", countof(peqHandle->pvData)); + } + + if (0 == peqHandle->dwColumns) // If it's the first column, start a new key + { + jGrb = jGrb | JET_bitNewKey; + } + + jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, pvData, cbData, jGrb); + ExitOnJetFailure(jEr, hr, "Failed to begin new query"); + + // If the query is wildcard, setup the cached copy of pvData + if (ESE_QUERY_EXACT != peqHandle->qtQueryType) + { + peqHandle->pvData[peqHandle->dwColumns] = MemAlloc(cbData, FALSE); + EseExitOnNull(peqHandle->pvData[peqHandle->dwColumns], hr, E_OUTOFMEMORY, "Failed to allocate memory"); + + memcpy(peqHandle->pvData[peqHandle->dwColumns], pvData, cbData); + + peqHandle->cbData[peqHandle->dwColumns] = cbData; + } + + // Increment the number of total columns + ++peqHandle->dwColumns; + +LExit: + return hr; +} + +HRESULT DAPI EseSetQueryColumnBinary( + __in ESE_QUERY_HANDLE eqhHandle, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ) +{ + HRESULT hr = S_OK; + ESE_QUERY *peqHandle = static_cast(eqhHandle); + JET_GRBIT jGrb = 0; + + if (cbBuffer > DWORD_MAX) + { + ExitFunction1(hr = E_INVALIDARG); + } + + if (fFinal) + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnStartLimit; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnEndLimit; + } + } + + hr = SetQueryColumn(eqhHandle, reinterpret_cast(pbBuffer), static_cast(cbBuffer), jGrb); + EseExitOnFailure(hr, "Failed to set value of query colum (as binary) to:"); + +LExit: + return hr; +} + +HRESULT DAPI EseSetQueryColumnDword( + __in ESE_QUERY_HANDLE eqhHandle, + __in DWORD dwData, + __in BOOL fFinal + ) +{ + HRESULT hr = S_OK; + ESE_QUERY *peqHandle = static_cast(eqhHandle); + JET_GRBIT jGrb = 0; + + if (fFinal) + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnStartLimit; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnEndLimit; + } + } + + hr = SetQueryColumn(eqhHandle, (const void *)&dwData, sizeof(DWORD), jGrb); + EseExitOnFailure(hr, "Failed to set value of query colum (as dword) to: %u", dwData); + +LExit: + return hr; +} + +HRESULT DAPI EseSetQueryColumnBool( + __in ESE_QUERY_HANDLE eqhHandle, + __in BOOL fValue, + __in BOOL fFinal + ) +{ + HRESULT hr = S_OK; + BYTE bByte = fValue ? 0xFF : 0x00; + ESE_QUERY *peqHandle = static_cast(eqhHandle); + JET_GRBIT jGrb = 0; + + if (fFinal) + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnStartLimit; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnEndLimit; + } + } + + hr = SetQueryColumn(eqhHandle, (const void *)&bByte, 1, jGrb); + EseExitOnFailure(hr, "Failed to set value of query colum (as bool) to: %s", fValue ? "TRUE" : "FALSE"); + +LExit: + return hr; +} + +HRESULT DAPI EseSetQueryColumnString( + __in ESE_QUERY_HANDLE eqhHandle, + __in_z LPCWSTR pszString, + __in BOOL fFinal + ) +{ + HRESULT hr = S_OK; + DWORD dwStringSize = 0; + size_t cchString = 0; + ESE_QUERY *peqHandle = static_cast(eqhHandle); + JET_GRBIT jGrb = 0; + + if (pszString) + { + hr = ::StringCchLengthW(pszString, STRSAFE_MAX_LENGTH, &cchString); + EseExitOnRootFailure(hr, "Failed to get size of column string"); + } + + dwStringSize = static_cast(sizeof(WCHAR) * (cchString + 1)); // Add 1 for null terminator + + if (fFinal) + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnStartLimit; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrb = jGrb | JET_bitFullColumnEndLimit; + } + } + + hr = SetQueryColumn(eqhHandle, (const void *)pszString, dwStringSize, jGrb); + EseExitOnFailure(hr, "Failed to set value of query colum (as string) to: %ls", pszString); + +LExit: + return hr; +} + +HRESULT DAPI EseFinishQuery( + __in ESE_QUERY_HANDLE eqhHandle + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + + ESE_QUERY *peqHandle = static_cast(eqhHandle); + + if (peqHandle->fIndexRangeSet) + { + jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeRemove); + ExitOnJetFailure(jEr, hr, "Failed to release index range"); + + peqHandle->fIndexRangeSet = FALSE; + } + + for (int i=0; i < countof(peqHandle->pvData); ++i) + { + ReleaseMem(peqHandle->pvData[i]); + } + + ReleaseMem(peqHandle); + +LExit: + return hr; +} + +HRESULT DAPI EseRunQuery( + __in ESE_QUERY_HANDLE eqhHandle + ) +{ + HRESULT hr = S_OK; + JET_ERR jEr = JET_errSuccess; + JET_GRBIT jGrb = 0; + JET_GRBIT jGrbSeekType = 0; + DWORD i; + + ESE_QUERY *peqHandle = static_cast(eqhHandle); + + if (ESE_QUERY_EXACT == peqHandle->qtQueryType) + { + jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, JET_bitSeekEQ); + ExitOnJetFailure(jEr, hr, "Failed to seek EQ within jet table"); + } + else + { + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType) + { + jGrbSeekType = JET_bitSeekGE; + } + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType) + { + jGrbSeekType = JET_bitSeekLE; + } + + jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, jGrbSeekType); + if (jEr == JET_wrnSeekNotEqual) + { + jEr = JET_errSuccess; + } + + // At this point we've already set our cursor to the beginning of the range of records to select. + // Now we'll make a key pointing to the end of the range of records to select, so we can call JetSetIndexRange() + // For a semi-explanation, see this doc page: http://msdn.microsoft.com/en-us/library/aa964799%28EXCHG.10%29.aspx + for (i = 0; i < peqHandle->dwColumns; ++i) + { + if (i == 0) + { + jGrb = JET_bitNewKey; + } + else + { + jGrb = 0; + } + + // On the last iteration + if (i == peqHandle->dwColumns - 1) + { + jGrb |= JET_bitFullColumnEndLimit; + } + + jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, peqHandle->pvData[i], peqHandle->cbData[i], jGrb); + ExitOnJetFailure(jEr, hr, "Failed to begin new query"); + } + + jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeUpperLimit); + ExitOnJetFailure(jEr, hr, "Failed to set index range"); + + peqHandle->fIndexRangeSet = TRUE; + + // Sometimes JetBlue doesn't check if there is a current record when calling the above function (and sometimes it does) + // So, let's check if there is a current record before returning (by reading the first byte of one). + jEr = JetMove(peqHandle->jsSession, peqHandle->jtTable, 0, 0); + ExitOnJetFailure(jEr, hr, "Failed to check if there is a current record after query"); + } + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/fileutil.cpp b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp new file mode 100644 index 00000000..1822727a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp @@ -0,0 +1,2032 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define FileExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__) +#define FileExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__) +#define FileExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__) +#define FileExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__) +#define FileExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_FILEUTIL, e, x, s, __VA_ARGS__) +#define FileExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_FILEUTIL, g, x, s, __VA_ARGS__) + +// constants + +const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF}; +const BYTE UTF16BOM[] = {0xFF, 0xFE}; + +const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager"; +const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations"; + +/******************************************************************* + FileFromPath - returns a pointer to the file part of the path + +********************************************************************/ +extern "C" LPWSTR DAPI FileFromPath( + __in_z LPCWSTR wzPath + ) +{ + if (!wzPath) + return NULL; + + LPWSTR wzFile = const_cast(wzPath); + for (LPWSTR wz = wzFile; *wz; ++wz) + { + // valid delineators + // \ => Windows path + // / => unix and URL path + // : => relative path from mapped root + if (L'\\' == *wz || L'/' == *wz || L':' == *wz) + wzFile = wz + 1; + } + + return wzFile; +} + + +/******************************************************************* + FileResolvePath - gets the full path to a file resolving environment + variables along the way. + +********************************************************************/ +extern "C" HRESULT DAPI FileResolvePath( + __in_z LPCWSTR wzRelativePath, + __out LPWSTR *ppwzFullPath + ) +{ + Assert(wzRelativePath && *wzRelativePath); + + HRESULT hr = S_OK; + DWORD cch = 0; + LPWSTR pwzExpandedPath = NULL; + DWORD cchExpandedPath = 0; + + LPWSTR pwzFullPath = NULL; + DWORD cchFullPath = 0; + + LPWSTR wzFileName = NULL; + + // + // First, expand any environment variables. + // + cchExpandedPath = MAX_PATH; + hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); + FileExitOnFailure(hr, "Failed to allocate space for expanded path."); + + cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); + if (0 == cch) + { + FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + } + else if (cchExpandedPath < cch) + { + cchExpandedPath = cch; + hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); + FileExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); + + cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); + if (0 == cch) + { + FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + } + else if (cchExpandedPath < cch) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + FileExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); + } + } + + // + // Second, get the full path. + // + cchFullPath = MAX_PATH; + hr = StrAlloc(&pwzFullPath, cchFullPath); + FileExitOnFailure(hr, "Failed to allocate space for full path."); + + cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); + if (0 == cch) + { + FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); + } + else if (cchFullPath < cch) + { + cchFullPath = cch; + hr = StrAlloc(&pwzFullPath, cchFullPath); + FileExitOnFailure(hr, "Failed to re-allocate more space for full path."); + + cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); + if (0 == cch) + { + FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); + } + else if (cchFullPath < cch) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + FileExitOnRootFailure(hr, "Failed to allocate buffer for full path."); + } + } + + *ppwzFullPath = pwzFullPath; + pwzFullPath = NULL; + +LExit: + ReleaseStr(pwzFullPath); + ReleaseStr(pwzExpandedPath); + + return hr; +} + + +/******************************************************************* +FileStripExtension - Strip extension from filename +********************************************************************/ +extern "C" HRESULT DAPI FileStripExtension( +__in_z LPCWSTR wzFileName, +__out LPWSTR *ppwzFileNameNoExtension +) +{ + Assert(wzFileName && *wzFileName); + + HRESULT hr = S_OK; + size_t cchFileName = 0; + LPWSTR pwzFileNameNoExtension = NULL; + size_t cchFileNameNoExtension = 0; + errno_t err = 0; + + hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_LENGTH, &cchFileName); + FileExitOnRootFailure(hr, "failed to get length of file name: %ls", wzFileName); + + cchFileNameNoExtension = cchFileName + 1; + + hr = StrAlloc(&pwzFileNameNoExtension, cchFileNameNoExtension); + FileExitOnFailure(hr, "failed to allocate space for File Name without extension"); + + // _wsplitpath_s can handle drive/path/filename/extension + err = _wsplitpath_s(wzFileName, NULL, NULL, NULL, NULL, pwzFileNameNoExtension, cchFileNameNoExtension, NULL, NULL); + if (err) + { + hr = E_INVALIDARG; + FileExitOnRootFailure(hr, "failed to parse filename: '%ls', error: %d", wzFileName, err); + } + + *ppwzFileNameNoExtension = pwzFileNameNoExtension; + pwzFileNameNoExtension = NULL; + +LExit: + ReleaseStr(pwzFileNameNoExtension); + + return hr; +} + + +/******************************************************************* +FileChangeExtension - Changes the extension of a filename +********************************************************************/ +extern "C" HRESULT DAPI FileChangeExtension( + __in_z LPCWSTR wzFileName, + __in_z LPCWSTR wzNewExtension, + __out LPWSTR *ppwzFileNameNewExtension + ) +{ + Assert(wzFileName && *wzFileName); + + HRESULT hr = S_OK; + LPWSTR sczFileName = NULL; + + hr = FileStripExtension(wzFileName, &sczFileName); + FileExitOnFailure(hr, "Failed to strip extension from file name: %ls", wzFileName); + + hr = StrAllocConcat(&sczFileName, wzNewExtension, 0); + FileExitOnFailure(hr, "Failed to add new extension."); + + *ppwzFileNameNewExtension = sczFileName; + sczFileName = NULL; + +LExit: + ReleaseStr(sczFileName); + + return hr; +} + + +/******************************************************************* +FileAddSuffixToBaseName - Adds a suffix the base portion of a file +name; e.g., file.ext to fileSuffix.ext. +********************************************************************/ +extern "C" HRESULT DAPI FileAddSuffixToBaseName( + __in_z LPCWSTR wzFileName, + __in_z LPCWSTR wzSuffix, + __out_z LPWSTR* psczNewFileName + ) +{ + Assert(wzFileName && *wzFileName); + + HRESULT hr = S_OK; + LPWSTR sczNewFileName = NULL; + size_t cchFileName = 0; + + hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_CCH, &cchFileName); + FileExitOnRootFailure(hr, "Failed to get length of file name: %ls", wzFileName); + + LPCWSTR wzExtension = wzFileName + cchFileName; + while (wzFileName < wzExtension && L'.' != *wzExtension) + { + --wzExtension; + } + + if (wzFileName < wzExtension) + { + // found an extension so add the suffix before it + hr = StrAllocFormatted(&sczNewFileName, L"%.*ls%ls%ls", static_cast(wzExtension - wzFileName), wzFileName, wzSuffix, wzExtension); + } + else + { + // no extension, so add the suffix at the end of the whole name + hr = StrAllocString(&sczNewFileName, wzFileName, 0); + FileExitOnFailure(hr, "Failed to allocate new file name."); + + hr = StrAllocConcat(&sczNewFileName, wzSuffix, 0); + } + FileExitOnFailure(hr, "Failed to allocate new file name with suffix."); + + *psczNewFileName = sczNewFileName; + sczNewFileName = NULL; + +LExit: + ReleaseStr(sczNewFileName); + + return hr; +} + + +/******************************************************************* + FileVersion + +********************************************************************/ +extern "C" HRESULT DAPI FileVersion( + __in_z LPCWSTR wzFilename, + __out DWORD *pdwVerMajor, + __out DWORD* pdwVerMinor + ) +{ + HRESULT hr = S_OK; + + DWORD dwHandle = 0; + UINT cbVerBuffer = 0; + LPVOID pVerBuffer = NULL; + VS_FIXEDFILEINFO* pvsFileInfo = NULL; + UINT cbFileInfo = 0; + + if (0 == (cbVerBuffer = ::GetFileVersionInfoSizeW(wzFilename, &dwHandle))) + { + FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); + } + + pVerBuffer = ::GlobalAlloc(GMEM_FIXED, cbVerBuffer); + FileExitOnNullDebugTrace(pVerBuffer, hr, E_OUTOFMEMORY, "failed to allocate version info for file: %ls", wzFilename); + + if (!::GetFileVersionInfoW(wzFilename, dwHandle, cbVerBuffer, pVerBuffer)) + { + FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); + } + + if (!::VerQueryValueW(pVerBuffer, L"\\", (void**)&pvsFileInfo, &cbFileInfo)) + { + FileExitOnLastErrorDebugTrace(hr, "failed to get version value for file: %ls", wzFilename); + } + + *pdwVerMajor = pvsFileInfo->dwFileVersionMS; + *pdwVerMinor = pvsFileInfo->dwFileVersionLS; + +LExit: + if (pVerBuffer) + { + ::GlobalFree(pVerBuffer); + } + return hr; +} + + +/******************************************************************* + FileVersionFromString + +*******************************************************************/ +extern "C" HRESULT DAPI FileVersionFromString( + __in_z LPCWSTR wzVersion, + __out DWORD* pdwVerMajor, + __out DWORD* pdwVerMinor + ) +{ + Assert(pdwVerMajor && pdwVerMinor); + + HRESULT hr = S_OK; + LPCWSTR pwz = wzVersion; + DWORD dw; + + *pdwVerMajor = 0; + *pdwVerMinor = 0; + + if ((L'v' == *pwz) || (L'V' == *pwz)) + { + ++pwz; + } + + dw = wcstoul(pwz, (WCHAR**)&pwz, 10); + if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) + { + *pdwVerMajor = dw << 16; + + if (!*pwz) + { + ExitFunction1(hr = S_OK); + } + ++pwz; + } + else + { + ExitFunction1(hr = S_FALSE); + } + + dw = wcstoul(pwz, (WCHAR**)&pwz, 10); + if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) + { + *pdwVerMajor |= dw; + + if (!*pwz) + { + ExitFunction1(hr = S_OK); + } + ++pwz; + } + else + { + ExitFunction1(hr = S_FALSE); + } + + dw = wcstoul(pwz, (WCHAR**)&pwz, 10); + if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) + { + *pdwVerMinor = dw << 16; + + if (!*pwz) + { + ExitFunction1(hr = S_OK); + } + ++pwz; + } + else + { + ExitFunction1(hr = S_FALSE); + } + + dw = wcstoul(pwz, (WCHAR**)&pwz, 10); + if (pwz && L'\0' == *pwz && dw < 0x10000) + { + *pdwVerMinor |= dw; + } + else + { + ExitFunction1(hr = S_FALSE); + } + +LExit: + return hr; +} + + +/******************************************************************* + FileVersionFromStringEx + +*******************************************************************/ +extern "C" HRESULT DAPI FileVersionFromStringEx( + __in_z LPCWSTR wzVersion, + __in SIZE_T cchVersion, + __out DWORD64* pqwVersion + ) +{ + Assert(wzVersion); + Assert(pqwVersion); + + HRESULT hr = S_OK; + LPCWSTR wzEnd = NULL; + LPCWSTR wzPartBegin = wzVersion; + LPCWSTR wzPartEnd = wzVersion; + DWORD iPart = 0; + USHORT us = 0; + DWORD64 qwVersion = 0; + + // get string length if not provided + if (0 >= cchVersion) + { + hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast(&cchVersion)); + FileExitOnRootFailure(hr, "Failed to get length of file version string: %ls", wzVersion); + + if (0 >= cchVersion) + { + ExitFunction1(hr = E_INVALIDARG); + } + } + + if ((L'v' == *wzVersion) || (L'V' == *wzVersion)) + { + ++wzVersion; + --cchVersion; + wzPartBegin = wzVersion; + wzPartEnd = wzVersion; + } + + // save end pointer + wzEnd = wzVersion + cchVersion; + + // loop through parts + for (;;) + { + if (4 <= iPart) + { + // error, too many parts + ExitFunction1(hr = E_INVALIDARG); + } + + // find end of part + while (wzPartEnd < wzEnd && L'.' != *wzPartEnd) + { + ++wzPartEnd; + } + if (wzPartBegin == wzPartEnd) + { + // error, empty part + ExitFunction1(hr = E_INVALIDARG); + } + + DWORD cchPart; + hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart); + FileExitOnFailure(hr, "Version number part was too long."); + + // parse version part + hr = StrStringToUInt16(wzPartBegin, cchPart, &us); + FileExitOnFailure(hr, "Failed to parse version number part."); + + // add part to qword version + qwVersion |= (DWORD64)us << ((3 - iPart) * 16); + + if (wzPartEnd >= wzEnd) + { + // end of string + break; + } + + wzPartBegin = ++wzPartEnd; // skip over separator + ++iPart; + } + + *pqwVersion = qwVersion; + +LExit: + return hr; +} + +/******************************************************************* + FileVersionFromStringEx - Formats the DWORD64 as a string version. + +*******************************************************************/ +extern "C" HRESULT DAPI FileVersionToStringEx( + __in DWORD64 qwVersion, + __out LPWSTR* psczVersion + ) +{ + HRESULT hr = S_OK; + WORD wMajor = 0; + WORD wMinor = 0; + WORD wBuild = 0; + WORD wRevision = 0; + + // Mask and shift each WORD for each field. + wMajor = (WORD)(qwVersion >> 48 & 0xffff); + wMinor = (WORD)(qwVersion >> 32 & 0xffff); + wBuild = (WORD)(qwVersion >> 16 & 0xffff); + wRevision = (WORD)(qwVersion & 0xffff); + + // Format and return the version string. + hr = StrAllocFormatted(psczVersion, L"%u.%u.%u.%u", wMajor, wMinor, wBuild, wRevision); + FileExitOnFailure(hr, "Failed to allocate and format the version number."); + +LExit: + return hr; +} + +/******************************************************************* + FileSetPointer - sets the file pointer. + +********************************************************************/ +extern "C" HRESULT DAPI FileSetPointer( + __in HANDLE hFile, + __in DWORD64 dw64Move, + __out_opt DWORD64* pdw64NewPosition, + __in DWORD dwMoveMethod + ) +{ + Assert(INVALID_HANDLE_VALUE != hFile); + + HRESULT hr = S_OK; + LARGE_INTEGER liMove; + LARGE_INTEGER liNewPosition; + + liMove.QuadPart = dw64Move; + if (!::SetFilePointerEx(hFile, liMove, &liNewPosition, dwMoveMethod)) + { + FileExitWithLastError(hr, "Failed to set file pointer."); + } + + if (pdw64NewPosition) + { + *pdw64NewPosition = liNewPosition.QuadPart; + } + +LExit: + return hr; +} + + +/******************************************************************* + FileSize + +********************************************************************/ +extern "C" HRESULT DAPI FileSize( + __in_z LPCWSTR pwzFileName, + __out LONGLONG* pllSize + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + FileExitOnNull(pwzFileName, hr, E_INVALIDARG, "Attempted to check filename, but no filename was provided"); + + hFile = ::CreateFileW(pwzFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (INVALID_HANDLE_VALUE == hFile) + { + FileExitWithLastError(hr, "Failed to open file %ls while checking file size", pwzFileName); + } + + hr = FileSizeByHandle(hFile, pllSize); + FileExitOnFailure(hr, "Failed to check size of file %ls by handle", pwzFileName); + +LExit: + ReleaseFileHandle(hFile); + + return hr; +} + + +/******************************************************************* + FileSizeByHandle + +********************************************************************/ +extern "C" HRESULT DAPI FileSizeByHandle( + __in HANDLE hFile, + __out LONGLONG* pllSize + ) +{ + Assert(INVALID_HANDLE_VALUE != hFile && pllSize); + HRESULT hr = S_OK; + LARGE_INTEGER li; + + *pllSize = 0; + + if (!::GetFileSizeEx(hFile, &li)) + { + FileExitWithLastError(hr, "Failed to get size of file."); + } + + *pllSize = li.QuadPart; + +LExit: + return hr; +} + + +/******************************************************************* + FileExistsEx + +********************************************************************/ +extern "C" BOOL DAPI FileExistsEx( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ) +{ + Assert(wzPath && *wzPath); + BOOL fExists = FALSE; + + WIN32_FIND_DATAW fd = { }; + HANDLE hff; + + if (INVALID_HANDLE_VALUE != (hff = ::FindFirstFileW(wzPath, &fd))) + { + ::FindClose(hff); + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + if (pdwAttributes) + { + *pdwAttributes = fd.dwFileAttributes; + } + + fExists = TRUE; + } + } + + return fExists; +} + + +/******************************************************************* + FileExistsAfterRestart - checks that a file exists and will continue + to exist after restart. + +********************************************************************/ +extern "C" BOOL DAPI FileExistsAfterRestart( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ) +{ + HRESULT hr = S_OK; + BOOL fExists = FALSE; + HKEY hkPendingFileRename = NULL; + LPWSTR* rgsczRenames = NULL; + DWORD cRenames = 0; + int nCompare = 0; + + fExists = FileExistsEx(wzPath, pdwAttributes); + if (fExists) + { + hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE, &hkPendingFileRename); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + FileExitOnFailure(hr, "Failed to open pending file rename registry key."); + + hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + FileExitOnFailure(hr, "Failed to read pending file renames."); + + // The pending file renames array is pairs of source and target paths. We only care + // about checking the source paths so skip the target paths (i += 2). + for (DWORD i = 0; i < cRenames; i += 2) + { + LPWSTR wzRename = rgsczRenames[i]; + if (wzRename && *wzRename) + { + // Skip the long path designator if present. + if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) + { + wzRename += 4; + } + + hr = PathCompare(wzPath, wzRename, &nCompare); + FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); + + if (CSTR_EQUAL == nCompare) + { + fExists = FALSE; + break; + } + } + } + } + +LExit: + ReleaseStrArray(rgsczRenames, cRenames); + ReleaseRegKey(hkPendingFileRename); + + return fExists; +} + + +/******************************************************************* + FileRemoveFromPendingRename - removes the file path from the pending + file rename list. + +********************************************************************/ +extern "C" HRESULT DAPI FileRemoveFromPendingRename( + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + HKEY hkPendingFileRename = NULL; + LPWSTR* rgsczRenames = NULL; + DWORD cRenames = 0; + int nCompare = 0; + BOOL fRemoved = FALSE; + DWORD cNewRenames = 0; + + hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkPendingFileRename); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + FileExitOnFailure(hr, "Failed to open pending file rename registry key."); + + hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + FileExitOnFailure(hr, "Failed to read pending file renames."); + + // The pending file renames array is pairs of source and target paths. We only care + // about checking the source paths so skip the target paths (i += 2). + for (DWORD i = 0; i < cRenames; i += 2) + { + LPWSTR wzRename = rgsczRenames[i]; + if (wzRename && *wzRename) + { + // Skip the long path designator if present. + if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) + { + wzRename += 4; + } + + hr = PathCompare(wzPath, wzRename, &nCompare); + FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); + + // If we find our path in the list, null out the source and target slot and + // we'll compact the array next. + if (CSTR_EQUAL == nCompare) + { + ReleaseNullStr(rgsczRenames[i]); + ReleaseNullStr(rgsczRenames[i + 1]); + fRemoved = TRUE; + } + } + } + + if (fRemoved) + { + // Compact the array by removing any nulls. + for (DWORD i = 0; i < cRenames; ++i) + { + LPWSTR wzRename = rgsczRenames[i]; + if (wzRename) + { + rgsczRenames[cNewRenames] = wzRename; + ++cNewRenames; + } + } + + cRenames = cNewRenames; // ignore the pointers on the end of the array since an early index points to them already. + + // Write the new array back to the pending file rename key. + hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames); + FileExitOnFailure(hr, "Failed to update pending file renames."); + } + +LExit: + ReleaseStrArray(rgsczRenames, cRenames); + ReleaseRegKey(hkPendingFileRename); + + return hr; +} + + +/******************************************************************* + FileRead - read a file into memory + +********************************************************************/ +extern "C" HRESULT DAPI FileRead( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath + ) +{ + HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE); + return hr; +} + +/******************************************************************* + FileRead - read a file into memory with specified share mode + +********************************************************************/ +extern "C" HRESULT DAPI FileReadEx( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in DWORD dwShareMode + ) +{ + HRESULT hr = FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE, dwShareMode); + return hr; +} + +/******************************************************************* + FileReadUntil - read a file into memory with a maximum size + +********************************************************************/ +extern "C" HRESULT DAPI FileReadUntil( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in DWORD cbMaxRead + ) +{ + HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, cbMaxRead, FALSE); + return hr; +} + + +/******************************************************************* + FileReadPartial - read a portion of a file into memory + +********************************************************************/ +extern "C" HRESULT DAPI FileReadPartial( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in BOOL fSeek, + __in DWORD cbStartPosition, + __in DWORD cbMaxRead, + __in BOOL fPartialOK + ) +{ + return FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, fSeek, cbStartPosition, cbMaxRead, fPartialOK, FILE_SHARE_READ | FILE_SHARE_DELETE); +} + +/******************************************************************* + FileReadPartial - read a portion of a file into memory + (with specified share mode) +********************************************************************/ +extern "C" HRESULT DAPI FileReadPartialEx( + __deref_inout_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in BOOL fSeek, + __in DWORD cbStartPosition, + __in DWORD cbMaxRead, + __in BOOL fPartialOK, + __in DWORD dwShareMode + ) +{ + HRESULT hr = S_OK; + + UINT er = ERROR_SUCCESS; + HANDLE hFile = INVALID_HANDLE_VALUE; + LARGE_INTEGER liFileSize = { }; + DWORD cbData = 0; + BYTE* pbData = NULL; + + FileExitOnNull(pcbDest, hr, E_INVALIDARG, "Invalid argument pcbDest"); + FileExitOnNull(ppbDest, hr, E_INVALIDARG, "Invalid argument ppbDest"); + FileExitOnNull(wzSrcPath, hr, E_INVALIDARG, "Invalid argument wzSrcPath"); + FileExitOnNull(*wzSrcPath, hr, E_INVALIDARG, "*wzSrcPath is null"); + + hFile = ::CreateFileW(wzSrcPath, GENERIC_READ, dwShareMode, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + er = ::GetLastError(); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + FileExitOnWin32Error(er, hr, "Failed to open file: %ls", wzSrcPath); + } + + if (!::GetFileSizeEx(hFile, &liFileSize)) + { + FileExitWithLastError(hr, "Failed to get size of file: %ls", wzSrcPath); + } + + if (fSeek) + { + if (cbStartPosition > liFileSize.QuadPart) + { + hr = E_INVALIDARG; + FileExitOnFailure(hr, "Start position %d bigger than file '%ls' size %llu", cbStartPosition, wzSrcPath, liFileSize.QuadPart); + } + + DWORD dwErr = ::SetFilePointer(hFile, cbStartPosition, NULL, FILE_CURRENT); + if (INVALID_SET_FILE_POINTER == dwErr) + { + FileExitOnLastError(hr, "Failed to seek position %d", cbStartPosition); + } + } + else + { + cbStartPosition = 0; + } + + if (fPartialOK) + { + cbData = cbMaxRead; + } + else + { + cbData = liFileSize.LowPart - cbStartPosition; // should only need the low part because we cap at DWORD + if (cbMaxRead < liFileSize.QuadPart - cbStartPosition) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + FileExitOnRootFailure(hr, "Failed to load file: %ls, too large.", wzSrcPath); + } + } + + if (*ppbDest) + { + if (0 == cbData) + { + ReleaseNullMem(*ppbDest); + *pcbDest = 0; + ExitFunction1(hr = S_OK); + } + + LPVOID pv = MemReAlloc(*ppbDest, cbData, TRUE); + FileExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to re-allocate memory to read in file: %ls", wzSrcPath); + + pbData = static_cast(pv); + } + else + { + if (0 == cbData) + { + *pcbDest = 0; + ExitFunction1(hr = S_OK); + } + + pbData = static_cast(MemAlloc(cbData, TRUE)); + FileExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in file: %ls", wzSrcPath); + } + + DWORD cbTotalRead = 0; + DWORD cbRead = 0; + do + { + DWORD cbRemaining = 0; + hr = ::ULongSub(cbData, cbTotalRead, &cbRemaining); + FileExitOnFailure(hr, "Underflow calculating remaining buffer size."); + + if (!::ReadFile(hFile, pbData + cbTotalRead, cbRemaining, &cbRead, NULL)) + { + FileExitWithLastError(hr, "Failed to read from file: %ls", wzSrcPath); + } + + cbTotalRead += cbRead; + } while (cbRead); + + if (cbTotalRead != cbData) + { + hr = E_UNEXPECTED; + FileExitOnFailure(hr, "Failed to completely read file: %ls", wzSrcPath); + } + + *ppbDest = pbData; + pbData = NULL; + *pcbDest = cbData; + +LExit: + ReleaseMem(pbData); + ReleaseFile(hFile); + + return hr; +} + +extern "C" HRESULT DAPI FileReadHandle( + __in HANDLE hFile, + __in_bcount(cbDest) LPBYTE pbDest, + __in SIZE_T cbDest + ) +{ + HRESULT hr = 0; + DWORD cbDataRead = 0; + SIZE_T cbRemaining = cbDest; + SIZE_T cbTotal = 0; + + while (0 < cbRemaining) + { + if (!::ReadFile(hFile, pbDest + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataRead, NULL)) + { + DWORD er = ::GetLastError(); + if (ERROR_MORE_DATA == er) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + FileExitOnRootFailure(hr, "Failed to read data from file handle."); + } + + cbRemaining -= cbDataRead; + cbTotal += cbDataRead; + } + +LExit: + return hr; +} + + +/******************************************************************* + FileWrite - write a file from memory + +********************************************************************/ +extern "C" HRESULT DAPI FileWrite( + __in_z LPCWSTR pwzFileName, + __in DWORD dwFlagsAndAttributes, + __in_bcount_opt(cbData) LPCBYTE pbData, + __in SIZE_T cbData, + __out_opt HANDLE* pHandle + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // Open the file + hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, dwFlagsAndAttributes, NULL); + FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file: %ls", pwzFileName); + + hr = FileWriteHandle(hFile, pbData, cbData); + FileExitOnFailure(hr, "Failed to write to file: %ls", pwzFileName); + + if (pHandle) + { + *pHandle = hFile; + hFile = INVALID_HANDLE_VALUE; + } + +LExit: + ReleaseFile(hFile); + + return hr; +} + + +/******************************************************************* + FileWriteHandle - write to a file handle from memory + +********************************************************************/ +extern "C" HRESULT DAPI FileWriteHandle( + __in HANDLE hFile, + __in_bcount_opt(cbData) LPCBYTE pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + DWORD cbDataWritten = 0; + SIZE_T cbTotal = 0; + SIZE_T cbRemaining = cbData; + + // Write out all of the data. + while (0 < cbRemaining) + { + if (!::WriteFile(hFile, pbData + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataWritten, NULL)) + { + FileExitOnLastError(hr, "Failed to write data to file handle."); + } + + cbRemaining -= cbDataWritten; + cbTotal += cbDataWritten; + } + +LExit: + return hr; +} + + +/******************************************************************* + FileCopyUsingHandles + +*******************************************************************/ +extern "C" HRESULT DAPI FileCopyUsingHandles( + __in HANDLE hSource, + __in HANDLE hTarget, + __in DWORD64 cbCopy, + __out_opt DWORD64* pcbCopied + ) +{ + HRESULT hr = S_OK; + DWORD64 cbTotalCopied = 0; + BYTE rgbData[4 * 1024]; + DWORD cbRead = 0; + + do + { + cbRead = static_cast((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied)); + if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL)) + { + FileExitWithLastError(hr, "Failed to read from source."); + } + + if (cbRead) + { + hr = FileWriteHandle(hTarget, rgbData, cbRead); + FileExitOnFailure(hr, "Failed to write to target."); + } + + cbTotalCopied += cbRead; + } while (cbTotalCopied < cbCopy && 0 != cbRead); + + if (pcbCopied) + { + *pcbCopied = cbTotalCopied; + } + +LExit: + return hr; +} + + +/******************************************************************* + FileCopyUsingHandlesWithProgress + +*******************************************************************/ +extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( + __in HANDLE hSource, + __in HANDLE hTarget, + __in DWORD64 cbCopy, + __in_opt LPPROGRESS_ROUTINE lpProgressRoutine, + __in_opt LPVOID lpData + ) +{ + HRESULT hr = S_OK; + DWORD64 cbTotalCopied = 0; + BYTE rgbData[64 * 1024]; + DWORD cbRead = 0; + + LARGE_INTEGER liSourceSize = { }; + LARGE_INTEGER liTotalCopied = { }; + LARGE_INTEGER liZero = { }; + DWORD dwResult = 0; + + hr = FileSizeByHandle(hSource, &liSourceSize.QuadPart); + FileExitOnFailure(hr, "Failed to get size of source."); + + if (0 < cbCopy && cbCopy < (DWORD64)liSourceSize.QuadPart) + { + liSourceSize.QuadPart = cbCopy; + } + + if (lpProgressRoutine) + { + dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_STREAM_SWITCH, hSource, hTarget, lpData); + switch (dwResult) + { + case PROGRESS_CONTINUE: + break; + + case PROGRESS_CANCEL: + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); + + case PROGRESS_STOP: + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); + + case PROGRESS_QUIET: + lpProgressRoutine = NULL; + break; + } + } + + // Set size of the target file. + ::SetFilePointerEx(hTarget, liSourceSize, NULL, FILE_BEGIN); + + if (!::SetEndOfFile(hTarget)) + { + FileExitWithLastError(hr, "Failed to set end of target file."); + } + + if (!::SetFilePointerEx(hTarget, liZero, NULL, FILE_BEGIN)) + { + FileExitWithLastError(hr, "Failed to reset target file pointer."); + } + + // Copy with progress. + while (0 == cbCopy || cbTotalCopied < cbCopy) + { + cbRead = static_cast((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied)); + if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL)) + { + FileExitWithLastError(hr, "Failed to read from source."); + } + + if (cbRead) + { + hr = FileWriteHandle(hTarget, rgbData, cbRead); + FileExitOnFailure(hr, "Failed to write to target."); + + cbTotalCopied += cbRead; + + if (lpProgressRoutine) + { + liTotalCopied.QuadPart = cbTotalCopied; + dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_CHUNK_FINISHED, hSource, hTarget, lpData); + switch (dwResult) + { + case PROGRESS_CONTINUE: + break; + + case PROGRESS_CANCEL: + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); + + case PROGRESS_STOP: + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED)); + + case PROGRESS_QUIET: + lpProgressRoutine = NULL; + break; + } + } + } + else + { + break; + } + } + +LExit: + return hr; +} + + +/******************************************************************* + FileEnsureCopy + +*******************************************************************/ +extern "C" HRESULT DAPI FileEnsureCopy( + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzTarget, + __in BOOL fOverwrite + ) +{ + HRESULT hr = S_OK; + DWORD er; + + // try to copy the file first + if (::CopyFileW(wzSource, wzTarget, !fOverwrite)) + { + ExitFunction(); // we're done + } + + er = ::GetLastError(); // check the error and do the right thing below + if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er)) + { + // if not overwriting this is an expected error + ExitFunction1(hr = S_FALSE); + } + else if (ERROR_PATH_NOT_FOUND == er) // if the path doesn't exist + { + // try to create the directory then do the copy + LPWSTR pwzLastSlash = NULL; + for (LPWSTR pwz = const_cast(wzTarget); *pwz; ++pwz) + { + if (*pwz == L'\\') + { + pwzLastSlash = pwz; + } + } + + if (pwzLastSlash) + { + *pwzLastSlash = L'\0'; // null terminate + hr = DirEnsureExists(wzTarget, NULL); + *pwzLastSlash = L'\\'; // now put the slash back + FileExitOnFailureDebugTrace(hr, "failed to create directory while copying file: '%ls' to: '%ls'", wzSource, wzTarget); + + // try to copy again + if (!::CopyFileW(wzSource, wzTarget, fOverwrite)) + { + FileExitOnLastErrorDebugTrace(hr, "failed to copy file: '%ls' to: '%ls'", wzSource, wzTarget); + } + } + else // no path was specified so just return the error + { + hr = HRESULT_FROM_WIN32(er); + } + } + else // unexpected error + { + hr = HRESULT_FROM_WIN32(er); + } + +LExit: + return hr; +} + + +/******************************************************************* + FileEnsureCopyWithRetry + +*******************************************************************/ +extern "C" HRESULT DAPI FileEnsureCopyWithRetry( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ) +{ + AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry."); + + HRESULT hr = E_FAIL; + DWORD i = 0; + + for (i = 0; FAILED(hr) && i <= cRetry; ++i) + { + if (0 < i) + { + ::Sleep(dwWaitMilliseconds); + } + + hr = FileEnsureCopy(wzSource, wzTarget, fOverwrite); + if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr + || HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr || HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) == hr) + { + break; // no reason to retry these errors. + } + } + FileExitOnFailure(hr, "Failed to copy file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); + +LExit: + return hr; +} + + +/******************************************************************* + FileEnsureMove + +*******************************************************************/ +extern "C" HRESULT DAPI FileEnsureMove( + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in BOOL fAllowCopy + ) +{ + HRESULT hr = S_OK; + DWORD er; + + DWORD dwFlags = 0; + + if (fOverwrite) + { + dwFlags |= MOVEFILE_REPLACE_EXISTING; + } + if (fAllowCopy) + { + dwFlags |= MOVEFILE_COPY_ALLOWED; + } + + // try to move the file first + if (::MoveFileExW(wzSource, wzTarget, dwFlags)) + { + ExitFunction(); // we're done + } + + er = ::GetLastError(); // check the error and do the right thing below + if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er)) + { + // if not overwriting this is an expected error + ExitFunction1(hr = S_FALSE); + } + else if (ERROR_FILE_NOT_FOUND == er) + { + // We are seeing some cases where ::MoveFileEx() says a file was not found + // but the source file is actually present. In that case, return path not + // found so we try to create the target path since that is most likely + // what is missing. Otherwise, the source file is missing and we're obviously + // not going to be recovering from that. + if (FileExistsEx(wzSource, NULL)) + { + er = ERROR_PATH_NOT_FOUND; + } + } + + // If the path doesn't exist, try to create the directory tree then do the move. + if (ERROR_PATH_NOT_FOUND == er) + { + LPWSTR pwzLastSlash = NULL; + for (LPWSTR pwz = const_cast(wzTarget); *pwz; ++pwz) + { + if (*pwz == L'\\') + { + pwzLastSlash = pwz; + } + } + + if (pwzLastSlash) + { + *pwzLastSlash = L'\0'; // null terminate + hr = DirEnsureExists(wzTarget, NULL); + *pwzLastSlash = L'\\'; // now put the slash back + FileExitOnFailureDebugTrace(hr, "failed to create directory while moving file: '%ls' to: '%ls'", wzSource, wzTarget); + + // try to move again + if (!::MoveFileExW(wzSource, wzTarget, dwFlags)) + { + FileExitOnLastErrorDebugTrace(hr, "failed to move file: '%ls' to: '%ls'", wzSource, wzTarget); + } + } + else // no path was specified so just return the error + { + hr = HRESULT_FROM_WIN32(er); + } + } + else // unexpected error + { + hr = HRESULT_FROM_WIN32(er); + } + +LExit: + return hr; +} + + +/******************************************************************* + FileEnsureMoveWithRetry + +*******************************************************************/ +extern "C" HRESULT DAPI FileEnsureMoveWithRetry( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in BOOL fAllowCopy, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ) +{ + AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry."); + + HRESULT hr = E_FAIL; + DWORD i = 0; + + for (i = 0; FAILED(hr) && i < cRetry + 1; ++i) + { + if (0 < i) + { + ::Sleep(dwWaitMilliseconds); + } + + hr = FileEnsureMove(wzSource, wzTarget, fOverwrite, fAllowCopy); + } + FileExitOnFailure(hr, "Failed to move file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); + +LExit: + return hr; +} + + +/******************************************************************* + FileCreateTemp - creates an empty temp file + + NOTE: uses ANSI functions internally so it is Win9x safe +********************************************************************/ +extern "C" HRESULT DAPI FileCreateTemp( + __in_z LPCWSTR wzPrefix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* ppwzTempFile, + __out_opt HANDLE* phTempFile + ) +{ + Assert(wzPrefix && *wzPrefix); + HRESULT hr = S_OK; + LPSTR pszTempPath = NULL; + DWORD cchTempPath = MAX_PATH; + + HANDLE hTempFile = INVALID_HANDLE_VALUE; + LPSTR pszTempFile = NULL; + + int i = 0; + + hr = StrAnsiAlloc(&pszTempPath, cchTempPath); + FileExitOnFailure(hr, "failed to allocate memory for the temp path"); + ::GetTempPathA(cchTempPath, pszTempPath); + + for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) + { + hr = StrAnsiAllocFormatted(&pszTempFile, "%s%ls%05d.%ls", pszTempPath, wzPrefix, i, wzExtension); + FileExitOnFailure(hr, "failed to allocate memory for log file"); + + hTempFile = ::CreateFileA(pszTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + // if the file already exists, just try again + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) + { + hr = S_OK; + continue; + } + FileExitOnFailureDebugTrace(hr, "failed to create file: %hs", pszTempFile); + } + } + + if (ppwzTempFile) + { + hr = StrAllocStringAnsi(ppwzTempFile, pszTempFile, 0, CP_UTF8); + } + + if (phTempFile) + { + *phTempFile = hTempFile; + hTempFile = INVALID_HANDLE_VALUE; + } + +LExit: + ReleaseFile(hTempFile); + ReleaseStr(pszTempFile); + ReleaseStr(pszTempPath); + + return hr; +} + + +/******************************************************************* + FileCreateTempW - creates an empty temp file + +*******************************************************************/ +extern "C" HRESULT DAPI FileCreateTempW( + __in_z LPCWSTR wzPrefix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* ppwzTempFile, + __out_opt HANDLE* phTempFile + ) +{ + Assert(wzPrefix && *wzPrefix); + HRESULT hr = E_FAIL; + + WCHAR wzTempPath[MAX_PATH]; + DWORD cchTempPath = countof(wzTempPath); + LPWSTR pwzTempFile = NULL; + + HANDLE hTempFile = INVALID_HANDLE_VALUE; + int i = 0; + + if (!::GetTempPathW(cchTempPath, wzTempPath)) + { + FileExitOnLastError(hr, "failed to get temp path"); + } + + for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) + { + hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", wzTempPath, wzPrefix, i, wzExtension); + FileExitOnFailure(hr, "failed to allocate memory for temp filename"); + + hTempFile = ::CreateFileW(pwzTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + // if the file already exists, just try again + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) + { + hr = S_OK; + continue; + } + FileExitOnFailureDebugTrace(hr, "failed to create file: %ls", pwzTempFile); + } + } + + if (phTempFile) + { + *phTempFile = hTempFile; + hTempFile = INVALID_HANDLE_VALUE; + } + + if (ppwzTempFile) + { + *ppwzTempFile = pwzTempFile; + pwzTempFile = NULL; + } + +LExit: + ReleaseFile(hTempFile); + ReleaseStr(pwzTempFile); + + return hr; +} + + +/******************************************************************* + FileIsSame + +********************************************************************/ +extern "C" HRESULT DAPI FileIsSame( + __in_z LPCWSTR wzFile1, + __in_z LPCWSTR wzFile2, + __out LPBOOL lpfSameFile + ) +{ + HRESULT hr = S_OK; + HANDLE hFile1 = NULL; + HANDLE hFile2 = NULL; + BY_HANDLE_FILE_INFORMATION fileInfo1 = { }; + BY_HANDLE_FILE_INFORMATION fileInfo2 = { }; + + hFile1 = ::CreateFileW(wzFile1, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + FileExitOnInvalidHandleWithLastError(hFile1, hr, "Failed to open file 1. File = '%ls'", wzFile1); + + hFile2 = ::CreateFileW(wzFile2, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + FileExitOnInvalidHandleWithLastError(hFile2, hr, "Failed to open file 2. File = '%ls'", wzFile2); + + if (!::GetFileInformationByHandle(hFile1, &fileInfo1)) + { + FileExitWithLastError(hr, "Failed to get information for file 1. File = '%ls'", wzFile1); + } + + if (!::GetFileInformationByHandle(hFile2, &fileInfo2)) + { + FileExitWithLastError(hr, "Failed to get information for file 2. File = '%ls'", wzFile2); + } + + *lpfSameFile = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber && + fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh && + fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow ? TRUE : FALSE; + +LExit: + ReleaseFile(hFile1); + ReleaseFile(hFile2); + + return hr; +} + +/******************************************************************* + FileEnsureDelete - deletes a file, first removing read-only, + hidden, or system attributes if necessary. +********************************************************************/ +extern "C" HRESULT DAPI FileEnsureDelete( + __in_z LPCWSTR wzFile + ) +{ + HRESULT hr = S_OK; + + DWORD dwAttrib = INVALID_FILE_ATTRIBUTES; + if (FileExistsEx(wzFile, &dwAttrib)) + { + if (dwAttrib & FILE_ATTRIBUTE_READONLY || dwAttrib & FILE_ATTRIBUTE_HIDDEN || dwAttrib & FILE_ATTRIBUTE_SYSTEM) + { + if (!::SetFileAttributesW(wzFile, FILE_ATTRIBUTE_NORMAL)) + { + FileExitOnLastError(hr, "Failed to remove attributes from file: %ls", wzFile); + } + } + + if (!::DeleteFileW(wzFile)) + { + FileExitOnLastError(hr, "Failed to delete file: %ls", wzFile); + } + } + +LExit: + return hr; +} + +/******************************************************************* + FileGetTime - Gets the file time of a specified file +********************************************************************/ +extern "C" HRESULT DAPI FileGetTime( + __in_z LPCWSTR wzFile, + __out_opt LPFILETIME lpCreationTime, + __out_opt LPFILETIME lpLastAccessTime, + __out_opt LPFILETIME lpLastWriteTime + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = NULL; + + hFile = ::CreateFileW(wzFile, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); + FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); + + if (!::GetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) + { + FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); + } + +LExit: + ReleaseFile(hFile); + return hr; +} + +/******************************************************************* + FileSetTime - Sets the file time of a specified file +********************************************************************/ +extern "C" HRESULT DAPI FileSetTime( + __in_z LPCWSTR wzFile, + __in_opt const FILETIME *lpCreationTime, + __in_opt const FILETIME *lpLastAccessTime, + __in_opt const FILETIME *lpLastWriteTime + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = NULL; + + hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); + FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); + + if (!::SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) + { + FileExitWithLastError(hr, "Failed to set file time for file. File = '%ls'", wzFile); + } + +LExit: + ReleaseFile(hFile); + return hr; +} + +/******************************************************************* + FileReSetTime - ReSets a file's last acess and modified time to the + creation time of the file +********************************************************************/ +extern "C" HRESULT DAPI FileResetTime( + __in_z LPCWSTR wzFile + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = NULL; + FILETIME ftCreateTime; + + hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); + + if (!::GetFileTime(hFile, &ftCreateTime, NULL, NULL)) + { + FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); + } + + if (!::SetFileTime(hFile, NULL, NULL, &ftCreateTime)) + { + FileExitWithLastError(hr, "Failed to reset file time for file. File = '%ls'", wzFile); + } + +LExit: + ReleaseFile(hFile); + return hr; +} + + +/******************************************************************* + FileExecutableArchitecture + +*******************************************************************/ +extern "C" HRESULT DAPI FileExecutableArchitecture( + __in_z LPCWSTR wzFile, + __out FILE_ARCHITECTURE *pArchitecture + ) +{ + HRESULT hr = S_OK; + + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD cbRead = 0; + IMAGE_DOS_HEADER DosImageHeader = { }; + IMAGE_NT_HEADERS NtImageHeader = { }; + + hFile = ::CreateFileW(wzFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (hFile == INVALID_HANDLE_VALUE) + { + FileExitWithLastError(hr, "Failed to open file: %ls", wzFile); + } + + if (!::ReadFile(hFile, &DosImageHeader, sizeof(DosImageHeader), &cbRead, NULL)) + { + FileExitWithLastError(hr, "Failed to read DOS header from file: %ls", wzFile); + } + + if (DosImageHeader.e_magic != IMAGE_DOS_SIGNATURE) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + FileExitOnRootFailure(hr, "Read invalid DOS header from file: %ls", wzFile); + } + + if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hFile, DosImageHeader.e_lfanew, NULL, FILE_BEGIN)) + { + FileExitWithLastError(hr, "Failed to seek the NT header in file: %ls", wzFile); + } + + if (!::ReadFile(hFile, &NtImageHeader, sizeof(NtImageHeader), &cbRead, NULL)) + { + FileExitWithLastError(hr, "Failed to read NT header from file: %ls", wzFile); + } + + if (NtImageHeader.Signature != IMAGE_NT_SIGNATURE) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + FileExitOnRootFailure(hr, "Read invalid NT header from file: %ls", wzFile); + } + + if (IMAGE_SUBSYSTEM_NATIVE == NtImageHeader.OptionalHeader.Subsystem || + IMAGE_SUBSYSTEM_WINDOWS_GUI == NtImageHeader.OptionalHeader.Subsystem || + IMAGE_SUBSYSTEM_WINDOWS_CUI == NtImageHeader.OptionalHeader.Subsystem) + { + switch (NtImageHeader.FileHeader.Machine) + { + case IMAGE_FILE_MACHINE_I386: + *pArchitecture = FILE_ARCHITECTURE_X86; + break; + case IMAGE_FILE_MACHINE_IA64: + *pArchitecture = FILE_ARCHITECTURE_IA64; + break; + case IMAGE_FILE_MACHINE_AMD64: + *pArchitecture = FILE_ARCHITECTURE_X64; + break; + default: + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + break; + } + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + } + FileExitOnFailure(hr, "Unexpected subsystem: %d machine type: %d specified in NT header from file: %ls", NtImageHeader.OptionalHeader.Subsystem, NtImageHeader.FileHeader.Machine, wzFile); + +LExit: + if (hFile != INVALID_HANDLE_VALUE) + { + ::CloseHandle(hFile); + } + + return hr; +} + +/******************************************************************* + FileToString + +*******************************************************************/ +extern "C" HRESULT DAPI FileToString( + __in_z LPCWSTR wzFile, + __out LPWSTR *psczString, + __out_opt FILE_ENCODING *pfeEncoding + ) +{ + HRESULT hr = S_OK; + BYTE *pbFullFileBuffer = NULL; + SIZE_T cbFullFileBuffer = 0; + BOOL fNullCharFound = FALSE; + LPWSTR sczFileText = NULL; + + // Check if the file is ANSI + hr = FileRead(&pbFullFileBuffer, &cbFullFileBuffer, wzFile); + FileExitOnFailure(hr, "Failed to read file: %ls", wzFile); + + if (0 == cbFullFileBuffer) + { + *psczString = NULL; + ExitFunction1(hr = S_OK); + } + + // UTF-8 BOM + if (cbFullFileBuffer > sizeof(UTF8BOM) && 0 == memcmp(pbFullFileBuffer, UTF8BOM, sizeof(UTF8BOM))) + { + if (pfeEncoding) + { + *pfeEncoding = FILE_ENCODING_UTF8_WITH_BOM; + } + + hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast(pbFullFileBuffer + 3), cbFullFileBuffer - 3, CP_UTF8); + FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8 as its BOM indicated", wzFile); + + *psczString = sczFileText; + sczFileText = NULL; + } + // UTF-16 BOM, little endian (windows regular UTF-16) + else if (cbFullFileBuffer > sizeof(UTF16BOM) && 0 == memcmp(pbFullFileBuffer, UTF16BOM, sizeof(UTF16BOM))) + { + if (pfeEncoding) + { + *pfeEncoding = FILE_ENCODING_UTF16_WITH_BOM; + } + + hr = StrAllocString(psczString, reinterpret_cast(pbFullFileBuffer + 2), (cbFullFileBuffer - 2) / sizeof(WCHAR)); + FileExitOnFailure(hr, "Failed to allocate copy of string"); + } + // No BOM, let's try to detect + else + { + for (DWORD i = 0; i < cbFullFileBuffer; ++i) + { + if (pbFullFileBuffer[i] == '\0') + { + fNullCharFound = TRUE; + break; + } + } + + if (!fNullCharFound) + { + if (pfeEncoding) + { + *pfeEncoding = FILE_ENCODING_UTF8; + } + + hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast(pbFullFileBuffer), cbFullFileBuffer, CP_UTF8); + if (FAILED(hr)) + { + if (E_OUTOFMEMORY == hr) + { + FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8", wzFile); + } + } + else + { + *psczString = sczFileText; + sczFileText = NULL; + } + } + else if (NULL == *psczString) + { + if (pfeEncoding) + { + *pfeEncoding = FILE_ENCODING_UTF16; + } + + hr = StrAllocString(psczString, reinterpret_cast(pbFullFileBuffer), cbFullFileBuffer / sizeof(WCHAR)); + FileExitOnFailure(hr, "Failed to allocate copy of string"); + } + } + +LExit: + ReleaseStr(sczFileText); + ReleaseMem(pbFullFileBuffer); + + return hr; +} + +/******************************************************************* + FileFromString + +*******************************************************************/ +extern "C" HRESULT DAPI FileFromString( + __in_z LPCWSTR wzFile, + __in DWORD dwFlagsAndAttributes, + __in_z LPCWSTR sczString, + __in FILE_ENCODING feEncoding + ) +{ + HRESULT hr = S_OK; + LPSTR sczUtf8String = NULL; + BYTE *pbFullFileBuffer = NULL; + const BYTE *pcbFullFileBuffer = NULL; + SIZE_T cbFullFileBuffer = 0; + SIZE_T cbStrLen = 0; + + switch (feEncoding) + { + case FILE_ENCODING_UTF8: + hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); + FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); + + hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast(&cbFullFileBuffer)); + FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string"); + + pcbFullFileBuffer = reinterpret_cast(sczUtf8String); + break; + case FILE_ENCODING_UTF8_WITH_BOM: + hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); + FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); + + hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); + FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string"); + + cbFullFileBuffer = sizeof(UTF8BOM) + cbStrLen; + + pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); + FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); + + memcpy_s(pbFullFileBuffer, sizeof(UTF8BOM), UTF8BOM, sizeof(UTF8BOM)); + memcpy_s(pbFullFileBuffer + sizeof(UTF8BOM), cbStrLen, sczUtf8String, cbStrLen); + pcbFullFileBuffer = pbFullFileBuffer; + break; + case FILE_ENCODING_UTF16: + hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); + FileExitOnRootFailure(hr, "Failed to get length of string"); + + cbFullFileBuffer = cbStrLen * sizeof(WCHAR); + pcbFullFileBuffer = reinterpret_cast(sczString); + break; + case FILE_ENCODING_UTF16_WITH_BOM: + hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast(&cbStrLen)); + FileExitOnRootFailure(hr, "Failed to get length of string"); + + cbStrLen *= sizeof(WCHAR); + cbFullFileBuffer = sizeof(UTF16BOM) + cbStrLen; + + pbFullFileBuffer = reinterpret_cast(MemAlloc(cbFullFileBuffer, TRUE)); + FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); + + memcpy_s(pbFullFileBuffer, sizeof(UTF16BOM), UTF16BOM, sizeof(UTF16BOM)); + memcpy_s(pbFullFileBuffer + sizeof(UTF16BOM), cbStrLen, sczString, cbStrLen); + pcbFullFileBuffer = pbFullFileBuffer; + break; + } + + hr = FileWrite(wzFile, dwFlagsAndAttributes, pcbFullFileBuffer, cbFullFileBuffer, NULL); + FileExitOnFailure(hr, "Failed to write file from string to: %ls", wzFile); + +LExit: + ReleaseStr(sczUtf8String); + ReleaseMem(pbFullFileBuffer); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/gdiputil.cpp b/src/libs/dutil/WixToolset.DUtil/gdiputil.cpp new file mode 100644 index 00000000..b5a0087c --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/gdiputil.cpp @@ -0,0 +1,227 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace Gdiplus; + + +// Exit macros +#define GdipExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__) +#define GdipExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GDIPUTIL, p, x, e, s, __VA_ARGS__) +#define GdipExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, p, x, s, __VA_ARGS__) +#define GdipExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, p, x, e, s, __VA_ARGS__) +#define GdipExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, p, x, s, __VA_ARGS__) +#define GdipExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_GDIPUTIL, e, x, s, __VA_ARGS__) +#define GdipExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_GDIPUTIL, g, x, s, __VA_ARGS__) + +/******************************************************************** + GdipInitialize - initializes GDI+. + + Note: pOutput must be non-NULL if pInput->SuppressBackgroundThread + is TRUE. See GdiplusStartup() for more information. +********************************************************************/ +extern "C" HRESULT DAPI GdipInitialize( + __in const Gdiplus::GdiplusStartupInput* pInput, + __out ULONG_PTR* pToken, + __out_opt Gdiplus::GdiplusStartupOutput *pOutput + ) +{ + AssertSz(!pInput->SuppressBackgroundThread || pOutput, "pOutput required if background thread suppressed."); + + HRESULT hr = S_OK; + Status status = Ok; + + status = GdiplusStartup(pToken, pInput, pOutput); + GdipExitOnGdipFailure(status, hr, "Failed to initialize GDI+."); + +LExit: + return hr; +} + +/******************************************************************** + GdipUninitialize - uninitializes GDI+. + +********************************************************************/ +extern "C" void DAPI GdipUninitialize( + __in ULONG_PTR token + ) +{ + GdiplusShutdown(token); +} + +/******************************************************************** + GdipBitmapFromResource - read a GDI+ image out of a resource stream + +********************************************************************/ +extern "C" HRESULT DAPI GdipBitmapFromResource( + __in_opt HINSTANCE hinst, + __in_z LPCSTR szId, + __out Bitmap **ppBitmap + ) +{ + HRESULT hr = S_OK; + LPVOID pvData = NULL; + DWORD cbData = 0; + HGLOBAL hGlobal = NULL;; + LPVOID pv = NULL; + IStream *pStream = NULL; + Bitmap *pBitmap = NULL; + Status gs = Ok; + + hr = ResReadData(hinst, szId, &pvData, &cbData); + GdipExitOnFailure(hr, "Failed to load GDI+ bitmap from resource."); + + // Have to copy the fixed resource data into moveable (heap) memory + // since that's what GDI+ expects. + hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, cbData); + GdipExitOnNullWithLastError(hGlobal, hr, "Failed to allocate global memory."); + + pv = ::GlobalLock(hGlobal); + GdipExitOnNullWithLastError(pv, hr, "Failed to lock global memory."); + + memcpy(pv, pvData, cbData); + + ::GlobalUnlock(pv); // no point taking any more memory than we have already + pv = NULL; + + hr = ::CreateStreamOnHGlobal(hGlobal, TRUE, &pStream); + GdipExitOnFailure(hr, "Failed to allocate stream from global memory."); + + hGlobal = NULL; // we gave the global memory to the stream object so it will close it + + pBitmap = Bitmap::FromStream(pStream); + GdipExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from stream."); + + gs = pBitmap->GetLastStatus(); + GdipExitOnGdipFailure(gs, hr, "Failed to load bitmap from stream."); + + *ppBitmap = pBitmap; + pBitmap = NULL; + +LExit: + if (pBitmap) + { + delete pBitmap; + } + + ReleaseObject(pStream); + + if (pv) + { + ::GlobalUnlock(pv); + } + + if (hGlobal) + { + ::GlobalFree(hGlobal); + } + + return hr; +} + + +/******************************************************************** + GdipBitmapFromFile - read a GDI+ image from a file. + +********************************************************************/ +extern "C" HRESULT DAPI GdipBitmapFromFile( + __in_z LPCWSTR wzFileName, + __out Bitmap **ppBitmap + ) +{ + HRESULT hr = S_OK; + Bitmap *pBitmap = NULL; + Status gs = Ok; + + GdipExitOnNull(ppBitmap, hr, E_INVALIDARG, "Invalid null wzFileName"); + + pBitmap = Bitmap::FromFile(wzFileName); + GdipExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from file."); + + gs = pBitmap->GetLastStatus(); + GdipExitOnGdipFailure(gs, hr, "Failed to load bitmap from file: %ls", wzFileName); + + *ppBitmap = pBitmap; + pBitmap = NULL; + +LExit: + if (pBitmap) + { + delete pBitmap; + } + + return hr; +} + + +HRESULT DAPI GdipHresultFromStatus( + __in Gdiplus::Status gs + ) +{ + switch (gs) + { + case Ok: + return S_OK; + + case GenericError: + return E_FAIL; + + case InvalidParameter: + return E_INVALIDARG; + + case OutOfMemory: + return E_OUTOFMEMORY; + + case ObjectBusy: + return HRESULT_FROM_WIN32(ERROR_BUSY); + + case InsufficientBuffer: + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + + case NotImplemented: + return E_NOTIMPL; + + case Win32Error: + return E_FAIL; + + case WrongState: + return E_FAIL; + + case Aborted: + return E_ABORT; + + case FileNotFound: + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + case ValueOverflow: + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + case AccessDenied: + return E_ACCESSDENIED; + + case UnknownImageFormat: + return HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); + + case FontFamilyNotFound: __fallthrough; + case FontStyleNotFound: __fallthrough; + case NotTrueTypeFont: + return E_UNEXPECTED; + + case UnsupportedGdiplusVersion: + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + case GdiplusNotInitialized: + return E_UNEXPECTED; + + case PropertyNotFound: __fallthrough; + case PropertyNotSupported: + return E_FAIL; + } + + return E_UNEXPECTED; +} diff --git a/src/libs/dutil/WixToolset.DUtil/guidutil.cpp b/src/libs/dutil/WixToolset.DUtil/guidutil.cpp new file mode 100644 index 00000000..204c9af2 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/guidutil.cpp @@ -0,0 +1,54 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define GuidExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__) +#define GuidExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__) +#define GuidExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__) +#define GuidExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__) +#define GuidExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__) +#define GuidExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_GUIDUTIL, e, x, s, __VA_ARGS__) +#define GuidExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_GUIDUTIL, g, x, s, __VA_ARGS__) + +extern "C" HRESULT DAPI GuidFixedCreate( + _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid + ) +{ + HRESULT hr = S_OK; + UUID guid = { }; + + hr = HRESULT_FROM_RPC(::UuidCreate(&guid)); + GuidExitOnFailure(hr, "UuidCreate failed."); + + if (!::StringFromGUID2(guid, wzGuid, GUID_STRING_LENGTH)) + { + hr = E_OUTOFMEMORY; + GuidExitOnRootFailure(hr, "Failed to convert guid into string."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI GuidCreate( + __deref_out_z LPWSTR* psczGuid + ) +{ + HRESULT hr = S_OK; + + hr = StrAlloc(psczGuid, GUID_STRING_LENGTH); + GuidExitOnFailure(hr, "Failed to allocate space for guid"); + + hr = GuidFixedCreate(*psczGuid); + GuidExitOnFailure(hr, "Failed to create new guid."); + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/iis7util.cpp b/src/libs/dutil/WixToolset.DUtil/iis7util.cpp new file mode 100644 index 00000000..d0a0b000 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/iis7util.cpp @@ -0,0 +1,535 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#include "iis7util.h" + + +// Exit macros +#define IisExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__) +#define IisExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_IIS7UTIL, p, x, e, s, __VA_ARGS__) +#define IisExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, p, x, s, __VA_ARGS__) +#define IisExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, p, x, e, s, __VA_ARGS__) +#define IisExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, p, x, s, __VA_ARGS__) +#define IisExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_IIS7UTIL, e, x, s, __VA_ARGS__) +#define IisExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_IIS7UTIL, g, x, s, __VA_ARGS__) + +#define ISSTRINGVARIANT(vt) (VT_BSTR == vt || VT_LPWSTR == vt) + +extern "C" HRESULT DAPI Iis7PutPropertyVariant( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in VARIANT vtPut + ) +{ + HRESULT hr = S_OK; + IAppHostProperty *pProperty = NULL; + BSTR bstrPropName = NULL; + + bstrPropName = ::SysAllocString(wzPropName); + IisExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = pElement->GetPropertyByName(bstrPropName, &pProperty); + IisExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); + + hr = pProperty->put_Value(vtPut); + IisExitOnFailure(hr, "Failed to set property value for %ls", wzPropName); + +LExit: + ReleaseBSTR(bstrPropName); + // caller responsible for cleaning up variant vtPut + ReleaseObject(pProperty); + + return hr; +} + +extern "C" HRESULT DAPI Iis7PutPropertyString( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in LPCWSTR wzString + ) +{ + HRESULT hr = S_OK; + VARIANT vtPut; + + ::VariantInit(&vtPut); + vtPut.vt = VT_BSTR; + vtPut.bstrVal = ::SysAllocString(wzString); + IisExitOnNull(vtPut.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = Iis7PutPropertyVariant(pElement, wzPropName, vtPut); + +LExit: + ReleaseVariant(vtPut); + + return hr; +} + +extern "C" HRESULT DAPI Iis7PutPropertyInteger( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in DWORD dValue + ) +{ + VARIANT vtPut; + + ::VariantInit(&vtPut); + vtPut.vt = VT_I4; + vtPut.lVal = dValue; + return Iis7PutPropertyVariant(pElement, wzPropName, vtPut); +} + +extern "C" HRESULT DAPI Iis7PutPropertyBool( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in BOOL fValue) +{ + VARIANT vtPut; + + ::VariantInit(&vtPut); + vtPut.vt = VT_BOOL; + vtPut.boolVal = (fValue == FALSE) ? VARIANT_FALSE : VARIANT_TRUE; + return Iis7PutPropertyVariant(pElement, wzPropName, vtPut); +} + +extern "C" HRESULT DAPI Iis7GetPropertyVariant( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in VARIANT* vtGet + ) +{ + HRESULT hr = S_OK; + IAppHostProperty *pProperty = NULL; + BSTR bstrPropName = NULL; + + bstrPropName = ::SysAllocString(wzPropName); + IisExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = pElement->GetPropertyByName(bstrPropName, &pProperty); + IisExitOnFailure(hr, "Failed to get property object for %ls", wzPropName); + + hr = pProperty->get_Value(vtGet); + IisExitOnFailure(hr, "Failed to get property value for %ls", wzPropName); + +LExit: + ReleaseBSTR(bstrPropName); + // caller responsible for cleaning up variant vtGet + ReleaseObject(pProperty); + + return hr; +} + +extern "C" HRESULT DAPI Iis7GetPropertyString( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in LPWSTR* psczGet + ) +{ + HRESULT hr = S_OK; + VARIANT vtGet; + + ::VariantInit(&vtGet); + hr = Iis7GetPropertyVariant(pElement, wzPropName, &vtGet); + IisExitOnFailure(hr, "Failed to get iis7 property variant with name: %ls", wzPropName); + + if (!ISSTRINGVARIANT(vtGet.vt)) + { + hr = E_UNEXPECTED; + IisExitOnFailure(hr, "Tried to get property as a string, but type was %d instead.", vtGet.vt); + } + + hr = StrAllocString(psczGet, vtGet.bstrVal, 0); + +LExit: + ReleaseVariant(vtGet); + + return hr; +} + +BOOL DAPI CompareVariantDefault( + __in VARIANT* pVariant1, + __in VARIANT* pVariant2 + ) +{ + BOOL fEqual = FALSE; + + switch (pVariant1->vt) + { + // VarCmp doesn't work for unsigned ints + // We'd like to allow signed/unsigned comparison as well since + // IIS doesn't document variant type for integer fields + case VT_I1: + case VT_UI1: + if (VT_I1 == pVariant2->vt || VT_UI1 == pVariant2->vt) + { + fEqual = pVariant1->bVal == pVariant2->bVal; + } + break; + case VT_I2: + case VT_UI2: + if (VT_I2 == pVariant2->vt || VT_UI2 == pVariant2->vt) + { + fEqual = pVariant1->uiVal == pVariant2->uiVal; + } + break; + case VT_UI4: + case VT_I4: + if (VT_I4 == pVariant2->vt || VT_UI4 == pVariant2->vt) + { + fEqual = pVariant1->ulVal == pVariant2->ulVal; + } + break; + case VT_UI8: + case VT_I8: + if (VT_I8 == pVariant2->vt || VT_UI8 == pVariant2->vt) + { + fEqual = pVariant1->ullVal == pVariant2->ullVal; + } + break; + default: + fEqual = VARCMP_EQ == ::VarCmp(pVariant1, + pVariant2, + LOCALE_INVARIANT, + NORM_IGNORECASE); + } + + return fEqual; +} + +BOOL DAPI CompareVariantPath( + __in VARIANT* pVariant1, + __in VARIANT* pVariant2 + ) +{ + HRESULT hr = S_OK; + BOOL fEqual = FALSE; + LPWSTR wzValue1 = NULL; + LPWSTR wzValue2 = NULL; + + if (ISSTRINGVARIANT(pVariant1->vt)) + { + hr = PathExpand(&wzValue1, pVariant1->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + IisExitOnFailure(hr, "Failed to expand path %ls", pVariant1->bstrVal); + } + + if (ISSTRINGVARIANT(pVariant2->vt)) + { + hr = PathExpand(&wzValue2, pVariant2->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + IisExitOnFailure(hr, "Failed to expand path %ls", pVariant2->bstrVal); + } + + fEqual = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzValue1, -1, wzValue2, -1); + +LExit: + ReleaseNullStr(wzValue1); + ReleaseNullStr(wzValue2); + return fEqual; +} + +BOOL DAPI IsMatchingAppHostElementCallback( + __in IAppHostElement *pElement, + __in_bcount(sizeof(IIS7_APPHOSTELEMENTCOMPARISON)) LPVOID pContext + ) +{ + IIS7_APPHOSTELEMENTCOMPARISON* pComparison = (IIS7_APPHOSTELEMENTCOMPARISON*) pContext; + + return Iis7IsMatchingAppHostElement(pElement, pComparison); +} + +extern "C" BOOL DAPI Iis7IsMatchingAppHostElement( + __in IAppHostElement *pElement, + __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison + ) +{ + HRESULT hr = S_OK; + BOOL fResult = FALSE; + IAppHostProperty *pProperty = NULL; + BSTR bstrElementName = NULL; + + VARIANT vPropValue; + ::VariantInit(&vPropValue); + + // Use the default comparator if a comparator is not specified + VARIANTCOMPARATORPROC pComparator = pComparison->pComparator ? pComparison->pComparator : CompareVariantDefault; + + hr = pElement->get_Name(&bstrElementName); + IisExitOnFailure(hr, "Failed to get name of element"); + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pComparison->sczElementName, -1, bstrElementName, -1)) + { + ExitFunction(); + } + + hr = Iis7GetPropertyVariant(pElement, pComparison->sczAttributeName, &vPropValue); + IisExitOnFailure(hr, "Failed to get value of %ls attribute of %ls element", pComparison->sczAttributeName, pComparison->sczElementName); + + if (TRUE == pComparator(pComparison->pvAttributeValue, &vPropValue)) + { + fResult = TRUE; + } + +LExit: + ReleaseBSTR(bstrElementName); + ReleaseVariant(vPropValue); + ReleaseObject(pProperty); + + return fResult; +} + +BOOL DAPI IsMatchingAppHostMethod( + __in IAppHostMethod *pMethod, + __in LPCWSTR wzMethodName + ) +{ + HRESULT hr = S_OK; + BOOL fResult = FALSE; + BSTR bstrName = NULL; + + hr = pMethod->get_Name(&bstrName); + IisExitOnFailure(hr, "Failed to get name of element"); + + Assert(bstrName); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzMethodName, -1, bstrName, -1)) + { + fResult = TRUE; + } + +LExit: + ReleaseBSTR(bstrName); + + return fResult; +} + +extern "C" HRESULT DAPI Iis7FindAppHostElementPath( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in LPCWSTR wzAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + IIS7_APPHOSTELEMENTCOMPARISON comparison = { }; + VARIANT vtValue = { }; + ::VariantInit(&vtValue); + + vtValue.vt = VT_BSTR; + vtValue.bstrVal = ::SysAllocString(wzAttributeValue); + IisExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + comparison.sczElementName = wzElementName; + comparison.sczAttributeName = wzAttributeName; + comparison.pvAttributeValue = &vtValue; + comparison.pComparator = CompareVariantPath; + + hr = Iis7EnumAppHostElements(pCollection, + IsMatchingAppHostElementCallback, + &comparison, + ppElement, + pdwIndex); + +LExit: + ReleaseVariant(vtValue); + + return hr; +} + +extern "C" HRESULT DAPI Iis7FindAppHostElementString( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in LPCWSTR wzAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + VARIANT vtValue; + ::VariantInit(&vtValue); + + vtValue.vt = VT_BSTR; + vtValue.bstrVal = ::SysAllocString(wzAttributeValue); + IisExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = Iis7FindAppHostElementVariant(pCollection, + wzElementName, + wzAttributeName, + &vtValue, + ppElement, + pdwIndex); + +LExit: + ReleaseVariant(vtValue); + + return hr; +} + +extern "C" HRESULT DAPI Iis7FindAppHostElementInteger( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in DWORD dwAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + VARIANT vtValue; + ::VariantInit(&vtValue); + + vtValue.vt = VT_UI4; + vtValue.ulVal = dwAttributeValue; + + hr = Iis7FindAppHostElementVariant(pCollection, + wzElementName, + wzAttributeName, + &vtValue, + ppElement, + pdwIndex); + + ReleaseVariant(vtValue); + + return hr; +} + +extern "C" HRESULT DAPI Iis7FindAppHostElementVariant( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in VARIANT* pvAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + IIS7_APPHOSTELEMENTCOMPARISON comparison = { }; + comparison.sczElementName = wzElementName; + comparison.sczAttributeName = wzAttributeName; + comparison.pvAttributeValue = pvAttributeValue; + comparison.pComparator = CompareVariantDefault; + + return Iis7EnumAppHostElements(pCollection, + IsMatchingAppHostElementCallback, + &comparison, + ppElement, + pdwIndex); +} + +extern "C" HRESULT DAPI Iis7EnumAppHostElements( + __in IAppHostElementCollection *pCollection, + __in ENUMAPHOSTELEMENTPROC pCallback, + __in LPVOID pContext, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + IAppHostElement *pElement = NULL; + DWORD dwElements = 0; + + VARIANT vtIndex; + ::VariantInit(&vtIndex); + + if (NULL != ppElement) + { + *ppElement = NULL; + } + if (NULL != pdwIndex) + { + *pdwIndex = MAXDWORD; + } + + hr = pCollection->get_Count(&dwElements); + IisExitOnFailure(hr, "Failed get application IAppHostElementCollection count"); + + vtIndex.vt = VT_UI4; + for (DWORD i = 0; i < dwElements; ++i) + { + vtIndex.ulVal = i; + hr = pCollection->get_Item(vtIndex , &pElement); + IisExitOnFailure(hr, "Failed get IAppHostElement element"); + + if (pCallback(pElement, pContext)) + { + if (NULL != ppElement) + { + *ppElement = pElement; + pElement = NULL; + } + if (NULL != pdwIndex) + { + *pdwIndex = i; + } + break; + } + + ReleaseNullObject(pElement); + } + +LExit: + ReleaseObject(pElement); + ReleaseVariant(vtIndex); + + return hr; +} + +extern "C" HRESULT DAPI Iis7FindAppHostMethod( + __in IAppHostMethodCollection *pCollection, + __in LPCWSTR wzMethodName, + __out IAppHostMethod** ppMethod, + __out DWORD* pdwIndex + ) +{ + HRESULT hr = S_OK; + IAppHostMethod *pMethod = NULL; + DWORD dwMethods = 0; + + VARIANT vtIndex; + ::VariantInit(&vtIndex); + + if (NULL != ppMethod) + { + *ppMethod = NULL; + } + if (NULL != pdwIndex) + { + *pdwIndex = MAXDWORD; + } + + hr = pCollection->get_Count(&dwMethods); + IisExitOnFailure(hr, "Failed get application IAppHostMethodCollection count"); + + vtIndex.vt = VT_UI4; + for (DWORD i = 0; i < dwMethods; ++i) + { + vtIndex.ulVal = i; + hr = pCollection->get_Item(vtIndex , &pMethod); + IisExitOnFailure(hr, "Failed get IAppHostMethod element"); + + if (IsMatchingAppHostMethod(pMethod, wzMethodName)) + { + if (NULL != ppMethod) + { + *ppMethod = pMethod; + pMethod = NULL; + } + if (NULL != pdwIndex) + { + *pdwIndex = i; + } + break; + } + + ReleaseNullObject(pMethod); + } + +LExit: + ReleaseObject(pMethod); + ReleaseVariant(vtIndex); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/inc/aclutil.h b/src/libs/dutil/WixToolset.DUtil/inc/aclutil.h new file mode 100644 index 00000000..ac03f9a8 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/aclutil.h @@ -0,0 +1,154 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include + +#define ReleaseSid(x) if (x) { AclFreeSid(x); } +#define ReleaseNullSid(x) if (x) { AclFreeSid(x); x = NULL; } + +#ifdef __cplusplus +extern "C" { +#endif + +// structs +struct ACL_ACCESS +{ + BOOL fDenyAccess; + DWORD dwAccessMask; + + // TODO: consider using a union + LPCWSTR pwzAccountName; // NOTE: the last three items in this structure are ignored if this is not NULL + + SID_IDENTIFIER_AUTHORITY sia; // used if pwzAccountName is NULL + BYTE nSubAuthorityCount; + DWORD nSubAuthority[8]; +}; + +struct ACL_ACE +{ + DWORD dwFlags; + DWORD dwMask; + PSID psid; +}; + + +// functions +HRESULT DAPI AclCheckAccess( + __in HANDLE hToken, + __in ACL_ACCESS* paa + ); +HRESULT DAPI AclCheckAdministratorAccess( + __in HANDLE hToken + ); +HRESULT DAPI AclCheckLocalSystemAccess( + __in HANDLE hToken + ); + +HRESULT DAPI AclGetWellKnownSid( + __in WELL_KNOWN_SID_TYPE wkst, + __deref_out PSID* ppsid + ); +HRESULT DAPI AclGetAccountSid( + __in_opt LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out PSID* ppsid + ); +HRESULT DAPI AclGetAccountSidString( + __in_z LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out_z LPWSTR* ppwzSid + ); + +HRESULT DAPI AclCreateDacl( + __in_ecount(cDeny) ACL_ACE rgaaDeny[], + __in DWORD cDeny, + __in_ecount(cAllow) ACL_ACE rgaaAllow[], + __in DWORD cAllow, + __deref_out ACL** ppAcl + ); +HRESULT DAPI AclAddToDacl( + __in ACL* pAcl, + __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[], + __in DWORD cDeny, + __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[], + __in DWORD cAllow, + __deref_out ACL** ppAclNew + ); +HRESULT DAPI AclMergeDacls( + __in const ACL* pAcl1, + __in const ACL* pAcl2, + __deref_out ACL** ppAclNew + ); +HRESULT DAPI AclCreateDaclOld( + __in_ecount(cAclAccesses) ACL_ACCESS* paa, + __in DWORD cAclAccesses, + __deref_out ACL** ppAcl + ); +HRESULT DAPI AclCreateSecurityDescriptor( + __in_ecount(cAclAccesses) ACL_ACCESS* paa, + __in DWORD cAclAccesses, + __deref_out SECURITY_DESCRIPTOR** ppsd + ); +HRESULT DAPI AclCreateSecurityDescriptorFromDacl( + __in ACL* pACL, + __deref_out SECURITY_DESCRIPTOR** ppsd + ); +HRESULT __cdecl AclCreateSecurityDescriptorFromString( + __deref_out SECURITY_DESCRIPTOR** ppsd, + __in_z __format_string LPCWSTR wzSddlFormat, + ... + ); +HRESULT DAPI AclDuplicateSecurityDescriptor( + __in SECURITY_DESCRIPTOR* psd, + __deref_out SECURITY_DESCRIPTOR** ppsd + ); +HRESULT DAPI AclGetSecurityDescriptor( + __in_z LPCWSTR wzObject, + __in SE_OBJECT_TYPE sot, + __in SECURITY_INFORMATION securityInformation, + __deref_out SECURITY_DESCRIPTOR** ppsd + ); +HRESULT DAPI AclSetSecurityWithRetry( + __in_z LPCWSTR wzObject, + __in SE_OBJECT_TYPE sot, + __in SECURITY_INFORMATION securityInformation, + __in_opt PSID psidOwner, + __in_opt PSID psidGroup, + __in_opt PACL pDacl, + __in_opt PACL pSacl, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ); + +HRESULT DAPI AclFreeSid( + __in PSID psid + ); +HRESULT DAPI AclFreeDacl( + __in ACL* pACL + ); +HRESULT DAPI AclFreeSecurityDescriptor( + __in SECURITY_DESCRIPTOR* psd + ); + +HRESULT DAPI AclAddAdminToSecurityDescriptor( + __in SECURITY_DESCRIPTOR* pSecurity, + __deref_out SECURITY_DESCRIPTOR** ppSecurityNew + ); + +// Following code in acl2util.cpp due to dependency on crypt32.dll. +HRESULT DAPI AclCalculateServiceSidString( + __in LPCWSTR wzServiceName, + __in SIZE_T cchServiceName, + __deref_out_z LPWSTR* psczSid + ); +HRESULT DAPI AclGetAccountSidStringEx( + __in_z LPCWSTR wzSystem, + __in_z LPCWSTR wzAccount, + __deref_out_z LPWSTR* psczSid + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/apputil.h b/src/libs/dutil/WixToolset.DUtil/inc/apputil.h new file mode 100644 index 00000000..1a1e14f7 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/apputil.h @@ -0,0 +1,45 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// functions + +/******************************************************************** +AppFreeCommandLineArgs - frees argv from AppParseCommandLine. + +********************************************************************/ +void DAPI AppFreeCommandLineArgs( + __in LPWSTR* argv + ); + +void DAPI AppInitialize( + __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[], + __in DWORD cSafelyLoadSystemDlls + ); + +/******************************************************************** +AppInitializeUnsafe - initializes without the full standard safety + precautions for an application. + +********************************************************************/ +void DAPI AppInitializeUnsafe(); + +/******************************************************************** +AppParseCommandLine - parses the command line using CommandLineToArgvW. + The caller must free the value of pArgv on success + by calling AppFreeCommandLineArgs. + +********************************************************************/ +DAPI_(HRESULT) AppParseCommandLine( + __in LPCWSTR wzCommandLine, + __in int* argc, + __in LPWSTR** pArgv + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/apuputil.h b/src/libs/dutil/WixToolset.DUtil/inc/apuputil.h new file mode 100644 index 00000000..f26a12b7 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/apuputil.h @@ -0,0 +1,87 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseApupChain(p) if (p) { ApupFreeChain(p); p = NULL; } +#define ReleaseNullApupChain(p) if (p) { ApupFreeChain(p); p = NULL; } + + +const LPCWSTR APPLICATION_SYNDICATION_NAMESPACE = L"http://appsyndication.org/2006/appsyn"; + +typedef enum APUP_HASH_ALGORITHM +{ + APUP_HASH_ALGORITHM_UNKNOWN, + APUP_HASH_ALGORITHM_MD5, + APUP_HASH_ALGORITHM_SHA1, + APUP_HASH_ALGORITHM_SHA256, + APUP_HASH_ALGORITHM_SHA512, +} APUP_HASH_ALGORITHM; + + +struct APPLICATION_UPDATE_ENCLOSURE +{ + LPWSTR wzUrl; + LPWSTR wzLocalName; + DWORD64 dw64Size; + + BYTE* rgbDigest; + DWORD cbDigest; + APUP_HASH_ALGORITHM digestAlgorithm; + + BOOL fInstaller; +}; + + +struct APPLICATION_UPDATE_ENTRY +{ + LPWSTR wzApplicationId; + LPWSTR wzApplicationType; + LPWSTR wzTitle; + LPWSTR wzSummary; + LPWSTR wzContentType; + LPWSTR wzContent; + + LPWSTR wzUpgradeId; + BOOL fUpgradeExclusive; + VERUTIL_VERSION* pVersion; + VERUTIL_VERSION* pUpgradeVersion; + + DWORD64 dw64TotalSize; + + DWORD cEnclosures; + APPLICATION_UPDATE_ENCLOSURE* rgEnclosures; +}; + + +struct APPLICATION_UPDATE_CHAIN +{ + LPWSTR wzDefaultApplicationId; + LPWSTR wzDefaultApplicationType; + + DWORD cEntries; + APPLICATION_UPDATE_ENTRY* rgEntries; +}; + + +HRESULT DAPI ApupAllocChainFromAtom( + __in ATOM_FEED* pFeed, + __out APPLICATION_UPDATE_CHAIN** ppChain + ); + +HRESULT DAPI ApupFilterChain( + __in APPLICATION_UPDATE_CHAIN* pChain, + __in VERUTIL_VERSION* pVersion, + __out APPLICATION_UPDATE_CHAIN** ppFilteredChain + ); + +void DAPI ApupFreeChain( + __in APPLICATION_UPDATE_CHAIN* pChain + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/atomutil.h b/src/libs/dutil/WixToolset.DUtil/inc/atomutil.h new file mode 100644 index 00000000..9acfc1d5 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/atomutil.h @@ -0,0 +1,146 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseAtomFeed(p) if (p) { AtomFreeFeed(p); } +#define ReleaseNullAtomFeed(p) if (p) { AtomFreeFeed(p); p = NULL; } + + +struct ATOM_UNKNOWN_ATTRIBUTE +{ + LPWSTR wzNamespace; + LPWSTR wzAttribute; + LPWSTR wzValue; + + ATOM_UNKNOWN_ATTRIBUTE* pNext; +}; + +struct ATOM_UNKNOWN_ELEMENT +{ + LPWSTR wzNamespace; + LPWSTR wzElement; + LPWSTR wzValue; + + ATOM_UNKNOWN_ATTRIBUTE* pAttributes; + ATOM_UNKNOWN_ELEMENT* pNext; +}; + +struct ATOM_LINK +{ + LPWSTR wzRel; + LPWSTR wzTitle; + LPWSTR wzType; + LPWSTR wzUrl; + LPWSTR wzValue; + DWORD64 dw64Length; + + ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttributes; + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct ATOM_CONTENT +{ + LPWSTR wzType; + LPWSTR wzUrl; + LPWSTR wzValue; + + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct ATOM_AUTHOR +{ + LPWSTR wzName; + LPWSTR wzEmail; + LPWSTR wzUrl; +}; + +struct ATOM_CATEGORY +{ + LPWSTR wzLabel; + LPWSTR wzScheme; + LPWSTR wzTerm; + + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct ATOM_ENTRY +{ + LPWSTR wzId; + LPWSTR wzSummary; + LPWSTR wzTitle; + FILETIME ftPublished; + FILETIME ftUpdated; + + ATOM_CONTENT* pContent; + + DWORD cAuthors; + ATOM_AUTHOR* rgAuthors; + + DWORD cCategories; + ATOM_CATEGORY* rgCategories; + + DWORD cLinks; + ATOM_LINK* rgLinks; + + IXMLDOMNode* pixn; + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct ATOM_FEED +{ + LPWSTR wzGenerator; + LPWSTR wzIcon; + LPWSTR wzId; + LPWSTR wzLogo; + LPWSTR wzSubtitle; + LPWSTR wzTitle; + FILETIME ftUpdated; + + DWORD cAuthors; + ATOM_AUTHOR* rgAuthors; + + DWORD cCategories; + ATOM_CATEGORY* rgCategories; + + DWORD cEntries; + ATOM_ENTRY* rgEntries; + + DWORD cLinks; + ATOM_LINK* rgLinks; + + IXMLDOMNode* pixn; + ATOM_UNKNOWN_ELEMENT* pUnknownElements; +}; + +HRESULT DAPI AtomInitialize( + ); + +void DAPI AtomUninitialize( + ); + +HRESULT DAPI AtomParseFromString( + __in_z LPCWSTR wzAtomString, + __out ATOM_FEED **ppFeed + ); + +HRESULT DAPI AtomParseFromFile( + __in_z LPCWSTR wzAtomFile, + __out ATOM_FEED **ppFeed + ); + +HRESULT DAPI AtomParseFromDocument( + __in IXMLDOMDocument* pixdDocument, + __out ATOM_FEED **ppFeed + ); + +void DAPI AtomFreeFeed( + __in_xcount(pFeed->cItems) ATOM_FEED* pFeed + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/buffutil.h b/src/libs/dutil/WixToolset.DUtil/inc/buffutil.h new file mode 100644 index 00000000..322209e6 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/buffutil.h @@ -0,0 +1,91 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +// macro definitions + +#define ReleaseBuffer ReleaseMem +#define ReleaseNullBuffer ReleaseNullMem +#define BuffFree MemFree + + +// function declarations + +HRESULT BuffReadNumber( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD* pdw + ); +HRESULT BuffReadNumber64( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD64* pdw64 + ); +HRESULT BuffReadPointer( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __out DWORD_PTR* pdw +); +HRESULT BuffReadString( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_z LPWSTR* pscz + ); +HRESULT BuffReadStringAnsi( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_out_z LPSTR* pscz + ); +HRESULT BuffReadStream( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer, + __deref_inout_bcount(*pcbStream) BYTE** ppbStream, + __out SIZE_T* pcbStream + ); + +HRESULT BuffWriteNumber( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD dw + ); +HRESULT BuffWriteNumber64( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD64 dw64 + ); +HRESULT BuffWritePointer( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in DWORD_PTR dw +); +HRESULT BuffWriteString( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_z_opt LPCWSTR scz + ); +HRESULT BuffWriteStringAnsi( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_z_opt LPCSTR scz + ); +HRESULT BuffWriteStream( + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer, + __in_bcount(cbStream) const BYTE* pbStream, + __in SIZE_T cbStream + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/butil.h b/src/libs/dutil/WixToolset.DUtil/inc/butil.h new file mode 100644 index 00000000..d1ec73bc --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/butil.h @@ -0,0 +1,60 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +enum BUNDLE_INSTALL_CONTEXT +{ + BUNDLE_INSTALL_CONTEXT_MACHINE, + BUNDLE_INSTALL_CONTEXT_USER, +}; + + +/******************************************************************** +BundleGetBundleInfo - Queries the bundle installation metadata for a given property + +RETURNS: + E_INVALIDARG + An invalid parameter was passed to the function. + HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) + The bundle is not installed + HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) + The property is unrecognized + HRESULT_FROM_WIN32(ERROR_MORE_DATA) + A buffer is too small to hold the requested data. + E_NOTIMPL: + Tried to read a bundle attribute for a type which has not been implemented + + All other returns are unexpected returns from other dutil methods. +********************************************************************/ +HRESULT DAPI BundleGetBundleInfo( + __in_z LPCWSTR szBundleId, // Bundle code + __in_z LPCWSTR szAttribute, // attribute name + __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, // returned value, NULL if not desired + __inout_opt LPDWORD pcchValueBuf // in/out buffer character count + ); + +/******************************************************************** +BundleEnumRelatedBundle - Queries the bundle installation metadata for installs with the given upgrade code + +NOTE: lpBundleIdBuff is a buffer to receive the bundle GUID. This buffer must be 39 characters long. + The first 38 characters are for the GUID, and the last character is for the terminating null character. +RETURNS: + E_INVALIDARG + An invalid parameter was passed to the function. + + All other returns are unexpected returns from other dutil methods. +********************************************************************/ +HRESULT DAPI BundleEnumRelatedBundle( + __in_z LPCWSTR lpUpgradeCode, + __in BUNDLE_INSTALL_CONTEXT context, + __inout PDWORD pdwStartIndex, + __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h b/src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h new file mode 100644 index 00000000..4f0c7b13 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h @@ -0,0 +1,62 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include + +// Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method +// First argument is the name of splitting cabinet without extension e.g. "cab1" +// Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab" +// Third argument is the file token of the first file present in the splitting cabinet +typedef void (__stdcall * FileSplitCabNamesCallback)(LPWSTR, LPWSTR, LPWSTR); + +#define CAB_MAX_SIZE 0x7FFFFFFF // (see KB: Q174866) + +#ifdef __cplusplus +extern "C" { +#endif + +extern const int CABC_HANDLE_BYTES; + +// time vs. space trade-off +typedef enum COMPRESSION_TYPE +{ + COMPRESSION_TYPE_NONE, // fastest + COMPRESSION_TYPE_LOW, + COMPRESSION_TYPE_MEDIUM, + COMPRESSION_TYPE_HIGH, // smallest + COMPRESSION_TYPE_MSZIP +} COMPRESSION_TYPE; + +// functions +HRESULT DAPI CabCBegin( + __in_z LPCWSTR wzCab, + __in_z LPCWSTR wzCabDir, + __in DWORD dwMaxFiles, + __in DWORD dwMaxSize, + __in DWORD dwMaxThresh, + __in COMPRESSION_TYPE ct, + __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext + ); +HRESULT DAPI CabCNextCab( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ); +HRESULT DAPI CabCAddFile( + __in_z LPCWSTR wzFile, + __in_z_opt LPCWSTR wzToken, + __in_opt PMSIFILEHASHINFO pmfHash, + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ); +HRESULT DAPI CabCFinish( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext, + __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback + ); +void DAPI CabCCancel( + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/cabutil.h b/src/libs/dutil/WixToolset.DUtil/inc/cabutil.h new file mode 100644 index 00000000..0bedba80 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/cabutil.h @@ -0,0 +1,56 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// structs + + +// callback function prototypes +typedef HRESULT (*CAB_CALLBACK_OPEN_FILE)(LPCWSTR wzFile, INT_PTR* ppFile); +typedef HRESULT (*CAB_CALLBACK_READ_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead); +typedef HRESULT (*CAB_CALLBACK_WRITE_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead); +typedef LONG (*CAB_CALLBACK_SEEK_FILE)(INT_PTR pFile, DWORD dwMove, DWORD dwMoveMethod); +typedef HRESULT (*CAB_CALLBACK_CLOSE_FILE)(INT_PTR pFile); + +typedef HRESULT (*CAB_CALLBACK_BEGIN_FILE)(LPCWSTR wzFileId, FILETIME* pftFileTime, DWORD cbFileSize, LPVOID pvContext, INT_PTR* ppFile); +typedef HRESULT (*CAB_CALLBACK_END_FILE)(LPCWSTR wzFileId, LPVOID pvContext, INT_PTR pFile); +typedef HRESULT (*CAB_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext); + +// function type with calling convention of __stdcall that .NET 1.1 understands only +// .NET 2.0 will not need this +typedef INT_PTR (FAR __stdcall *STDCALL_PFNFDINOTIFY)(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin); + + +// functions +HRESULT DAPI CabInitialize( + __in BOOL fDelayLoad + ); +void DAPI CabUninitialize( + ); + +HRESULT DAPI CabExtract( + __in_z LPCWSTR wzCabinet, + __in_z LPCWSTR wzExtractFile, + __in_z LPCWSTR wzExtractDir, + __in_opt CAB_CALLBACK_PROGRESS pfnProgress, + __in_opt LPVOID pvContext, + __in DWORD64 dw64EmbeddedOffset + ); + +HRESULT DAPI CabEnumerate( + __in_z LPCWSTR wzCabinet, + __in_z LPCWSTR wzEnumerateFile, + __in STDCALL_PFNFDINOTIFY pfnNotify, + __in DWORD64 dw64EmbeddedOffset + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/certutil.h b/src/libs/dutil/WixToolset.DUtil/inc/certutil.h new file mode 100644 index 00000000..8565c1cf --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/certutil.h @@ -0,0 +1,66 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define ReleaseCertStore(p) if (p) { ::CertCloseStore(p, 0); p = NULL; } +#define ReleaseCertContext(p) if (p) { ::CertFreeCertificateContext(p); p = NULL; } +#define ReleaseCertChain(p) if (p) { ::CertFreeCertificateChain(p); p = NULL; } + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI CertReadProperty( + __in PCCERT_CONTEXT pCertContext, + __in DWORD dwProperty, + __out_bcount(*pcbValue) LPVOID pvValue, + __out_opt DWORD* pcbValue + ); + +HRESULT DAPI CertGetAuthenticodeSigningTimestamp( + __in CMSG_SIGNER_INFO* pSignerInfo, + __out FILETIME* pft + ); + +HRESULT DAPI GetCryptProvFromCert( + __in_opt HWND hwnd, + __in PCCERT_CONTEXT pCert, + __out HCRYPTPROV *phCryptProv, + __out DWORD *pdwKeySpec, + __in BOOL *pfDidCryptAcquire, + __deref_opt_out LPWSTR *ppwszTmpContainer, + __deref_opt_out LPWSTR *ppwszProviderName, + __out DWORD *pdwProviderType + ); + +HRESULT DAPI FreeCryptProvFromCert( + __in BOOL fAcquired, + __in HCRYPTPROV hProv, + __in_opt LPWSTR pwszCapiProvider, + __in DWORD dwProviderType, + __in_opt LPWSTR pwszTmpContainer + ); + +HRESULT DAPI GetProvSecurityDesc( + __in HCRYPTPROV hProv, + __deref_out SECURITY_DESCRIPTOR** pSecurity + ); + +HRESULT DAPI SetProvSecurityDesc( + __in HCRYPTPROV hProv, + __in SECURITY_DESCRIPTOR* pSecurity + ); + +BOOL DAPI CertHasPrivateKey( + __in PCCERT_CONTEXT pCertContext, + __out_opt DWORD* pdwKeySpec + ); + +HRESULT DAPI CertInstallSingleCertificate( + __in HCERTSTORE hStore, + __in PCCERT_CONTEXT pCertContext, + __in LPCWSTR wzName + ); +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/conutil.h b/src/libs/dutil/WixToolset.DUtil/inc/conutil.h new file mode 100644 index 00000000..38aaea84 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/conutil.h @@ -0,0 +1,80 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ConsoleExitOnFailureSource(d, x, c, f, ...) if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } +#define ConsoleExitOnLastErrorSource(d, x, c, f, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } } +#define ConsoleExitOnNullSource(d, p, x, e, c, f, ...) if (NULL == p) { x = e; ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } +#define ConsoleExitOnNullWithLastErrorSource(d, p, x, c, f, ...) if (NULL == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } +#define ConsoleExitWithLastErrorSource(d, x, c, f, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } + + +#define ConsoleExitOnFailure(x, c, f, ...) ConsoleExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) +#define ConsoleExitOnLastError(x, c, f, ...) ConsoleExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) +#define ConsoleExitOnNull(p, x, e, c, f, ...) ConsoleExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, c, f, __VA_ARGS__) +#define ConsoleExitOnNullWithLastError(p, x, c, f, ...) ConsoleExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, c, f, __VA_ARGS__) +#define ConsoleExitWithLastError(x, c, f, ...) ConsoleExitWithLastErrorSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__) + +// enums +typedef enum CONSOLE_COLOR { CONSOLE_COLOR_NORMAL, CONSOLE_COLOR_RED, CONSOLE_COLOR_YELLOW, CONSOLE_COLOR_GREEN } CONSOLE_COLOR; + +// structs + +// functions +HRESULT DAPI ConsoleInitialize(); +void DAPI ConsoleUninitialize(); + +void DAPI ConsoleGreen(); +void DAPI ConsoleRed(); +void DAPI ConsoleYellow(); +void DAPI ConsoleNormal(); + +HRESULT DAPI ConsoleWrite( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ); +HRESULT DAPI ConsoleWriteLine( + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ); +HRESULT DAPI ConsoleWriteError( + HRESULT hrError, + CONSOLE_COLOR cc, + __in_z __format_string LPCSTR szFormat, + ... + ); + +HRESULT DAPI ConsoleReadW( + __deref_out_z LPWSTR* ppwzBuffer + ); + +HRESULT DAPI ConsoleReadStringA( + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* szCharBuffer, + CONST DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ); +HRESULT DAPI ConsoleReadStringW( + __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* szCharBuffer, + CONST DWORD cchCharBuffer, + __out DWORD* pcchNumCharReturn + ); + +HRESULT DAPI ConsoleReadNonBlockingW( + __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, + __out DWORD* pcchSize, + BOOL fReadLine + ); + +HRESULT DAPI ConsoleSetReadHidden(void); +HRESULT DAPI ConsoleSetReadNormal(void); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libs/dutil/WixToolset.DUtil/inc/cryputil.h b/src/libs/dutil/WixToolset.DUtil/inc/cryputil.h new file mode 100644 index 00000000..02492d8a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/cryputil.h @@ -0,0 +1,106 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define ReleaseCryptMsg(p) if (p) { ::CryptMsgClose(p); p = NULL; } + +#ifdef __cplusplus +extern "C" { +#endif + + +// Use CRYPTPROTECTMEMORY_BLOCK_SIZE, because it's larger and thus more restrictive than RTL_ENCRYPT_MEMORY_SIZE. +#define CRYP_ENCRYPT_MEMORY_SIZE CRYPTPROTECTMEMORY_BLOCK_SIZE +#define MD5_HASH_LEN 16 +#define SHA1_HASH_LEN 20 +#define SHA256_HASH_LEN 32 +#define SHA512_HASH_LEN 64 + +typedef NTSTATUS (APIENTRY *PFN_RTLENCRYPTMEMORY)( + __inout PVOID Memory, + __in ULONG MemoryLength, + __in ULONG OptionFlags + ); + +typedef NTSTATUS (APIENTRY *PFN_RTLDECRYPTMEMORY)( + __inout PVOID Memory, + __in ULONG MemoryLength, + __in ULONG OptionFlags + ); + +typedef BOOL (APIENTRY *PFN_CRYPTPROTECTMEMORY)( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ); + +typedef BOOL (APIENTRY *PFN_CRYPTUNPROTECTMEMORY)( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ); + +// function declarations + +HRESULT DAPI CrypInitialize(); +void DAPI CrypUninitialize(); + +HRESULT DAPI CrypDecodeObject( + __in_z LPCSTR szStructType, + __in_ecount(cbData) const BYTE* pbData, + __in DWORD cbData, + __in DWORD dwFlags, + __out LPVOID* ppvObject, + __out_opt DWORD* pcbObject + ); + +HRESULT DAPI CrypMsgGetParam( + __in HCRYPTMSG hCryptMsg, + __in DWORD dwType, + __in DWORD dwIndex, + __out LPVOID* ppvData, + __out_opt DWORD* pcbData + ); + +HRESULT DAPI CrypHashFile( + __in_z LPCWSTR wzFilePath, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash, + __out_opt DWORD64* pqwBytesHashed + ); + +HRESULT DAPI CrypHashFileHandle( + __in HANDLE hFile, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash, + __out_opt DWORD64* pqwBytesHashed + ); + +HRESULT DAPI CrypHashBuffer( + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in DWORD dwProvType, + __in ALG_ID algid, + __out_bcount(cbHash) BYTE* pbHash, + __in DWORD cbHash + ); + +HRESULT DAPI CrypEncryptMemory( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ); + +HRESULT DAPI CrypDecryptMemory( + __inout LPVOID pData, + __in DWORD cbData, + __in DWORD dwFlags + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/deputil.h b/src/libs/dutil/WixToolset.DUtil/inc/deputil.h new file mode 100644 index 00000000..bfe235f3 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/deputil.h @@ -0,0 +1,147 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseDependencyArray(rg, c) if (rg) { DepDependencyArrayFree(rg, c); } +#define ReleaseNullDependencyArray(rg, c) if (rg) { DepDependencyArrayFree(rg, c); rg = NULL; } + +typedef struct _DEPENDENCY +{ + LPWSTR sczKey; + LPWSTR sczName; +} DEPENDENCY; + + +/*************************************************************************** + DepGetProviderInformation - gets the various pieces of data registered + with a dependency. + + Note: Returns E_NOTFOUND if the dependency was not found. +***************************************************************************/ +DAPI_(HRESULT) DepGetProviderInformation( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __deref_out_z_opt LPWSTR* psczId, + __deref_out_z_opt LPWSTR* psczName, + __deref_out_z_opt LPWSTR* psczVersion + ); + +/*************************************************************************** + DepCheckDependency - Checks that the dependency is registered and within + the proper version range. + + Note: Returns E_NOTFOUND if the dependency was not found. +***************************************************************************/ +DAPI_(HRESULT) DepCheckDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __in_z_opt LPCWSTR wzMinVersion, + __in_z_opt LPCWSTR wzMaxVersion, + __in int iAttributes, + __in STRINGDICT_HANDLE sdDependencies, + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies + ); + +/*************************************************************************** + DepCheckDependents - Checks if any dependents are still installed for the + given provider key. + +***************************************************************************/ +DAPI_(HRESULT) DepCheckDependents( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __reserved int iAttributes, + __in C_STRINGDICT_HANDLE sdIgnoredDependents, + __deref_inout_ecount_opt(*pcDependents) DEPENDENCY** prgDependents, + __inout LPUINT pcDependents + ); + +/*************************************************************************** + DepRegisterDependency - Registers the dependency provider. + +***************************************************************************/ +DAPI_(HRESULT) DepRegisterDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey, + __in_z LPCWSTR wzVersion, + __in_z LPCWSTR wzDisplayName, + __in_z_opt LPCWSTR wzId, + __in int iAttributes + ); + +/*************************************************************************** + DepDependentExists - Determines if a dependent is registered. + + Note: Returns S_OK if dependent is registered. + Returns E_FILENOTFOUND if dependent is not registered +***************************************************************************/ +DAPI_(HRESULT) DepDependentExists( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey + ); + +/*************************************************************************** + DepRegisterDependent - Registers a dependent under the dependency provider. + +***************************************************************************/ +DAPI_(HRESULT) DepRegisterDependent( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey, + __in_z_opt LPCWSTR wzMinVersion, + __in_z_opt LPCWSTR wzMaxVersion, + __in int iAttributes + ); + +/*************************************************************************** + DepUnregisterDependency - Removes the dependency provider. + + Note: Caller should call CheckDependents prior to remove a dependency. + Returns E_FILENOTFOUND if the dependency is not registered. +***************************************************************************/ +DAPI_(HRESULT) DepUnregisterDependency( + __in HKEY hkHive, + __in_z LPCWSTR wzProviderKey + ); + +/*************************************************************************** + DepUnregisterDependent - Removes a dependent under the dependency provider. + + Note: Returns E_FILENOTFOUND if neither the dependency or dependent are + registered. + ***************************************************************************/ +DAPI_(HRESULT) DepUnregisterDependent( + __in HKEY hkHive, + __in_z LPCWSTR wzDependencyProviderKey, + __in_z LPCWSTR wzProviderKey + ); + +/*************************************************************************** + DependencyArrayAlloc - Allocates or expands an array of DEPENDENCY structs. + +***************************************************************************/ +DAPI_(HRESULT) DepDependencyArrayAlloc( + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies, + __in_z LPCWSTR wzKey, + __in_z_opt LPCWSTR wzName + ); + +/*************************************************************************** + DepDependencyArrayFree - Frees an array of DEPENDENCY structs. + +***************************************************************************/ +DAPI_(void) DepDependencyArrayFree( + __in_ecount(cDependencies) DEPENDENCY* rgDependencies, + __in UINT cDependencies + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dictutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dictutil.h new file mode 100644 index 00000000..f0a3bb5a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/dictutil.h @@ -0,0 +1,69 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseDict(sdh) if (sdh) { DictDestroy(sdh); } +#define ReleaseNullDict(sdh) if (sdh) { DictDestroy(sdh); sdh = NULL; } + +typedef void* STRINGDICT_HANDLE; +typedef const void* C_STRINGDICT_HANDLE; + +extern const int STRINGDICT_HANDLE_BYTES; + +typedef enum DICT_FLAG +{ + DICT_FLAG_NONE = 0, + DICT_FLAG_CASEINSENSITIVE = 1 +} DICT_FLAG; + +HRESULT DAPI DictCreateWithEmbeddedKey( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in DWORD dwNumExpectedItems, + __in_opt void **ppvArray, + __in size_t cByteOffset, + __in DICT_FLAG dfFlags + ); +HRESULT DAPI DictCreateStringList( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in DWORD dwNumExpectedItems, + __in DICT_FLAG dfFlags + ); +HRESULT DAPI DictCreateStringListFromArray( + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle, + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, + __in const DWORD cStringArray, + __in DICT_FLAG dfFlags + ); +HRESULT DAPI DictCompareStringListToArray( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList, + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray, + __in const DWORD cStringArray + ); +HRESULT DAPI DictAddKey( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR szString + ); +HRESULT DAPI DictAddValue( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle, + __in void *pvValue + ); +HRESULT DAPI DictKeyExists( + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR szString + ); +HRESULT DAPI DictGetValue( + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle, + __in_z LPCWSTR szString, + __out void **ppvValue + ); +void DAPI DictDestroy( + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h new file mode 100644 index 00000000..539b3a73 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h @@ -0,0 +1,59 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +typedef enum DIR_DELETE +{ + DIR_DELETE_FILES = 1, + DIR_DELETE_RECURSE = 2, + DIR_DELETE_SCHEDULE = 4, +} DIR_DELETE; + +#ifdef __cplusplus +extern "C" { +#endif + +BOOL DAPI DirExists( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ); + +HRESULT DAPI DirCreateTempPath( + __in_z LPCWSTR wzPrefix, + __out_ecount_z(cchPath) LPWSTR wzPath, + __in DWORD cchPath + ); + +HRESULT DAPI DirEnsureExists( + __in_z LPCWSTR wzPath, + __in_opt LPSECURITY_ATTRIBUTES psa + ); + +HRESULT DAPI DirEnsureDelete( + __in_z LPCWSTR wzPath, + __in BOOL fDeleteFiles, + __in BOOL fRecurse + ); + +HRESULT DAPI DirEnsureDeleteEx( + __in_z LPCWSTR wzPath, + __in DWORD dwFlags + ); + +DWORD DAPI DirDeleteEmptyDirectoriesToRoot( + __in_z LPCWSTR wzPath, + __in DWORD dwFlags + ); + +HRESULT DAPI DirGetCurrent( + __deref_out_z LPWSTR* psczCurrentDirectory + ); + +HRESULT DAPI DirSetCurrent( + __in_z LPCWSTR wzDirectory + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dlutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dlutil.h new file mode 100644 index 00000000..3e95103a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/dlutil.h @@ -0,0 +1,59 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef HRESULT (WINAPI *LPAUTHENTICATION_ROUTINE)( + __in LPVOID pVoid, + __in HINTERNET hUrl, + __in long lHttpCode, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ); + +typedef int (WINAPI *LPCANCEL_ROUTINE)( + __in HRESULT hrError, + __in_z_opt LPCWSTR wzError, + __in BOOL fAllowRetry, + __in_opt LPVOID pvContext + ); + +// structs +typedef struct _DOWNLOAD_SOURCE +{ + LPWSTR sczUrl; + LPWSTR sczUser; + LPWSTR sczPassword; +} DOWNLOAD_SOURCE; + +typedef struct _DOWNLOAD_CACHE_CALLBACK +{ + LPPROGRESS_ROUTINE pfnProgress; + LPCANCEL_ROUTINE pfnCancel; + LPVOID pv; +} DOWNLOAD_CACHE_CALLBACK; + +typedef struct _DOWNLOAD_AUTHENTICATION_CALLBACK +{ + LPAUTHENTICATION_ROUTINE pfnAuthenticate; + LPVOID pv; +} DOWNLOAD_AUTHENTICATION_CALLBACK; + + +// functions + +HRESULT DAPI DownloadUrl( + __in DOWNLOAD_SOURCE* pDownloadSource, + __in DWORD64 dw64AuthoredDownloadSize, + __in LPCWSTR wzDestinationPath, + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache, + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate + ); + + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dpiutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dpiutil.h new file mode 100644 index 00000000..b30e2332 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/dpiutil.h @@ -0,0 +1,120 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// from WinUser.h +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#endif +#ifndef USER_DEFAULT_SCREEN_DPI +#define USER_DEFAULT_SCREEN_DPI 96 +#endif + +typedef enum DPIU_AWARENESS +{ + DPIU_AWARENESS_NONE = 0x0, + DPIU_AWARENESS_SYSTEM = 0x1, + DPIU_AWARENESS_PERMONITOR = 0x2, + DPIU_AWARENESS_PERMONITORV2 = 0x4, + DPIU_AWARENESS_GDISCALED = 0x8, +} DPIU_PROCESS_AWARENESS; + +typedef struct _DPIU_MONITOR_CONTEXT +{ + UINT nDpi; + MONITORINFOEXW mi; +} DPIU_MONITOR_CONTEXT; + +typedef struct _DPIU_WINDOW_CONTEXT +{ + UINT nDpi; +} DPIU_WINDOW_CONTEXT; + +typedef BOOL (APIENTRY* PFN_ADJUSTWINDOWRECTEXFORDPI)( + __in LPRECT lpRect, + __in DWORD dwStyle, + __in BOOL bMenu, + __in DWORD dwExStyle, + __in UINT dpi + ); +typedef UINT (APIENTRY *PFN_GETDPIFORWINDOW)( + __in HWND hwnd + ); +typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARE)(); +typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARENESSCONTEXT)( + __in DPI_AWARENESS_CONTEXT value + ); + +#ifdef DPI_ENUMS_DECLARED +typedef HRESULT(APIENTRY* PFN_GETDPIFORMONITOR)( + __in HMONITOR hmonitor, + __in MONITOR_DPI_TYPE dpiType, + __in UINT* dpiX, + __in UINT* dpiY + ); +typedef HRESULT(APIENTRY* PFN_SETPROCESSDPIAWARENESS)( + __in PROCESS_DPI_AWARENESS value + ); +#endif + +void DAPI DpiuInitialize(); +void DAPI DpiuUninitialize(); + +/******************************************************************** + DpiuAdjustWindowRect - calculate the required size of the window rectangle, + based on the desired size of the client rectangle + and the provided DPI. + +*******************************************************************/ +void DAPI DpiuAdjustWindowRect( + __in RECT* pWindowRect, + __in DWORD dwStyle, + __in BOOL fMenu, + __in DWORD dwExStyle, + __in UINT nDpi + ); + +/******************************************************************** + DpiuGetMonitorContextFromPoint - get the DPI context of the monitor from the given point. + +*******************************************************************/ +HRESULT DAPI DpiuGetMonitorContextFromPoint( + __in const POINT* pt, + __out DPIU_MONITOR_CONTEXT** ppMonitorContext + ); + +/******************************************************************** + DpiuGetWindowContext - get the DPI context of the given window. + +*******************************************************************/ +void DAPI DpiuGetWindowContext( + __in HWND hWnd, + __in DPIU_WINDOW_CONTEXT* pWindowContext + ); + +/******************************************************************** + DpiuScaleValue - scale the value to the target DPI. + +*******************************************************************/ +int DAPI DpiuScaleValue( + __in int nDefaultDpiValue, + __in UINT nTargetDpi + ); + +/******************************************************************** + DpiuSetProcessDpiAwareness - set the process DPI awareness. The ranking is + PERMONITORV2 > PERMONITOR > SYSTEM > GDISCALED > NONE. + +*******************************************************************/ +HRESULT DAPI DpiuSetProcessDpiAwareness( + __in DPIU_AWARENESS supportedAwareness, + __in_opt DPIU_AWARENESS* pSelectedAwareness + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dutil.h new file mode 100644 index 00000000..fc9ec0f4 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/dutil.h @@ -0,0 +1,190 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "dutilsources.h" + +#define DAPI __stdcall +#define DAPIV __cdecl // used only for functions taking variable length arguments + +#define DAPI_(type) EXTERN_C type DAPI +#define DAPIV_(type) EXTERN_C type DAPIV + + +// asserts and traces +typedef BOOL (DAPI *DUTIL_ASSERTDISPLAYFUNCTION)(__in_z LPCSTR sz); + +typedef void (CALLBACK *DUTIL_CALLBACK_TRACEERROR)( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in UINT source, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************** + DutilInitialize - initialize dutil. + +*******************************************************************/ +HRESULT DAPI DutilInitialize( + __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback + ); + +/******************************************************************** + DutilUninitialize - uninitialize dutil. + +*******************************************************************/ +void DAPI DutilUninitialize(); + +void DAPI Dutil_SetAssertModule(__in HMODULE hAssertModule); +void DAPI Dutil_SetAssertDisplayFunction(__in DUTIL_ASSERTDISPLAYFUNCTION pfn); +void DAPI Dutil_Assert(__in_z LPCSTR szFile, __in int iLine); +void DAPI Dutil_AssertSz(__in_z LPCSTR szFile, __in int iLine, __in_z __format_string LPCSTR szMessage); + +void DAPI Dutil_TraceSetLevel(__in REPORT_LEVEL ll, __in BOOL fTraceFilenames); +REPORT_LEVEL DAPI Dutil_TraceGetLevel(); +void DAPIV Dutil_Trace(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in_z __format_string LPCSTR szMessage, ...); +void DAPIV Dutil_TraceError(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in HRESULT hr, __in_z __format_string LPCSTR szMessage, ...); +void DAPIV Dutil_TraceErrorSource(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in UINT source, __in HRESULT hr, __in_z __format_string LPCSTR szMessage, ...); +void DAPI Dutil_RootFailure(__in_z LPCSTR szFile, __in int iLine, __in HRESULT hrError); + +#ifdef __cplusplus +} +#endif + + +#ifdef DEBUG + +#define AssertSetModule(m) (void)Dutil_SetAssertModule(m) +#define AssertSetDisplayFunction(pfn) (void)Dutil_SetAssertDisplayFunction(pfn) +#define Assert(f) ((f) ? (void)0 : (void)Dutil_Assert(__FILE__, __LINE__)) +#define AssertSz(f, sz) ((f) ? (void)0 : (void)Dutil_AssertSz(__FILE__, __LINE__, sz)) + +#define TraceSetLevel(l, f) (void)Dutil_TraceSetLevel(l, f) +#define TraceGetLevel() (REPORT_LEVEL)Dutil_TraceGetLevel() +#define Trace(l, f, ...) (void)Dutil_Trace(__FILE__, __LINE__, l, f, __VA_ARGS__) +#define TraceError(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_ERROR, x, f, __VA_ARGS__) +#define TraceErrorDebug(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_DEBUG, x, f, __VA_ARGS__) + +#else // !DEBUG + +#define AssertSetModule(m) +#define AssertSetDisplayFunction(pfn) +#define Assert(f) +#define AssertSz(f, sz) + +#define TraceSetLevel(l, f) +#define Trace(l, f, ...) +#define TraceError(x, f, ...) +#define TraceErrorDebug(x, f, ...) + +#endif // DEBUG + +// DUTIL_SOURCE_DEFAULT can be overriden +#ifndef DUTIL_SOURCE_DEFAULT +#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_UNKNOWN +#endif + +// Exit macros +#define ExitFunction() { goto LExit; } +#define ExitFunction1(x) { x; goto LExit; } + +#define ExitFunctionWithLastError(x) { x = HRESULT_FROM_WIN32(::GetLastError()); goto LExit; } + +#define ExitTraceSource(d, x, s, ...) { TraceError(x, s, __VA_ARGS__); (void)Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_ERROR, d, x, s, __VA_ARGS__); } +#define ExitTraceDebugSource(d, x, s, ...) { TraceErrorDebug(x, s, __VA_ARGS__); (void)Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_DEBUG, d, x, s, __VA_ARGS__); } + +#define ExitOnLastErrorSource(d, x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } } +#define ExitOnLastErrorDebugTraceSource(d, x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceDebugSource(d, x, s, __VA_ARGS__); goto LExit; } } +#define ExitWithLastErrorSource(d, x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } +#define ExitOnFailureSource(d, x, s, ...) if (FAILED(x)) { ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } +#define ExitOnRootFailureSource(d, x, s, ...) if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } +#define ExitOnFailureDebugTraceSource(d, x, s, ...) if (FAILED(x)) { ExitTraceDebugSource(d, x, s, __VA_ARGS__); goto LExit; } +#define ExitOnNullSource(d, p, x, e, s, ...) if (NULL == p) { x = e; Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } +#define ExitOnNullWithLastErrorSource(d, p, x, s, ...) if (NULL == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } +#define ExitOnNullDebugTraceSource(d, p, x, e, s, ...) if (NULL == p) { x = e; Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceDebugSource(d, x, s, __VA_ARGS__); goto LExit; } +#define ExitOnInvalidHandleWithLastErrorSource(d, p, x, s, ...) if (INVALID_HANDLE_VALUE == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } +#define ExitOnWin32ErrorSource(d, e, x, s, ...) if (ERROR_SUCCESS != e) { x = HRESULT_FROM_WIN32(e); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } + +#define ExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, s, __VA_ARGS__) +#define ExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, s, __VA_ARGS__) +#define ExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DEFAULT, p, x, e, s, __VA_ARGS__) +#define ExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, s, __VA_ARGS__) +#define ExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEFAULT, e, x, s, __VA_ARGS__) + +// release macros +#define ReleaseObject(x) if (x) { x->Release(); } +#define ReleaseObjectArray(prg, cel) if (prg) { for (DWORD Dutil_ReleaseObjectArrayIndex = 0; Dutil_ReleaseObjectArrayIndex < cel; ++Dutil_ReleaseObjectArrayIndex) { ReleaseObject(prg[Dutil_ReleaseObjectArrayIndex]); } ReleaseMem(prg); } +#define ReleaseVariant(x) { ::VariantClear(&x); } +#define ReleaseNullObject(x) if (x) { (x)->Release(); x = NULL; } +#define ReleaseCertificate(x) if (x) { ::CertFreeCertificateContext(x); x=NULL; } +#define ReleaseHandle(x) if (x) { ::CloseHandle(x); x = NULL; } + + +// useful defines and macros +#define Unused(x) ((void)x) + +#ifndef countof +#if 1 +#define countof(ary) (sizeof(ary) / sizeof(ary[0])) +#else +#ifndef __cplusplus +#define countof(ary) (sizeof(ary) / sizeof(ary[0])) +#else +template static char countofVerify(void const *, T) throw() { return 0; } +template static void countofVerify(T *const, T *const *) throw() {}; +#define countof(arr) (sizeof(countofVerify(arr,&(arr))) * sizeof(arr)/sizeof(*(arr))) +#endif +#endif +#endif + +#define roundup(x, n) roundup_typed(x, n, DWORD) +#define roundup_typed(x, n, t) (((t)(x) + ((t)(n) - 1)) & ~((t)(n) - 1)) + +#define HRESULT_FROM_RPC(x) ((HRESULT) ((x) | FACILITY_RPC)) + +#ifndef MAXSIZE_T +#define MAXSIZE_T ((SIZE_T)~((SIZE_T)0)) +#endif + +typedef const BYTE* LPCBYTE; + +#define E_FILENOTFOUND HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) +#define E_PATHNOTFOUND HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) +#define E_INVALIDDATA HRESULT_FROM_WIN32(ERROR_INVALID_DATA) +#define E_INVALIDSTATE HRESULT_FROM_WIN32(ERROR_INVALID_STATE) +#define E_INSUFFICIENT_BUFFER HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) +#define E_MOREDATA HRESULT_FROM_WIN32(ERROR_MORE_DATA) +#define E_NOMOREITEMS HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) +#define E_NOTFOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND) +#define E_MODNOTFOUND HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND) +#define E_BADCONFIGURATION HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION) + +#define AddRefAndRelease(x) { x->AddRef(); x->Release(); } + +#define MAKEDWORD(lo, hi) ((DWORD)MAKELONG(lo, hi)) +#define MAKEQWORDVERSION(mj, mi, b, r) (((DWORD64)MAKELONG(r, b)) | (((DWORD64)MAKELONG(mi, mj)) << 32)) + + +#ifdef __cplusplus +extern "C" { +#endif + +// other functions +HRESULT DAPI LoadSystemLibrary(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule); +HRESULT DAPI LoadSystemLibraryWithPath(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule, __deref_out_z_opt LPWSTR* psczPath); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h b/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h new file mode 100644 index 00000000..7d512cb3 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h @@ -0,0 +1,76 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +typedef enum DUTIL_SOURCE +{ + DUTIL_SOURCE_UNKNOWN, + DUTIL_SOURCE_ACLUTIL, + DUTIL_SOURCE_APPUTIL, + DUTIL_SOURCE_APUPUTIL, + DUTIL_SOURCE_ATOMUTIL, + DUTIL_SOURCE_BUFFUTIL, + DUTIL_SOURCE_BUTIL, + DUTIL_SOURCE_CABCUTIL, + DUTIL_SOURCE_CABUTIL, + DUTIL_SOURCE_CERTUTIL, + DUTIL_SOURCE_CONUTIL, + DUTIL_SOURCE_CRYPUTIL, + DUTIL_SOURCE_DEPUTIL, + DUTIL_SOURCE_DICTUTIL, + DUTIL_SOURCE_DIRUTIL, + DUTIL_SOURCE_DLUTIL, + DUTIL_SOURCE_DPIUTIL, + DUTIL_SOURCE_DUTIL, + DUTIL_SOURCE_ESEUTIL, + DUTIL_SOURCE_FILEUTIL, + DUTIL_SOURCE_GDIPUTIL, + DUTIL_SOURCE_GUIDUTIL, + DUTIL_SOURCE_IIS7UTIL, + DUTIL_SOURCE_INETUTIL, + DUTIL_SOURCE_INIUTIL, + DUTIL_SOURCE_JSONUTIL, + DUTIL_SOURCE_LOCUTIL, + DUTIL_SOURCE_LOGUTIL, + DUTIL_SOURCE_MEMUTIL, + DUTIL_SOURCE_METAUTIL, + DUTIL_SOURCE_MONUTIL, + DUTIL_SOURCE_OSUTIL, + DUTIL_SOURCE_PATHUTIL, + DUTIL_SOURCE_PERFUTIL, + DUTIL_SOURCE_POLCUTIL, + DUTIL_SOURCE_PROCUTIL, + DUTIL_SOURCE_REGUTIL, + DUTIL_SOURCE_RESRUTIL, + DUTIL_SOURCE_RESWUTIL, + DUTIL_SOURCE_REXUTIL, + DUTIL_SOURCE_RMUTIL, + DUTIL_SOURCE_RSSUTIL, + DUTIL_SOURCE_SCEUTIL, + DUTIL_SOURCE_SCZUTIL, + DUTIL_SOURCE_SHELUTIL, + DUTIL_SOURCE_SQLUTIL, + DUTIL_SOURCE_SRPUTIL, + DUTIL_SOURCE_STRUTIL, + DUTIL_SOURCE_SVCUTIL, + DUTIL_SOURCE_THMUTIL, + DUTIL_SOURCE_TIMEUTIL, + DUTIL_SOURCE_UNCUTIL, + DUTIL_SOURCE_URIUTIL, + DUTIL_SOURCE_USERUTIL, + DUTIL_SOURCE_WIUTIL, + DUTIL_SOURCE_WUAUTIL, + DUTIL_SOURCE_XMLUTIL, + DUTIL_SOURCE_VERUTIL, + + DUTIL_SOURCE_EXTERNAL = 256, +} DUTIL_SOURCE; + +typedef enum REPORT_LEVEL +{ + REPORT_NONE, // turns off report (only valid for XXXSetLevel()) + REPORT_WARNING, // written if want only warnings or reporting is on in general + REPORT_STANDARD, // written if reporting is on + REPORT_VERBOSE, // written only if verbose reporting is on + REPORT_DEBUG, // reporting useful when debugging code + REPORT_ERROR, // always gets reported, but can never be specified +} REPORT_LEVEL; diff --git a/src/libs/dutil/WixToolset.DUtil/inc/eseutil.h b/src/libs/dutil/WixToolset.DUtil/inc/eseutil.h new file mode 100644 index 00000000..bea47b2b --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/eseutil.h @@ -0,0 +1,223 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); } +#define ReleaseNullEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); pqh = NULL; } + +struct ESE_COLUMN_SCHEMA +{ + JET_COLUMNID jcColumn; + LPCWSTR pszName; + JET_COLTYP jcColumnType; + BOOL fKey; // If this column is part of the key of the table + BOOL fFixed; + BOOL fNullable; + BOOL fAutoIncrement; +}; + +struct ESE_TABLE_SCHEMA +{ + JET_TABLEID jtTable; + LPCWSTR pszName; + DWORD dwColumns; + ESE_COLUMN_SCHEMA *pcsColumns; +}; + +struct ESE_DATABASE_SCHEMA +{ + DWORD dwTables; + ESE_TABLE_SCHEMA *ptsTables; +}; + +typedef enum ESE_QUERY_TYPE +{ + ESE_QUERY_EXACT, + ESE_QUERY_FROM_TOP, + ESE_QUERY_FROM_BOTTOM +} ESE_QUERY_TYPE; + +typedef void* ESE_QUERY_HANDLE; + +HRESULT DAPI EseBeginSession( + __out JET_INSTANCE *pjiInstance, + __out JET_SESID *pjsSession, + __in_z LPCWSTR pszInstance, + __in_z LPCWSTR pszPath + ); +HRESULT DAPI EseEndSession( + __in JET_INSTANCE jiInstance, + __in JET_SESID jsSession + ); +HRESULT DAPI EseEnsureDatabase( + __in JET_SESID jsSession, + __in_z LPCWSTR pszFile, + __in ESE_DATABASE_SCHEMA *pdsSchema, + __out JET_DBID* pjdbDb, + __in BOOL fExclusive, + __in BOOL fReadonly + ); +HRESULT DAPI EseCloseDatabase( + __in JET_SESID jsSession, + __in JET_DBID jdbDb + ); +HRESULT DAPI EseCreateTable( + __in JET_SESID jsSession, + __in JET_DBID jdbDb, + __in_z LPCWSTR pszTable, + __out JET_TABLEID *pjtTable + ); +HRESULT DAPI EseOpenTable( + __in JET_SESID jsSession, + __in JET_DBID jdbDb, + __in_z LPCWSTR pszTable, + __out JET_TABLEID *pjtTable + ); +HRESULT DAPI EseCloseTable( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable + ); +HRESULT DAPI EseEnsureColumn( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in_z LPCWSTR pszColumnName, + __in JET_COLTYP jcColumnType, + __in ULONG ulColumnSize, + __in BOOL fFixed, + __in BOOL fNullable, + __out_opt JET_COLUMNID *pjcColumn + ); +HRESULT DAPI EseGetColumn( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in_z LPCWSTR pszColumnName, + __out JET_COLUMNID *pjcColumn + ); +HRESULT DAPI EseMoveCursor( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in LONG lRow + ); +HRESULT DAPI EseDeleteRow( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable + ); +HRESULT DAPI EseBeginTransaction( + __in JET_SESID jsSession + ); +HRESULT DAPI EseRollbackTransaction( + __in JET_SESID jsSession, + __in BOOL fAll + ); +HRESULT DAPI EseCommitTransaction( + __in JET_SESID jsSession + ); +HRESULT DAPI EsePrepareUpdate( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in ULONG ulPrep + ); +HRESULT DAPI EseFinishUpdate( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in BOOL fSeekToInsertedRecord + ); +HRESULT DAPI EseSetColumnBinary( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ); +HRESULT DAPI EseSetColumnDword( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in DWORD dwValue + ); +HRESULT DAPI EseSetColumnBool( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in BOOL fValue + ); +HRESULT DAPI EseSetColumnString( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __in_z LPCWSTR pszValue + ); +HRESULT DAPI EseSetColumnEmpty( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn + ); +HRESULT DAPI EseGetColumnBinary( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ); +HRESULT DAPI EseGetColumnDword( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out DWORD *pdwValue + ); +HRESULT DAPI EseGetColumnBool( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out BOOL *pfValue + ); +HRESULT DAPI EseGetColumnString( + __in JET_SESID jsSession, + __in ESE_TABLE_SCHEMA tsTable, + __in DWORD dwColumn, + __out LPWSTR *ppszValue + ); + +// Call this once for each key column in the table +HRESULT DAPI EseBeginQuery( + __in JET_SESID jsSession, + __in JET_TABLEID jtTable, + __in ESE_QUERY_TYPE qtQueryType, + __out ESE_QUERY_HANDLE *peqhHandle + ); +HRESULT DAPI EseSetQueryColumnBinary( + __in ESE_QUERY_HANDLE eqhHandle, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ); +HRESULT DAPI EseSetQueryColumnDword( + __in ESE_QUERY_HANDLE eqhHandle, + __in DWORD dwData, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ); +HRESULT DAPI EseSetQueryColumnBool( + __in ESE_QUERY_HANDLE eqhHandle, + __in BOOL fValue, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ); +HRESULT DAPI EseSetQueryColumnString( + __in ESE_QUERY_HANDLE eqhHandle, + __in_z LPCWSTR pszString, + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*" + ); +HRESULT DAPI EseFinishQuery( + __in ESE_QUERY_HANDLE eqhHandle + ); +// Once all columns have been set up, call this and read the result +HRESULT DAPI EseRunQuery( + __in ESE_QUERY_HANDLE eqhHandle + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/fileutil.h b/src/libs/dutil/WixToolset.DUtil/inc/fileutil.h new file mode 100644 index 00000000..d3e326f7 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/fileutil.h @@ -0,0 +1,247 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseFile(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; } +#define ReleaseFileHandle(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; } +#define ReleaseFileFindHandle(h) if (INVALID_HANDLE_VALUE != h) { ::FindClose(h); h = INVALID_HANDLE_VALUE; } + +#define FILEMAKEVERSION(major, minor, build, revision) static_cast((static_cast(major & 0xFFFF) << 48) \ + | (static_cast(minor & 0xFFFF) << 32) \ + | (static_cast(build & 0xFFFF) << 16) \ + | (static_cast(revision & 0xFFFF))) + +typedef enum FILE_ARCHITECTURE +{ + FILE_ARCHITECTURE_UNKNOWN, + FILE_ARCHITECTURE_X86, + FILE_ARCHITECTURE_X64, + FILE_ARCHITECTURE_IA64, +} FILE_ARCHITECTURE; + +typedef enum FILE_ENCODING +{ + FILE_ENCODING_UNSPECIFIED = 0, + // TODO: distinguish between non-BOM utf-8 and ANSI in the future? + FILE_ENCODING_UTF8, + FILE_ENCODING_UTF8_WITH_BOM, + FILE_ENCODING_UTF16, + FILE_ENCODING_UTF16_WITH_BOM, +} FILE_ENCODING; + + +LPWSTR DAPI FileFromPath( + __in_z LPCWSTR wzPath + ); +HRESULT DAPI FileResolvePath( + __in_z LPCWSTR wzRelativePath, + __out LPWSTR *ppwzFullPath + ); +HRESULT DAPI FileStripExtension( + __in_z LPCWSTR wzFileName, + __out LPWSTR *ppwzFileNameNoExtension + ); +HRESULT DAPI FileChangeExtension( + __in_z LPCWSTR wzFileName, + __in_z LPCWSTR wzNewExtension, + __out LPWSTR *ppwzFileNameNewExtension + ); +HRESULT DAPI FileAddSuffixToBaseName( + __in_z LPCWSTR wzFileName, + __in_z LPCWSTR wzSuffix, + __out_z LPWSTR* psczNewFileName + ); +HRESULT DAPI FileVersionFromString( + __in_z LPCWSTR wzVersion, + __out DWORD *pdwVerMajor, + __out DWORD* pdwVerMinor + ); +HRESULT DAPI FileVersionFromStringEx( + __in_z LPCWSTR wzVersion, + __in SIZE_T cchVersion, + __out DWORD64* pqwVersion + ); +HRESULT DAPI FileVersionToStringEx( + __in DWORD64 qwVersion, + __out LPWSTR* psczVersion + ); +HRESULT DAPI FileSetPointer( + __in HANDLE hFile, + __in DWORD64 dw64Move, + __out_opt DWORD64* pdw64NewPosition, + __in DWORD dwMoveMethod + ); +HRESULT DAPI FileSize( + __in_z LPCWSTR pwzFileName, + __out LONGLONG* pllSize + ); +HRESULT DAPI FileSizeByHandle( + __in HANDLE hFile, + __out LONGLONG* pllSize + ); +BOOL DAPI FileExistsEx( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ); +BOOL DAPI FileExistsAfterRestart( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ); +HRESULT DAPI FileRemoveFromPendingRename( + __in_z LPCWSTR wzPath + ); +HRESULT DAPI FileRead( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath + ); +HRESULT DAPI FileReadEx( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in DWORD dwShareMode + ); +HRESULT DAPI FileReadUntil( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in DWORD cbMaxRead + ); +HRESULT DAPI FileReadPartial( + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in BOOL fSeek, + __in DWORD cbStartPosition, + __in DWORD cbMaxRead, + __in BOOL fPartialOK + ); +HRESULT DAPI FileReadPartialEx( + __deref_inout_bcount_full(*pcbDest) LPBYTE* ppbDest, + __out_range(<=, cbMaxRead) SIZE_T* pcbDest, + __in_z LPCWSTR wzSrcPath, + __in BOOL fSeek, + __in DWORD cbStartPosition, + __in DWORD cbMaxRead, + __in BOOL fPartialOK, + __in DWORD dwShareMode + ); +HRESULT DAPI FileReadHandle( + __in HANDLE hFile, + __in_bcount(cbDest) LPBYTE pbDest, + __in SIZE_T cbDest + ); +HRESULT DAPI FileWrite( + __in_z LPCWSTR pwzFileName, + __in DWORD dwFlagsAndAttributes, + __in_bcount_opt(cbData) LPCBYTE pbData, + __in SIZE_T cbData, + __out_opt HANDLE* pHandle + ); +HRESULT DAPI FileWriteHandle( + __in HANDLE hFile, + __in_bcount_opt(cbData) LPCBYTE pbData, + __in SIZE_T cbData + ); +HRESULT DAPI FileCopyUsingHandles( + __in HANDLE hSource, + __in HANDLE hTarget, + __in DWORD64 cbCopy, + __out_opt DWORD64* pcbCopied + ); +HRESULT DAPI FileCopyUsingHandlesWithProgress( + __in HANDLE hSource, + __in HANDLE hTarget, + __in DWORD64 cbCopy, + __in_opt LPPROGRESS_ROUTINE lpProgressRoutine, + __in_opt LPVOID lpData + ); +HRESULT DAPI FileEnsureCopy( + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzTarget, + __in BOOL fOverwrite + ); +HRESULT DAPI FileEnsureCopyWithRetry( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ); +HRESULT DAPI FileEnsureMove( + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in BOOL fAllowCopy + ); +HRESULT DAPI FileEnsureMoveWithRetry( + __in LPCWSTR wzSource, + __in LPCWSTR wzTarget, + __in BOOL fOverwrite, + __in BOOL fAllowCopy, + __in DWORD cRetry, + __in DWORD dwWaitMilliseconds + ); +HRESULT DAPI FileCreateTemp( + __in_z LPCWSTR wzPrefix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* ppwzTempFile, + __out_opt HANDLE* phTempFile + ); +HRESULT DAPI FileCreateTempW( + __in_z LPCWSTR wzPrefix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* ppwzTempFile, + __out_opt HANDLE* phTempFile + ); +HRESULT DAPI FileVersion( + __in_z LPCWSTR wzFilename, + __out DWORD *pdwVerMajor, + __out DWORD* pdwVerMinor + ); +HRESULT DAPI FileIsSame( + __in_z LPCWSTR wzFile1, + __in_z LPCWSTR wzFile2, + __out LPBOOL lpfSameFile + ); +HRESULT DAPI FileEnsureDelete( + __in_z LPCWSTR wzFile + ); +HRESULT DAPI FileGetTime( + __in_z LPCWSTR wzFile, + __out_opt LPFILETIME lpCreationTime, + __out_opt LPFILETIME lpLastAccessTime, + __out_opt LPFILETIME lpLastWriteTime + ); +HRESULT DAPI FileSetTime( + __in_z LPCWSTR wzFile, + __in_opt const FILETIME *lpCreationTime, + __in_opt const FILETIME *lpLastAccessTime, + __in_opt const FILETIME *lpLastWriteTime + ); +HRESULT DAPI FileResetTime( + __in_z LPCWSTR wzFile + ); +HRESULT DAPI FileExecutableArchitecture( + __in_z LPCWSTR wzFile, + __out FILE_ARCHITECTURE *pArchitecture + ); +HRESULT DAPI FileToString( + __in_z LPCWSTR wzFile, + __out LPWSTR *psczString, + __out_opt FILE_ENCODING *pfeEncoding + ); +HRESULT DAPI FileFromString( + __in_z LPCWSTR wzFile, + __in DWORD dwFlagsAndAttributes, + __in_z LPCWSTR sczString, + __in FILE_ENCODING feEncoding + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/gdiputil.h b/src/libs/dutil/WixToolset.DUtil/inc/gdiputil.h new file mode 100644 index 00000000..f2145828 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/gdiputil.h @@ -0,0 +1,39 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define ExitOnGdipFailureSource(d, g, x, s, ...) { x = GdipHresultFromStatus(g); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } } +#define ExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DEFAULT, g, x, s, __VA_ARGS__) + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI GdipInitialize( + __in const Gdiplus::GdiplusStartupInput* pInput, + __out ULONG_PTR* pToken, + __out_opt Gdiplus::GdiplusStartupOutput *pOutput + ); + +void DAPI GdipUninitialize( + __in ULONG_PTR token + ); + +HRESULT DAPI GdipBitmapFromResource( + __in_opt HINSTANCE hinst, + __in_z LPCSTR szId, + __out Gdiplus::Bitmap **ppBitmap + ); + +HRESULT DAPI GdipBitmapFromFile( + __in_z LPCWSTR wzFileName, + __out Gdiplus::Bitmap **ppBitmap + ); + +HRESULT DAPI GdipHresultFromStatus( + __in Gdiplus::Status gs + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/guidutil.h b/src/libs/dutil/WixToolset.DUtil/inc/guidutil.h new file mode 100644 index 00000000..478a464f --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/guidutil.h @@ -0,0 +1,21 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define GUID_STRING_LENGTH 39 + +HRESULT DAPI GuidFixedCreate( + _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid + ); + +HRESULT DAPI GuidCreate( + __deref_out_z LPWSTR* psczGuid + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/iis7util.h b/src/libs/dutil/WixToolset.DUtil/inc/iis7util.h new file mode 100644 index 00000000..3572b4e9 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/iis7util.h @@ -0,0 +1,222 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// IIS Config schema names +#define IIS_CONFIG_ADD L"add" +#define IIS_CONFIG_ALLOWED L"allowed" +#define IIS_CONFIG_APPHOST_ROOT L"MACHINE/WEBROOT/APPHOST" +#define IIS_CONFIG_APPLICATION L"application" +#define IIS_CONFIG_APPPOOL L"applicationPool" +#define IIS_CONFIG_APPPOOL_AUTO L"autoStart" +#define IIS_CONFIG_APPPOOL_SECTION L"system.applicationHost/applicationPools" +#define IIS_CONFIG_AUTOSTART L"serverAutoStart" +#define IIS_CONFIG_BINDING L"binding" +#define IIS_CONFIG_BINDINGINFO L"bindingInformation" +#define IIS_CONFIG_BINDINGS L"bindings" +#define IIS_CONFIG_DESC L"description" +#define IIS_CONFIG_EXECUTABLE L"scriptProcessor" +#define IIS_CONFIG_ENABLED L"enabled" +#define IIS_CONFIG_ENABLE32 L"enable32BitAppOnWin64" +#define IIS_CONFIG_FILEEXT L"fileExtension" +#define IIS_CONFIG_FILTER L"filter" +#define IIS_CONFIG_GROUPID L"groupId" +#define IIS_CONFIG_HEADERS L"customHeaders" +#define IIS_CONFIG_HTTPERRORS_SECTION L"system.webServer/httpErrors" +#define IIS_CONFIG_ID L"id" +#define IIS_CONFIG_ISAPI_SECTION L"system.webServer/isapiFilters" +#define IIS_CONFIG_HTTPPROTO_SECTION L"system.webServer/httpProtocol" +#define IIS_CONFIG_LOG_SECTION L"system.applicationHost/log" +#define IIS_CONFIG_LOG_UTF8 L"logInUTF8" +#define IIS_CONFIG_LIMITS L"limits" +#define IIS_CONFIG_PIPELINEMODE L"managedPipelineMode" +#define IIS_CONFIG_MANAGEDRUNTIMEVERSION L"managedRuntimeVersion" +#define IIS_CONFIG_WEBLOG L"logFile" +#define IIS_CONFIG_LOGFORMAT L"logFormat" +#define IIS_CONFIG_MIMEMAP L"mimeMap" +#define IIS_CONFIG_MIMETYPE L"mimeType" +#define IIS_CONFIG_MODULES L"modules" +#define IIS_CONFIG_NAME L"name" +#define IIS_CONFIG_PATH L"path" +#define IIS_CONFIG_PHYSPATH L"physicalPath" +#define IIS_CONFIG_PROTOCOL L"protocol" +#define IIS_CONFIG_RESTRICTION_SECTION L"system.webServer/security/isapiCgiRestriction" +#define IIS_CONFIG_SITE L"site" +#define IIS_CONFIG_SITE_ID L"id" +#define IIS_CONFIG_SITES_SECTION L"system.applicationHost/sites" +#define IIS_CONFIG_CONNECTTIMEOUT L"connectionTimeout" +#define IIS_CONFIG_VDIR L"virtualDirectory" +#define IIS_CONFIG_VALUE L"value" +#define IIS_CONFIG_VERBS L"verb" +#define IIS_CONFIG_WEBLIMITS_SECTION L"system.applicationHost/webLimits" +#define IIS_CONFIG_WEBLIMITS_MAXBAND L"maxGlobalBandwidth" +#define IIS_CONFIG_TRUE L"true" +#define IIS_CONFIG_FALSE L"false" +#define IIS_CONFIG_ERROR L"error" +#define IIS_CONFIG_STATUSCODE L"statusCode" +#define IIS_CONFIG_SUBSTATUS L"subStatusCode" +#define IIS_CONFIG_LANGPATH L"prefixLanguageFilePath" +#define IIS_CONFIG_RESPMODE L"responseMode" +#define IIS_CONFIG_CLEAR L"clear" +#define IIS_CONFIG_RECYCLING L"recycling" +#define IIS_CONFIG_PEROIDRESTART L"periodicRestart" +#define IIS_CONFIG_TIME L"time" +#define IIS_CONFIG_REQUESTS L"requests" +#define IIS_CONFIG_SCHEDULE L"schedule" +#define IIS_CONFIG_MEMORY L"memory" +#define IIS_CONFIG_PRIVMEMORY L"privateMemory" +#define IIS_CONFIG_PROCESSMODEL L"processModel" +#define IIS_CONFIG_IDLETIMEOUT L"idleTimeout" +#define IIS_CONFIG_QUEUELENGTH L"queueLength" +#define IIS_CONFIG_IDENITITYTYPE L"identityType" +#define IIS_CONFIG_LOCALSYSTEM L"LocalSystem" +#define IIS_CONFIG_LOCALSERVICE L"LocalService" +#define IIS_CONFIG_NETWORKSERVICE L"NetworkService" +#define IIS_CONFIG_SPECIFICUSER L"SpecificUser" +#define IIS_CONFIG_APPLICATIONPOOLIDENTITY L"ApplicationPoolIdentity" +#define IIS_CONFIG_USERNAME L"userName" +#define IIS_CONFIG_PASSWORD L"password" +#define IIS_CONFIG_CPU L"cpu" +#define IIS_CONFIG_LIMIT L"limit" +#define IIS_CONFIG_CPU_ACTION L"action" +#define IIS_CONFIG_KILLW3WP L"KillW3wp" +#define IIS_CONFIG_NOACTION L"NoAction" +#define IIS_CONFIG_RESETINTERVAL L"resetInterval" +#define IIS_CONFIG_MAXWRKPROCESSES L"maxProcesses" +#define IIS_CONFIG_HANDLERS_SECTION L"system.webServer/handlers" +#define IIS_CONFIG_DEFAULTDOC_SECTION L"system.webServer/defaultDocument" +#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp" +#define IIS_CONFIG_SCRIPTERROR L"scriptErrorSentToBrowser" +#define IIS_CONFIG_STATICCONTENT_SECTION L"system.webServer/staticContent" +#define IIS_CONFIG_HTTPEXPIRES L"httpExpires" +#define IIS_CONFIG_MAXAGE L"cacheControlMaxAge" +#define IIS_CONFIG_CLIENTCACHE L"clientCache" +#define IIS_CONFIG_CACHECONTROLMODE L"cacheControlMode" +#define IIS_CONFIG_USEMAXAGE L"UseMaxAge" +#define IIS_CONFIG_USEEXPIRES L"UseExpires" +#define IIS_CONFIG_CACHECUST L"cacheControlCustom" +#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp" +#define IIS_CONFIG_SESSION L"session" +#define IIS_CONFIG_ALLOWSTATE L"allowSessionState" +#define IIS_CONFIG_TIMEOUT L"timeout" +#define IIS_CONFIG_BUFFERING L"bufferingOn" +#define IIS_CONFIG_PARENTPATHS L"enableParentPaths" +#define IIS_CONFIG_SCRIPTLANG L"scriptLanguage" +#define IIS_CONFIG_SCRIPTTIMEOUT L"scriptTimeout" +#define IIS_CONFIG_LIMITS L"limits" +#define IIS_CONFIG_ALLOWDEBUG L"appAllowDebugging" +#define IIS_CONFIG_ALLOWCLIENTDEBUG L"appAllowClientDebug" +#define IIS_CONFIG_CERTIFICATEHASH L"certificateHash" +#define IIS_CONFIG_CERTIFICATESTORENAME L"certificateStoreName" +#define IIS_CONFIG_HTTPLOGGING_SECTION L"system.webServer/httpLogging" +#define IIS_CONFIG_DONTLOG L"dontLog" + +typedef BOOL (CALLBACK* ENUMAPHOSTELEMENTPROC)(IAppHostElement*, LPVOID); +typedef BOOL (CALLBACK* VARIANTCOMPARATORPROC)(VARIANT*, VARIANT*); + +HRESULT DAPI Iis7PutPropertyVariant( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in VARIANT vtPut + ); + +HRESULT DAPI Iis7PutPropertyInteger( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in DWORD dValue + ); + +HRESULT DAPI Iis7PutPropertyString( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in LPCWSTR wzString + ); + +HRESULT DAPI Iis7PutPropertyBool( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in BOOL fValue); + +HRESULT DAPI Iis7GetPropertyVariant( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in VARIANT* vtGet + ); + +HRESULT DAPI Iis7GetPropertyString( + __in IAppHostElement *pElement, + __in LPCWSTR wzPropName, + __in LPWSTR* psczGet + ); + +struct IIS7_APPHOSTELEMENTCOMPARISON +{ + LPCWSTR sczElementName; + LPCWSTR sczAttributeName; + VARIANT* pvAttributeValue; + VARIANTCOMPARATORPROC pComparator; +}; + +BOOL DAPI Iis7IsMatchingAppHostElement( + __in IAppHostElement *pElement, + __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison + ); + +HRESULT DAPI Iis7FindAppHostElementString( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in LPCWSTR wzAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7FindAppHostElementPath( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in LPCWSTR wzAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7FindAppHostElementInteger( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in DWORD dwAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7FindAppHostElementVariant( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR wzElementName, + __in LPCWSTR wzAttributeName, + __in VARIANT* pvAttributeValue, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7EnumAppHostElements( + __in IAppHostElementCollection *pCollection, + __in ENUMAPHOSTELEMENTPROC pCallback, + __in LPVOID pContext, + __out IAppHostElement** ppElement, + __out DWORD* pdwIndex + ); + +HRESULT DAPI Iis7FindAppHostMethod( + __in IAppHostMethodCollection *pCollection, + __in LPCWSTR wzMethodName, + __out IAppHostMethod** ppMethod, + __out DWORD* pdwIndex + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/inetutil.h b/src/libs/dutil/WixToolset.DUtil/inc/inetutil.h new file mode 100644 index 00000000..19ace88b --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/inetutil.h @@ -0,0 +1,39 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; } +#define ReleaseNullInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; } + + +// functions +HRESULT DAPI InternetGetSizeByHandle( + __in HINTERNET hiFile, + __out LONGLONG* pllSize + ); + +HRESULT DAPI InternetGetCreateTimeByHandle( + __in HINTERNET hiFile, + __out LPFILETIME pft + ); + +HRESULT DAPI InternetQueryInfoString( + __in HINTERNET h, + __in DWORD dwInfo, + __deref_out_z LPWSTR* psczValue + ); + +HRESULT DAPI InternetQueryInfoNumber( + __in HINTERNET h, + __in DWORD dwInfo, + __inout LONG* plInfo + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libs/dutil/WixToolset.DUtil/inc/iniutil.h b/src/libs/dutil/WixToolset.DUtil/inc/iniutil.h new file mode 100644 index 00000000..c8503155 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/iniutil.h @@ -0,0 +1,79 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseIni(ih) if (ih) { IniUninitialize(ih); } +#define ReleaseNullIni(ih) if (ih) { IniUninitialize(ih); ih = NULL; } + +typedef void* INI_HANDLE; +typedef const void* C_INI_HANDLE; + +extern const int INI_HANDLE_BYTES; + +struct INI_VALUE +{ + LPCWSTR wzName; + LPCWSTR wzValue; + + DWORD dwLineNumber; +}; + +HRESULT DAPI IniInitialize( + __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle + ); +void DAPI IniUninitialize( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle + ); +HRESULT DAPI IniSetOpenTag( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzOpenTagPrefix, + __in_z_opt LPCWSTR wzOpenTagPostfix + ); +HRESULT DAPI IniSetValueStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzValuePrefix, + __in_z_opt LPCWSTR wzValueSeparator + ); +HRESULT DAPI IniSetValueSeparatorException( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z LPCWSTR wzValueNamePrefix + ); +HRESULT DAPI IniSetCommentStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzLinePrefix + ); +HRESULT DAPI IniParse( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzPath, + __out_opt FILE_ENCODING *pfeEncodingFound + ); +// Gets the full value array, this includes values that may have been deleted +// (their value will be NULL) +HRESULT DAPI IniGetValueList( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues, + __out DWORD *pcValues + ); +HRESULT DAPI IniGetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __deref_out_z LPWSTR* psczValue + ); +HRESULT DAPI IniSetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __in_z_opt LPCWSTR wzValue + ); +HRESULT DAPI IniWriteFile( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzPath, + __in FILE_ENCODING feOverrideEncoding + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/jsonutil.h b/src/libs/dutil/WixToolset.DUtil/inc/jsonutil.h new file mode 100644 index 00000000..b05e9970 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/jsonutil.h @@ -0,0 +1,112 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum JSON_TOKEN +{ + JSON_TOKEN_NONE, + JSON_TOKEN_ARRAY_START, + JSON_TOKEN_ARRAY_VALUE, + JSON_TOKEN_ARRAY_END, + JSON_TOKEN_OBJECT_START, + JSON_TOKEN_OBJECT_KEY, + JSON_TOKEN_OBJECT_VALUE, + JSON_TOKEN_OBJECT_END, + JSON_TOKEN_VALUE, +} JSON_TOKEN; + +typedef struct _JSON_VALUE +{ +} JSON_VALUE; + +typedef struct _JSON_READER +{ + CRITICAL_SECTION cs; + LPWSTR sczJson; + + LPWSTR pwz; + JSON_TOKEN token; +} JSON_READER; + +typedef struct _JSON_WRITER +{ + CRITICAL_SECTION cs; + LPWSTR sczJson; + + JSON_TOKEN* rgTokenStack; + DWORD cTokens; + DWORD cMaxTokens; +} JSON_WRITER; + + +DAPI_(HRESULT) JsonInitializeReader( + __in_z LPCWSTR wzJson, + __in JSON_READER* pReader + ); + +DAPI_(void) JsonUninitializeReader( + __in JSON_READER* pReader + ); + +DAPI_(HRESULT) JsonReadNext( + __in JSON_READER* pReader, + __out JSON_TOKEN* pToken, + __out JSON_VALUE* pValue + ); + +DAPI_(HRESULT) JsonReadValue( + __in JSON_READER* pReader, + __in JSON_VALUE* pValue + ); + +DAPI_(HRESULT) JsonInitializeWriter( + __in JSON_WRITER* pWriter + ); + +DAPI_(void) JsonUninitializeWriter( + __in JSON_WRITER* pWriter + ); + +DAPI_(HRESULT) JsonWriteBool( + __in JSON_WRITER* pWriter, + __in BOOL fValue + ); + +DAPI_(HRESULT) JsonWriteNumber( + __in JSON_WRITER* pWriter, + __in DWORD dwValue + ); + +DAPI_(HRESULT) JsonWriteString( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzValue + ); + +DAPI_(HRESULT) JsonWriteArrayStart( + __in JSON_WRITER* pWriter + ); + +DAPI_(HRESULT) JsonWriteArrayEnd( + __in JSON_WRITER* pWriter + ); + +DAPI_(HRESULT) JsonWriteObjectStart( + __in JSON_WRITER* pWriter + ); + +DAPI_(HRESULT) JsonWriteObjectKey( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzKey + ); + +DAPI_(HRESULT) JsonWriteObjectEnd( + __in JSON_WRITER* pWriter + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/locutil.h b/src/libs/dutil/WixToolset.DUtil/inc/locutil.h new file mode 100644 index 00000000..38ddda20 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/locutil.h @@ -0,0 +1,120 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +struct LOC_STRING +{ + LPWSTR wzId; + LPWSTR wzText; + BOOL bOverridable; +}; + +const int LOC_CONTROL_NOT_SET = INT_MAX; + +struct LOC_CONTROL +{ + LPWSTR wzControl; + int nX; + int nY; + int nWidth; + int nHeight; + LPWSTR wzText; +}; + +const int WIX_LOCALIZATION_LANGUAGE_NOT_SET = INT_MAX; + +struct WIX_LOCALIZATION +{ + DWORD dwLangId; + + DWORD cLocStrings; + LOC_STRING* rgLocStrings; + + DWORD cLocControls; + LOC_CONTROL* rgLocControls; +}; + +/******************************************************************** + LocProbeForFile - Searches for a localization file on disk. + +*******************************************************************/ +HRESULT DAPI LocProbeForFile( + __in_z LPCWSTR wzBasePath, + __in_z LPCWSTR wzLocFileName, + __in_z_opt LPCWSTR wzLanguage, + __inout LPWSTR* psczPath + ); + +/******************************************************************** + LocLoadFromFile - Loads a localization file + +*******************************************************************/ +HRESULT DAPI LocLoadFromFile( + __in_z LPCWSTR wzWxlFile, + __out WIX_LOCALIZATION** ppWixLoc + ); + +/******************************************************************** + LocLoadFromResource - loads a localization file from a module's data + resource. + + NOTE: The resource data must be UTF-8 encoded. +*******************************************************************/ +HRESULT DAPI LocLoadFromResource( + __in HMODULE hModule, + __in_z LPCSTR szResource, + __out WIX_LOCALIZATION** ppWixLoc + ); + +/******************************************************************** + LocFree - free memory allocated when loading a localization file + +*******************************************************************/ +void DAPI LocFree( + __in_opt WIX_LOCALIZATION* pWixLoc + ); + +/******************************************************************** + LocLocalizeString - replace any #(loc.id) in a string with the + correct sub string +*******************************************************************/ +HRESULT DAPI LocLocalizeString( + __in const WIX_LOCALIZATION* pWixLoc, + __inout LPWSTR* psczInput + ); + +/******************************************************************** + LocGetControl - returns a control's localization information +*******************************************************************/ +HRESULT DAPI LocGetControl( + __in const WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __out LOC_CONTROL** ppLocControl + ); + +/******************************************************************** +LocGetString - returns a string's localization information +*******************************************************************/ +extern "C" HRESULT DAPI LocGetString( + __in const WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __out LOC_STRING** ppLocString + ); + +/******************************************************************** +LocAddString - adds a localization string +*******************************************************************/ +extern "C" HRESULT DAPI LocAddString( + __in WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __in_z LPCWSTR wzLocString, + __in BOOL bOverridable + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/logutil.h b/src/libs/dutil/WixToolset.DUtil/inc/logutil.h new file mode 100644 index 00000000..426506ee --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/logutil.h @@ -0,0 +1,192 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define LogExitOnFailureSource(d, x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } +#define LogExitOnRootFailureSource(d, x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } + +#define LogExitOnFailure(x, i, f, ...) LogExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, i, f, __VA_ARGS__) +#define LogExitOnRootFailure(x, i, f, ...) LogExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, i, f, __VA_ARGS__) + +typedef HRESULT (DAPI *PFN_LOGSTRINGWORKRAW)( + __in_z LPCSTR szString, + __in_opt LPVOID pvContext + ); + +// enums + +// structs + +// functions +BOOL DAPI IsLogInitialized(); + +BOOL DAPI IsLogOpen(); + +void DAPI LogInitialize( + __in HMODULE hModule + ); + +HRESULT DAPI LogOpen( + __in_z_opt LPCWSTR wzDirectory, + __in_z LPCWSTR wzLog, + __in_z_opt LPCWSTR wzPostfix, + __in_z_opt LPCWSTR wzExt, + __in BOOL fAppend, + __in BOOL fHeader, + __out_z_opt LPWSTR* psczLogPath + ); + +void DAPI LogDisable(); + +void DAPI LogRedirect( + __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw, + __in_opt LPVOID pvContext + ); + +HRESULT DAPI LogRename( + __in_z LPCWSTR wzNewPath + ); + +void DAPI LogClose( + __in BOOL fFooter + ); + +void DAPI LogUninitialize( + __in BOOL fFooter + ); + +BOOL DAPI LogIsOpen(); + +HRESULT DAPI LogSetSpecialParams( + __in_z_opt LPCWSTR wzSpecialBeginLine, + __in_z_opt LPCWSTR wzSpecialAfterTimeStamp, + __in_z_opt LPCWSTR wzSpecialEndLine + ); + +REPORT_LEVEL DAPI LogSetLevel( + __in REPORT_LEVEL rl, + __in BOOL fLogChange + ); + +REPORT_LEVEL DAPI LogGetLevel(); + +HRESULT DAPI LogGetPath( + __out_ecount_z(cchLogPath) LPWSTR pwzLogPath, + __in DWORD cchLogPath + ); + +HANDLE DAPI LogGetHandle(); + +HRESULT DAPIV LogString( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ); + +HRESULT DAPI LogStringArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + +HRESULT DAPIV LogStringLine( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ); + +HRESULT DAPI LogStringLineArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + +HRESULT DAPI LogIdModuleArgs( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + __in va_list args + ); + +/* + * Wraps LogIdModuleArgs, so inline to save the function call + */ + +inline HRESULT LogId( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, dwLogId); + hr = LogIdModuleArgs(rl, dwLogId, NULL, args); + va_end(args); + + return hr; +} + + +/* + * Wraps LogIdModuleArgs, so inline to save the function call + */ + +inline HRESULT LogIdArgs( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in va_list args + ) +{ + return LogIdModuleArgs(rl, dwLogId, NULL, args); +} + +HRESULT DAPIV LogErrorString( + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + ... + ); + +HRESULT DAPI LogErrorStringArgs( + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + +HRESULT DAPI LogErrorIdModule( + __in HRESULT hrError, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzString1, + __in_z_opt LPCWSTR wzString2, + __in_z_opt LPCWSTR wzString3 + ); + +inline HRESULT LogErrorId( + __in HRESULT hrError, + __in DWORD dwLogId, + __in_z_opt LPCWSTR wzString1 = NULL, + __in_z_opt LPCWSTR wzString2 = NULL, + __in_z_opt LPCWSTR wzString3 = NULL + ) +{ + return LogErrorIdModule(hrError, dwLogId, NULL, wzString1, wzString2, wzString3); +} + +HRESULT DAPI LogHeader(); + +HRESULT DAPI LogFooter(); + +HRESULT LogStringWorkRaw( + __in_z LPCSTR szLogData + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libs/dutil/WixToolset.DUtil/inc/memutil.h b/src/libs/dutil/WixToolset.DUtil/inc/memutil.h new file mode 100644 index 00000000..49f86e0a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/memutil.h @@ -0,0 +1,80 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseMem(p) if (p) { MemFree(p); } +#define ReleaseNullMem(p) if (p) { MemFree(p); p = NULL; } + +HRESULT DAPI MemInitialize(); +void DAPI MemUninitialize(); + +LPVOID DAPI MemAlloc( + __in SIZE_T cbSize, + __in BOOL fZero + ); +LPVOID DAPI MemReAlloc( + __in LPVOID pv, + __in SIZE_T cbSize, + __in BOOL fZero + ); +HRESULT DAPI MemReAllocSecure( + __in LPVOID pv, + __in SIZE_T cbSize, + __in BOOL fZero, + __deref_out LPVOID* ppvNew + ); +HRESULT DAPI MemAllocArray( + __inout LPVOID* ppvArray, + __in SIZE_T cbArrayType, + __in DWORD dwItemCount + ); +HRESULT DAPI MemReAllocArray( + __inout LPVOID* ppvArray, + __in DWORD cArray, + __in SIZE_T cbArrayType, + __in DWORD dwNewItemCount + ); +HRESULT DAPI MemEnsureArraySize( + __deref_inout_bcount(cArray * cbArrayType) LPVOID* ppvArray, + __in DWORD cArray, + __in SIZE_T cbArrayType, + __in DWORD dwGrowthCount + ); +HRESULT DAPI MemInsertIntoArray( + __deref_inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, + __in DWORD dwInsertIndex, + __in DWORD cInsertItems, + __in DWORD cExistingArray, + __in SIZE_T cbArrayType, + __in DWORD dwGrowthCount + ); +void DAPI MemRemoveFromArray( + __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, + __in DWORD dwRemoveIndex, + __in DWORD cRemoveItems, + __in DWORD cExistingArray, + __in SIZE_T cbArrayType, + __in BOOL fPreserveOrder + ); +void DAPI MemArraySwapItems( + __inout_bcount(cbArrayType) LPVOID pvArray, + __in DWORD dwIndex1, + __in DWORD dwIndex2, + __in SIZE_T cbArrayType + ); + +HRESULT DAPI MemFree( + __in LPVOID pv + ); +SIZE_T DAPI MemSize( + __in LPCVOID pv + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libs/dutil/WixToolset.DUtil/inc/metautil.h b/src/libs/dutil/WixToolset.DUtil/inc/metautil.h new file mode 100644 index 00000000..31f9ff5c --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/metautil.h @@ -0,0 +1,52 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// structs + +// prototypes +HRESULT DAPI MetaFindWebBase( + __in IMSAdminBaseW* piMetabase, + __in_z LPCWSTR wzIP, + __in int iPort, + __in_z LPCWSTR wzHeader, + __in BOOL fSecure, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ); +HRESULT DAPI MetaFindFreeWebBase( + __in IMSAdminBaseW* piMetabase, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ); + +HRESULT DAPI MetaOpenKey( + __in IMSAdminBaseW* piMetabase, + __in METADATA_HANDLE mhKey, + __in_z LPCWSTR wzKey, + __in DWORD dwAccess, + __in DWORD cRetries, + __out METADATA_HANDLE* pmh + ); +HRESULT DAPI MetaGetValue( + __in IMSAdminBaseW* piMetabase, + __in METADATA_HANDLE mhKey, + __in_z LPCWSTR wzKey, + __inout METADATA_RECORD* pmr + ); +void DAPI MetaFreeValue( + __in METADATA_RECORD* pmr + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/monutil.h b/src/libs/dutil/WixToolset.DUtil/inc/monutil.h new file mode 100644 index 00000000..f644e205 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/monutil.h @@ -0,0 +1,108 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseMon(mh) if (mh) { MonDestroy(mh); } +#define ReleaseNullMon(mh) if (mh) { MonDestroy(mh); mh = NULL; } + +typedef void* MON_HANDLE; +typedef const void* C_MON_HANDLE; + +// Defined in regutil.h +enum REG_KEY_BITNESS; + +extern const int MON_HANDLE_BYTES; + +// Note: callbacks must be implemented in a thread-safe manner. They will be called asynchronously by a MonUtil-spawned thread. +// They must also be written to return as soon as possible - they are called from the waiter thread +typedef void (*PFN_MONGENERAL)( + __in HRESULT hr, + __in_opt LPVOID pvContext + ); +// This callback is not specific to any wait - it will notify client of any drive status changes, such as removable drive insertion / removal +typedef void (*PFN_MONDRIVESTATUS)( + __in WCHAR chDrive, + __in BOOL fArriving, + __in_opt LPVOID pvContext + ); +// Note if these fire with a failed result it means an error has occurred with the wait, and so the wait will stay in the list and be retried. When waits start succeeding again, +// MonUtil will notify of changes, because it may have not noticed changes during the interval for which the wait had failed. This behavior can result in false positive notifications, +// so all consumers of MonUtil should be designed with this in mind. +typedef void (*PFN_MONDIRECTORY)( + __in HRESULT hr, + __in_z LPCWSTR wzPath, + __in BOOL fRecursive, + __in_opt LPVOID pvContext, + __in_opt LPVOID pvDirectoryContext + ); +typedef void (*PFN_MONREGKEY)( + __in HRESULT hr, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive, + __in_opt LPVOID pvContext, + __in_opt LPVOID pvRegKeyContext + ); + +// Silence period allows you to avoid lots of notifications when a lot of writes are going on in a directory +// MonUtil will wait until the directory has been "silent" for at least dwSilencePeriodInMs milliseconds +// The drawback to setting this to a value higher than zero is that even single write notifications +// are delayed by this amount +HRESULT DAPI MonCreate( + __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle, + __in PFN_MONGENERAL vpfMonGeneral, + __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus, + __in_opt PFN_MONDIRECTORY vpfMonDirectory, + __in_opt PFN_MONREGKEY vpfMonRegKey, + __in_opt LPVOID pvContext + ); +// Don't add multiple identical waits! Not only is it wasteful and will cause multiple fires for the exact same change, it will also +// result in slightly odd behavior when you remove a duplicated wait (removing a wait may or may not remove multiple waits) +// This is due to the way coordinator thread and waiter threads handle removing, and while it is possible to solve, doing so would complicate the code. +// So instead, de-dupe your wait requests before sending them to MonUtil. +// Special notes for network waits: MonUtil can send false positive notifications (i.e. notifications when nothing had changed) if connection +// to the share is lost and reconnected, because MonUtil can't know for sure whether changes occurred while the connection was lost. +// Also, MonUtil will very every 20 minutes retry even successful network waits, because the underlying Win32 API cannot notify us if a remote server +// had its network cable unplugged or similar sudden failure. When we retry the successful network waits, we will also send a false positive notification, +// because it's impossible for MonUtil to detect if we're reconnecting to a server that had died and come back to life, or if we're reconnecting to a server that had +// been up all along. For both of the above reasons, clients of MonUtil must be written to do very, very little work in the case of false positive network waits. +HRESULT DAPI MonAddDirectory( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in_z LPCWSTR wzPath, + __in BOOL fRecursive, + __in DWORD dwSilencePeriodInMs, + __in_opt LPVOID pvDirectoryContext + ); +HRESULT DAPI MonAddRegKey( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive, + __in DWORD dwSilencePeriodInMs, + __in_opt LPVOID pvRegKeyContext + ); +HRESULT DAPI MonRemoveDirectory( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in_z LPCWSTR wzPath, + __in BOOL fRecursive + ); +HRESULT DAPI MonRemoveRegKey( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive + ); +void DAPI MonDestroy( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/osutil.h b/src/libs/dutil/WixToolset.DUtil/inc/osutil.h new file mode 100644 index 00000000..2cce6f63 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/osutil.h @@ -0,0 +1,42 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum OS_VERSION +{ + OS_VERSION_UNKNOWN, + OS_VERSION_WINNT, + OS_VERSION_WIN2000, + OS_VERSION_WINXP, + OS_VERSION_WIN2003, + OS_VERSION_VISTA, + OS_VERSION_WIN2008, + OS_VERSION_WIN7, + OS_VERSION_WIN2008_R2, + OS_VERSION_FUTURE +} OS_VERSION; + +void DAPI OsGetVersion( + __out OS_VERSION* pVersion, + __out DWORD* pdwServicePack + ); +HRESULT DAPI OsCouldRunPrivileged( + __out BOOL* pfPrivileged + ); +HRESULT DAPI OsIsRunningPrivileged( + __out BOOL* pfPrivileged + ); +HRESULT DAPI OsIsUacEnabled( + __out BOOL* pfUacEnabled + ); +HRESULT DAPI OsRtlGetVersion( + __inout RTL_OSVERSIONINFOEXW* pOvix + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h new file mode 100644 index 00000000..579b8454 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h @@ -0,0 +1,252 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum PATH_EXPAND +{ + PATH_EXPAND_ENVIRONMENT = 0x0001, + PATH_EXPAND_FULLPATH = 0x0002, +} PATH_EXPAND; + + +/******************************************************************* + PathCommandLineAppend - appends a command line argument on to a + string such that ::CommandLineToArgv() will shred them correctly + (i.e. quote arguments with spaces in them). +********************************************************************/ +DAPI_(HRESULT) PathCommandLineAppend( + __deref_inout_z LPWSTR* psczCommandLine, + __in_z LPCWSTR wzArgument + ); + +/******************************************************************* + PathFile - returns a pointer to the file part of the path. +********************************************************************/ +DAPI_(LPWSTR) PathFile( + __in_z LPCWSTR wzPath + ); + +/******************************************************************* + PathExtension - returns a pointer to the extension part of the path + (including the dot). +********************************************************************/ +DAPI_(LPCWSTR) PathExtension( + __in_z LPCWSTR wzPath + ); + +/******************************************************************* + PathGetDirectory - extracts the directory from a path. +********************************************************************/ +DAPI_(HRESULT) PathGetDirectory( + __in_z LPCWSTR wzPath, + __out_z LPWSTR *psczDirectory + ); + +/******************************************************************* +PathGetParentPath - extracts the parent directory from a full path. +********************************************************************/ +DAPI_(HRESULT) PathGetParentPath( + __in_z LPCWSTR wzPath, + __out_z LPWSTR *psczDirectory + ); + +/******************************************************************* + PathExpand - gets the full path to a file resolving environment + variables along the way. +********************************************************************/ +DAPI_(HRESULT) PathExpand( + __out LPWSTR *psczFullPath, + __in_z LPCWSTR wzRelativePath, + __in DWORD dwResolveFlags + ); + +/******************************************************************* + PathPrefix - prefixes a full path with \\?\ or \\?\UNC as + appropriate. +********************************************************************/ +DAPI_(HRESULT) PathPrefix( + __inout LPWSTR *psczFullPath + ); + +/******************************************************************* + PathFixedBackslashTerminate - appends a \ if path does not have it + already, but fails if the buffer is + insufficient. +********************************************************************/ +DAPI_(HRESULT) PathFixedBackslashTerminate( + __inout_ecount_z(cchPath) LPWSTR wzPath, + __in SIZE_T cchPath + ); + +/******************************************************************* + PathBackslashTerminate - appends a \ if path does not have it + already. +********************************************************************/ +DAPI_(HRESULT) PathBackslashTerminate( + __inout LPWSTR* psczPath + ); + +/******************************************************************* + PathForCurrentProcess - gets the full path to the currently executing + process or (optionally) a module inside the process. +********************************************************************/ +DAPI_(HRESULT) PathForCurrentProcess( + __inout LPWSTR *psczFullPath, + __in_opt HMODULE hModule + ); + +/******************************************************************* + PathRelativeToModule - gets the name of a file in the same + directory as the current process or (optionally) a module inside + the process +********************************************************************/ +DAPI_(HRESULT) PathRelativeToModule( + __inout LPWSTR *psczFullPath, + __in_opt LPCWSTR wzFileName, + __in_opt HMODULE hModule + ); + +/******************************************************************* + PathCreateTempFile + + Note: if wzDirectory is null, ::GetTempPath() will be used instead. + if wzFileNameTemplate is null, GetTempFileName() will be used instead. +*******************************************************************/ +DAPI_(HRESULT) PathCreateTempFile( + __in_opt LPCWSTR wzDirectory, + __in_opt __format_string LPCWSTR wzFileNameTemplate, + __in DWORD dwUniqueCount, + __in DWORD dwFileAttributes, + __out_opt LPWSTR* psczTempFile, + __out_opt HANDLE* phTempFile + ); + +/******************************************************************* + PathCreateTimeBasedTempFile - creates an empty temp file based on current + system time +********************************************************************/ +DAPI_(HRESULT) PathCreateTimeBasedTempFile( + __in_z_opt LPCWSTR wzDirectory, + __in_z LPCWSTR wzPrefix, + __in_z_opt LPCWSTR wzPostfix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* psczTempFile, + __out_opt HANDLE* phTempFile + ); + +/******************************************************************* + PathCreateTempDirectory + + Note: if wzDirectory is null, ::GetTempPath() will be used instead. +*******************************************************************/ +DAPI_(HRESULT) PathCreateTempDirectory( + __in_opt LPCWSTR wzDirectory, + __in __format_string LPCWSTR wzDirectoryNameTemplate, + __in DWORD dwUniqueCount, + __out LPWSTR* psczTempDirectory + ); + +/******************************************************************* + PathGetKnownFolder - returns the path to a well-known shell folder + +*******************************************************************/ +DAPI_(HRESULT) PathGetKnownFolder( + __in int csidl, + __out LPWSTR* psczKnownFolder + ); + +/******************************************************************* + PathIsAbsolute - returns true if the path is absolute; false + otherwise. +*******************************************************************/ +DAPI_(BOOL) PathIsAbsolute( + __in_z LPCWSTR wzPath + ); + +/******************************************************************* + PathConcat - like .NET's Path.Combine, lets you build up a path + one piece -- file or directory -- at a time. +*******************************************************************/ +DAPI_(HRESULT) PathConcat( + __in_opt LPCWSTR wzPath1, + __in_opt LPCWSTR wzPath2, + __deref_out_z LPWSTR* psczCombined + ); + +/******************************************************************* + PathConcatCch - like .NET's Path.Combine, lets you build up a path + one piece -- file or directory -- at a time. +*******************************************************************/ +DAPI_(HRESULT) PathConcatCch( + __in_opt LPCWSTR wzPath1, + __in SIZE_T cchPath1, + __in_opt LPCWSTR wzPath2, + __in SIZE_T cchPath2, + __deref_out_z LPWSTR* psczCombined + ); + +/******************************************************************* + PathEnsureQuoted - ensures that a path is quoted; optionally, + this function also terminates a directory with a backslash + if it is not already. +*******************************************************************/ +DAPI_(HRESULT) PathEnsureQuoted( + __inout LPWSTR* ppszPath, + __in BOOL fDirectory + ); + +/******************************************************************* + PathCompare - compares the fully expanded path of the two paths using + ::CompareStringW(). +*******************************************************************/ +DAPI_(HRESULT) PathCompare( + __in_z LPCWSTR wzPath1, + __in_z LPCWSTR wzPath2, + __out int* pnResult + ); + +/******************************************************************* + PathCompress - sets the compression state on an existing file or + directory. A no-op on file systems that don't + support compression. +*******************************************************************/ +DAPI_(HRESULT) PathCompress( + __in_z LPCWSTR wzPath + ); + +/******************************************************************* + PathGetHierarchyArray - allocates an array containing, + in order, every parent directory of the specified path, + ending with the actual input path + This function also works with registry subkeys +*******************************************************************/ +DAPI_(HRESULT) PathGetHierarchyArray( + __in_z LPCWSTR wzPath, + __deref_inout_ecount_opt(*pcPathArray) LPWSTR **prgsczPathArray, + __inout LPUINT pcPathArray + ); + +/******************************************************************* + PathCanonicalizePath - wrapper around PathCanonicalizeW. +*******************************************************************/ +DAPI_(HRESULT) PathCanonicalizePath( + __in_z LPCWSTR wzPath, + __deref_out_z LPWSTR* psczCanonicalized + ); + +/******************************************************************* +PathDirectoryContainsPath - checks if wzPath is located inside + wzDirectory. +*******************************************************************/ +DAPI_(HRESULT) PathDirectoryContainsPath( + __in_z LPCWSTR wzDirectory, + __in_z LPCWSTR wzPath + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/perfutil.h b/src/libs/dutil/WixToolset.DUtil/inc/perfutil.h new file mode 100644 index 00000000..7557511d --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/perfutil.h @@ -0,0 +1,24 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// structs + + +// functions +void DAPI PerfInitialize( + ); +void DAPI PerfClickTime( + __out_opt LARGE_INTEGER* pliElapsed + ); +double DAPI PerfConvertToSeconds( + __in const LARGE_INTEGER* pli + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/polcutil.h b/src/libs/dutil/WixToolset.DUtil/inc/polcutil.h new file mode 100644 index 00000000..13618043 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/polcutil.h @@ -0,0 +1,39 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +const LPCWSTR POLICY_BURN_REGISTRY_PATH = L"WiX\\Burn"; + +/******************************************************************** +PolcReadNumber - reads a number from policy. + +NOTE: S_FALSE returned if policy not set. +NOTE: out is set to default on S_FALSE or any error. +********************************************************************/ +HRESULT DAPI PolcReadNumber( + __in_z LPCWSTR wzPolicyPath, + __in_z LPCWSTR wzPolicyName, + __in DWORD dwDefault, + __out DWORD* pdw + ); + +/******************************************************************** +PolcReadString - reads a string from policy. + +NOTE: S_FALSE returned if policy not set. +NOTE: out is set to default on S_FALSE or any error. +********************************************************************/ +HRESULT DAPI PolcReadString( + __in_z LPCWSTR wzPolicyPath, + __in_z LPCWSTR wzPolicyName, + __in_z_opt LPCWSTR wzDefault, + __deref_out_z LPWSTR* pscz + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/procutil.h b/src/libs/dutil/WixToolset.DUtil/inc/procutil.h new file mode 100644 index 00000000..00f3f358 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/procutil.h @@ -0,0 +1,75 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// structs +typedef struct _PROC_FILESYSTEMREDIRECTION +{ + BOOL fDisabled; + LPVOID pvRevertState; +} PROC_FILESYSTEMREDIRECTION; + +HRESULT DAPI ProcElevated( + __in HANDLE hProcess, + __out BOOL* pfElevated + ); + +HRESULT DAPI ProcWow64( + __in HANDLE hProcess, + __out BOOL* pfWow64 + ); +HRESULT DAPI ProcDisableWowFileSystemRedirection( + __in PROC_FILESYSTEMREDIRECTION* pfsr + ); +HRESULT DAPI ProcRevertWowFileSystemRedirection( + __in PROC_FILESYSTEMREDIRECTION* pfsr + ); + +HRESULT DAPI ProcExec( + __in_z LPCWSTR wzExecutablePath, + __in_z_opt LPCWSTR wzCommandLine, + __in int nCmdShow, + __out HANDLE *phProcess + ); +HRESULT DAPI ProcExecute( + __in_z LPWSTR wzCommand, + __out HANDLE *phProcess, + __out_opt HANDLE *phChildStdIn, + __out_opt HANDLE *phChildStdOutErr + ); +HRESULT DAPI ProcWaitForCompletion( + __in HANDLE hProcess, + __in DWORD dwTimeout, + __out DWORD *pReturnCode + ); +HRESULT DAPI ProcWaitForIds( + __in_ecount(cProcessIds) const DWORD* pdwProcessIds, + __in DWORD cProcessIds, + __in DWORD dwMilliseconds + ); +HRESULT DAPI ProcCloseIds( + __in_ecount(cProcessIds) const DWORD* pdwProcessIds, + __in DWORD cProcessIds + ); + +// following code in proc2utl.cpp due to dependency on PSAPI.DLL. +HRESULT DAPI ProcFindAllIdsFromExeName( + __in_z LPCWSTR wzExeName, + __out DWORD** ppdwProcessIds, + __out DWORD* pcProcessIds + ); + +// following code in proc3utl.cpp due to dependency on Wtsapi32.DLL. +HRESULT DAPI ProcExecuteAsInteractiveUser( + __in_z LPCWSTR wzExecutablePath, + __in_z LPCWSTR wzCommand, + __out HANDLE *phProcess + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/regutil.h b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h new file mode 100644 index 00000000..75284940 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h @@ -0,0 +1,246 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ReleaseRegKey(h) if (h) { ::RegCloseKey(h); h = NULL; } + +typedef enum REG_KEY_BITNESS +{ + REG_KEY_DEFAULT = 0, + REG_KEY_32BIT = 1, + REG_KEY_64BIT = 2 +} REG_KEY_BITNESS; + +typedef LSTATUS (APIENTRY *PFN_REGCREATEKEYEXW)( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __reserved DWORD Reserved, + __in_opt LPWSTR lpClass, + __in DWORD dwOptions, + __in REGSAM samDesired, + __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, + __out PHKEY phkResult, + __out_opt LPDWORD lpdwDisposition + ); +typedef LSTATUS (APIENTRY *PFN_REGOPENKEYEXW)( + __in HKEY hKey, + __in_opt LPCWSTR lpSubKey, + __reserved DWORD ulOptions, + __in REGSAM samDesired, + __out PHKEY phkResult + ); +typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYEXW)( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __in REGSAM samDesired, + __reserved DWORD Reserved + ); +typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYW)( + __in HKEY hKey, + __in LPCWSTR lpSubKey + ); +typedef LSTATUS (APIENTRY *PFN_REGENUMKEYEXW)( + __in HKEY hKey, + __in DWORD dwIndex, + __out LPWSTR lpName, + __inout LPDWORD lpcName, + __reserved LPDWORD lpReserved, + __inout_opt LPWSTR lpClass, + __inout_opt LPDWORD lpcClass, + __out_opt PFILETIME lpftLastWriteTime + ); +typedef LSTATUS (APIENTRY *PFN_REGENUMVALUEW)( + __in HKEY hKey, + __in DWORD dwIndex, + __out LPWSTR lpValueName, + __inout LPDWORD lpcchValueName, + __reserved LPDWORD lpReserved, + __out_opt LPDWORD lpType, + __out_opt LPBYTE lpData, + __out_opt LPDWORD lpcbData + ); +typedef LSTATUS (APIENTRY *PFN_REGQUERYINFOKEYW)( + __in HKEY hKey, + __out_opt LPWSTR lpClass, + __inout_opt LPDWORD lpcClass, + __reserved LPDWORD lpReserved, + __out_opt LPDWORD lpcSubKeys, + __out_opt LPDWORD lpcMaxSubKeyLen, + __out_opt LPDWORD lpcMaxClassLen, + __out_opt LPDWORD lpcValues, + __out_opt LPDWORD lpcMaxValueNameLen, + __out_opt LPDWORD lpcMaxValueLen, + __out_opt LPDWORD lpcbSecurityDescriptor, + __out_opt PFILETIME lpftLastWriteTime + ); +typedef LSTATUS (APIENTRY *PFN_REGQUERYVALUEEXW)( + __in HKEY hKey, + __in_opt LPCWSTR lpValueName, + __reserved LPDWORD lpReserved, + __out_opt LPDWORD lpType, + __out_bcount_part_opt(*lpcbData, *lpcbData) __out_data_source(REGISTRY) LPBYTE lpData, + __inout_opt LPDWORD lpcbData + ); +typedef LSTATUS (APIENTRY *PFN_REGSETVALUEEXW)( + __in HKEY hKey, + __in_opt LPCWSTR lpValueName, + __reserved DWORD Reserved, + __in DWORD dwType, + __in_bcount_opt(cbData) CONST BYTE* lpData, + __in DWORD cbData + ); +typedef LSTATUS (APIENTRY *PFN_REGDELETEVALUEW)( + __in HKEY hKey, + __in_opt LPCWSTR lpValueName + ); + +HRESULT DAPI RegInitialize(); +void DAPI RegUninitialize(); + +void DAPI RegFunctionOverride( + __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW, + __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW, + __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW, + __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW, + __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW, + __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, + __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, + __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, + __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW + ); +HRESULT DAPI RegCreate( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __out HKEY* phk + ); +HRESULT DAPI RegCreateEx( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __in BOOL fVolatile, + __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes, + __out HKEY* phk, + __out_opt BOOL* pfCreated + ); +HRESULT DAPI RegOpen( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __out HKEY* phk + ); +HRESULT DAPI RegDelete( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fDeleteTree + ); +HRESULT DAPI RegKeyEnum( + __in HKEY hk, + __in DWORD dwIndex, + __deref_out_z LPWSTR* psczKey + ); +HRESULT DAPI RegValueEnum( + __in HKEY hk, + __in DWORD dwIndex, + __deref_out_z LPWSTR* psczName, + __out_opt DWORD *pdwType + ); +HRESULT DAPI RegGetType( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD *pdwType + ); +HRESULT DAPI RegReadBinary( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, + __out SIZE_T *pcbBuffer + ); +HRESULT DAPI RegReadString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_z LPWSTR* psczValue + ); +HRESULT DAPI RegReadStringArray( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings, + __out DWORD *pcStrings + ); +HRESULT DAPI RegReadVersion( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD64* pdw64Version + ); +HRESULT DAPI RegReadNumber( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD* pdwValue + ); +HRESULT DAPI RegReadQword( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD64* pqwValue + ); +HRESULT DAPI RegWriteBinary( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_bcount(cbBuffer) const BYTE *pbBuffer, + __in DWORD cbBuffer + ); +HRESULT DAPI RegWriteString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue + ); +HRESULT DAPI RegWriteStringArray( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_ecount(cStrings) LPWSTR *rgwzStrings, + __in DWORD cStrings + ); +HRESULT DAPI RegWriteStringFormatted( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in __format_string LPCWSTR szFormat, + ... + ); +HRESULT DAPI RegWriteNumber( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in DWORD dwValue + ); +HRESULT DAPI RegWriteQword( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in DWORD64 qwValue + ); +HRESULT DAPI RegQueryKey( + __in HKEY hk, + __out_opt DWORD* pcSubKeys, + __out_opt DWORD* pcValues + ); +HRESULT DAPI RegKeyReadNumber( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit, + __out DWORD* pdwValue + ); +BOOL DAPI RegValueExists( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libs/dutil/WixToolset.DUtil/inc/resrutil.h b/src/libs/dutil/WixToolset.DUtil/inc/resrutil.h new file mode 100644 index 00000000..1f4d8e17 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/resrutil.h @@ -0,0 +1,43 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI ResGetStringLangId( + __in_opt LPCWSTR wzPath, + __in UINT uID, + __out WORD *pwLangId + ); + +HRESULT DAPI ResReadString( + __in HINSTANCE hinst, + __in UINT uID, + __deref_out_z LPWSTR* ppwzString + ); + +HRESULT DAPI ResReadStringAnsi( + __in HINSTANCE hinst, + __in UINT uID, + __deref_out_z LPSTR* ppszString + ); + +HRESULT DAPI ResReadData( + __in_opt HINSTANCE hinst, + __in_z LPCSTR szDataName, + __deref_out_bcount(*pcb) PVOID *ppv, + __out DWORD *pcb + ); + +HRESULT DAPI ResExportDataToFile( + __in_z LPCSTR szDataName, + __in_z LPCWSTR wzTargetFile, + __in DWORD dwCreationDisposition + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libs/dutil/WixToolset.DUtil/inc/reswutil.h b/src/libs/dutil/WixToolset.DUtil/inc/reswutil.h new file mode 100644 index 00000000..31435ae2 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/reswutil.h @@ -0,0 +1,31 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI ResWriteString( + __in_z LPCWSTR wzResourceFile, + __in DWORD dwDataId, + __in_z LPCWSTR wzData, + __in WORD wLangId + ); + +HRESULT DAPI ResWriteData( + __in_z LPCWSTR wzResourceFile, + __in_z LPCSTR szDataName, + __in PVOID pData, + __in DWORD cbData + ); + +HRESULT DAPI ResImportDataFromFile( + __in_z LPCWSTR wzTargetFile, + __in_z LPCWSTR wzSourceFile, + __in_z LPCSTR szDataName + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/rexutil.h b/src/libs/dutil/WixToolset.DUtil/inc/rexutil.h new file mode 100644 index 00000000..77f5604a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/rexutil.h @@ -0,0 +1,54 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// defines +#define FILETABLESIZE 40 + +// structs +struct MEM_FILE +{ + LPCBYTE vpStart; + UINT uiCurrent; + UINT uiLength; +}; + +typedef enum FAKE_FILE_TYPE { NORMAL_FILE, MEMORY_FILE } FAKE_FILE_TYPE; + +typedef HRESULT (*REX_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext); +typedef VOID (*REX_CALLBACK_WRITE)(UINT cb); + + +struct FAKE_FILE // used __in internal file table +{ + BOOL fUsed; + FAKE_FILE_TYPE fftType; + MEM_FILE mfFile; // State for memory file + HANDLE hFile; // Handle for disk file +}; + +// functions +HRESULT RexInitialize(); +void RexUninitialize(); + +HRESULT RexExtract( + __in_z LPCSTR szResource, + __in_z LPCWSTR wzExtractId, + __in_z LPCWSTR wzExtractDir, + __in_z LPCWSTR wzExtractName, + __in REX_CALLBACK_PROGRESS pfnProgress, + __in REX_CALLBACK_WRITE pfnWrite, + __in LPVOID pvContext + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libs/dutil/WixToolset.DUtil/inc/rmutil.h b/src/libs/dutil/WixToolset.DUtil/inc/rmutil.h new file mode 100644 index 00000000..ce7bf254 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/rmutil.h @@ -0,0 +1,46 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _RMU_SESSION *PRMU_SESSION; + +HRESULT DAPI RmuJoinSession( + __out PRMU_SESSION *ppSession, + __in_z LPCWSTR wzSessionKey + ); + +HRESULT DAPI RmuAddFile( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzPath + ); + +HRESULT DAPI RmuAddProcessById( + __in PRMU_SESSION pSession, + __in DWORD dwProcessId + ); + +HRESULT DAPI RmuAddProcessesByName( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzProcessName + ); + +HRESULT DAPI RmuAddService( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzServiceName + ); + +HRESULT DAPI RmuRegisterResources( + __in PRMU_SESSION pSession + ); + +HRESULT DAPI RmuEndSession( + __in PRMU_SESSION pSession + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/rssutil.h b/src/libs/dutil/WixToolset.DUtil/inc/rssutil.h new file mode 100644 index 00000000..064ab147 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/rssutil.h @@ -0,0 +1,89 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseRssChannel(p) if (p) { RssFreeChannel(p); } +#define ReleaseNullRssChannel(p) if (p) { RssFreeChannel(p); p = NULL; } + + +struct RSS_UNKNOWN_ATTRIBUTE +{ + LPWSTR wzNamespace; + LPWSTR wzAttribute; + LPWSTR wzValue; + + RSS_UNKNOWN_ATTRIBUTE* pNext; +}; + +struct RSS_UNKNOWN_ELEMENT +{ + LPWSTR wzNamespace; + LPWSTR wzElement; + LPWSTR wzValue; + + RSS_UNKNOWN_ATTRIBUTE* pAttributes; + RSS_UNKNOWN_ELEMENT* pNext; +}; + +struct RSS_ITEM +{ + LPWSTR wzTitle; + LPWSTR wzLink; + LPWSTR wzDescription; + + LPWSTR wzGuid; + FILETIME ftPublished; + + LPWSTR wzEnclosureUrl; + DWORD dwEnclosureSize; + LPWSTR wzEnclosureType; + + RSS_UNKNOWN_ELEMENT* pUnknownElements; +}; + +struct RSS_CHANNEL +{ + LPWSTR wzTitle; + LPWSTR wzLink; + LPWSTR wzDescription; + DWORD dwTimeToLive; + + RSS_UNKNOWN_ELEMENT* pUnknownElements; + + DWORD cItems; + RSS_ITEM rgItems[1]; +}; + +HRESULT DAPI RssInitialize( + ); + +void DAPI RssUninitialize( + ); + +HRESULT DAPI RssParseFromString( + __in_z LPCWSTR wzRssString, + __out RSS_CHANNEL **ppChannel + ); + +HRESULT DAPI RssParseFromFile( + __in_z LPCWSTR wzRssFile, + __out RSS_CHANNEL **ppChannel + ); + +// Adding this until we have the updated specstrings.h +#ifndef __in_xcount +#define __in_xcount(size) +#endif + +void DAPI RssFreeChannel( + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libs/dutil/WixToolset.DUtil/inc/sceutil.h b/src/libs/dutil/WixToolset.DUtil/inc/sceutil.h new file mode 100644 index 00000000..9d14eecf --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/sceutil.h @@ -0,0 +1,273 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +typedef void* SCE_DATABASE_HANDLE; +typedef void* SCE_ROW_HANDLE; +typedef void* SCE_QUERY_HANDLE; +typedef void* SCE_QUERY_RESULTS_HANDLE; + +extern const int SCE_ROW_HANDLE_BYTES; +extern const int SCE_QUERY_HANDLE_BYTES; +extern const int SCE_QUERY_RESULTS_HANDLE_BYTES; + +#define ReleaseSceRow(prrh) if (prrh) { SceFreeRow(prrh); } +#define ReleaseNullSceRow(prrh) if (prrh) { SceFreeRow(prrh); prrh = NULL; } +#define ReleaseSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); } +#define ReleaseNullSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); pqh = NULL; } +#define ReleaseSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); } +#define ReleaseNullSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); pqh = NULL; } + +struct SCE_COLUMN_SCHEMA +{ + LPCWSTR wzName; + DBTYPE dbtColumnType; + DWORD dwLength; + BOOL fPrimaryKey; // If this column is the primary key + BOOL fNullable; + BOOL fAutoIncrement; + BOOL fDescending; // If this column should be descending when used in an index (default is ascending) + + LPWSTR wzRelationName; + DWORD dwForeignKeyTable; + DWORD dwForeignKeyColumn; +}; + +struct SCE_INDEX_SCHEMA +{ + LPWSTR wzName; + + DWORD *rgColumns; + DWORD cColumns; +}; + +struct SCE_TABLE_SCHEMA +{ + LPCWSTR wzName; + DWORD cColumns; + SCE_COLUMN_SCHEMA *rgColumns; + + DWORD cIndexes; + SCE_INDEX_SCHEMA *rgIndexes; + + // Internal to SCEUtil - consumers shouldn't access or modify + // TODO: enforce / hide in a handle of some sort? + IRowset *pIRowset; + IRowsetChange *pIRowsetChange; +}; + +struct SCE_DATABASE_SCHEMA +{ + DWORD cTables; + SCE_TABLE_SCHEMA *rgTables; +}; + +struct SCE_DATABASE +{ + SCE_DATABASE_HANDLE sdbHandle; + SCE_DATABASE_SCHEMA *pdsSchema; +}; + +HRESULT DAPI SceCreateDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __deref_out SCE_DATABASE **ppDatabase + ); +HRESULT DAPI SceOpenDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __in LPCWSTR wzSchemaType, + __in DWORD dwExpectedVersion, + __deref_out SCE_DATABASE **ppDatabase, + __in BOOL fReadOnly + ); +HRESULT DAPI SceEnsureDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __in LPCWSTR wzSchemaType, + __in DWORD dwExpectedVersion, + __in SCE_DATABASE_SCHEMA *pdsSchema, + __deref_out SCE_DATABASE **ppDatabase + ); +HRESULT DAPI SceIsTableEmpty( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out BOOL *pfEmpty + ); +HRESULT DAPI SceGetFirstRow( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI SceGetNextRow( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI SceBeginTransaction( + __in SCE_DATABASE *pDatabase + ); +HRESULT DAPI SceCommitTransaction( + __in SCE_DATABASE *pDatabase + ); +HRESULT DAPI SceRollbackTransaction( + __in SCE_DATABASE *pDatabase + ); +HRESULT DAPI SceDeleteRow( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI ScePrepareInsert( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI SceFinishUpdate( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle + ); +HRESULT DAPI SceSetColumnBinary( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ); +HRESULT DAPI SceSetColumnDword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const DWORD dwValue + ); +HRESULT DAPI SceSetColumnQword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const DWORD64 qwValue + ); +HRESULT DAPI SceSetColumnBool( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const BOOL fValue + ); +HRESULT DAPI SceSetColumnString( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in_z_opt LPCWSTR wzValue + ); +HRESULT DAPI SceSetColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const SYSTEMTIME *pst + ); +HRESULT DAPI SceSetColumnNull( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex + ); +HRESULT DAPI SceGetColumnBinary( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out_opt BYTE **ppbBuffer, + __inout SIZE_T *pcbBuffer + ); +HRESULT DAPI SceGetColumnDword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out DWORD *pdwValue + ); +HRESULT DAPI SceGetColumnQword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out DWORD64 *pqwValue + ); +HRESULT DAPI SceGetColumnBool( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out BOOL *pfValue + ); +HRESULT DAPI SceGetColumnString( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out_z LPWSTR *psczValue + ); +HRESULT DAPI SceGetColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out SYSTEMTIME *pst + ); +HRESULT DAPI SceBeginQuery( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __in DWORD dwIndex, + __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle + ); +HRESULT DAPI SceSetQueryColumnBinary( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ); +HRESULT DAPI SceSetQueryColumnDword( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const DWORD dwValue + ); +HRESULT DAPI SceSetQueryColumnQword( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const DWORD64 qwValue + ); +HRESULT DAPI SceSetQueryColumnBool( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const BOOL fValue + ); +HRESULT DAPI SceSetQueryColumnString( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in_z_opt LPCWSTR wzString + ); +HRESULT DAPI SceSetQueryColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in const SYSTEMTIME *pst + ); +HRESULT DAPI SceSetQueryColumnEmpty( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle + ); +HRESULT DAPI SceRunQueryExact( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +HRESULT DAPI SceRunQueryRange( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, + __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle + ); +HRESULT DAPI SceGetNextResultRow( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle, + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ); +void DAPI SceCloseTable( + __in SCE_TABLE_SCHEMA *pTable + ); +// Returns whether the data in the database changed. Ignores schema changes. +BOOL DAPI SceDatabaseChanged( + __in SCE_DATABASE *pDatabase + ); +// Resets the database changed flag +void DAPI SceResetDatabaseChanged( + __in SCE_DATABASE *pDatabase + ); +HRESULT DAPI SceCloseDatabase( + __in SCE_DATABASE *pDatabase + ); +void DAPI SceFreeRow( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle + ); +void DAPI SceFreeQuery( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle + ); +void DAPI SceFreeQueryResults( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/sczutil.h b/src/libs/dutil/WixToolset.DUtil/inc/sczutil.h new file mode 100644 index 00000000..fcfbd13a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/sczutil.h @@ -0,0 +1,30 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +class PSCZ +{ +public: + PSCZ() : m_scz(NULL) { } + + ~PSCZ() { ReleaseNullStr(m_scz); } + + operator LPWSTR() { return m_scz; } + + operator LPCWSTR() { return m_scz; } + + operator bool() { return NULL != m_scz; } + + LPWSTR* operator &() { return &m_scz; } + + bool operator !() { return !m_scz; } + + WCHAR operator *() { return *m_scz; } + + LPWSTR Detach() { LPWSTR scz = m_scz; m_scz = NULL; return scz; } + +private: + LPWSTR m_scz; +}; +#endif //__cplusplus diff --git a/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h b/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h new file mode 100644 index 00000000..0b9f539d --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h @@ -0,0 +1,47 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifndef REFKNOWNFOLDERID +#define REFKNOWNFOLDERID REFGUID +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef BOOL (STDAPICALLTYPE *PFN_SHELLEXECUTEEXW)( + __inout LPSHELLEXECUTEINFOW lpExecInfo + ); + +void DAPI ShelFunctionOverride( + __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW + ); +HRESULT DAPI ShelExec( + __in_z LPCWSTR wzTargetPath, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd, + __in_opt HWND hwndParent, + __out_opt HANDLE* phProcess + ); +HRESULT DAPI ShelExecUnelevated( + __in_z LPCWSTR wzTargetPath, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd + ); +HRESULT DAPI ShelGetFolder( + __out_z LPWSTR* psczFolderPath, + __in int csidlFolder + ); +HRESULT DAPI ShelGetKnownFolder( + __out_z LPWSTR* psczFolderPath, + __in REFKNOWNFOLDERID rfidFolder + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/sqlutil.h b/src/libs/dutil/WixToolset.DUtil/inc/sqlutil.h new file mode 100644 index 00000000..ddf09323 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/sqlutil.h @@ -0,0 +1,136 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +// Adding this until the SQL annotations are published to specstrings.h +#ifndef __sql_command +#define __sql_command +#endif + +// structs +struct SQL_FILESPEC +{ + WCHAR wzName[MAX_PATH]; + WCHAR wzFilename[MAX_PATH]; + WCHAR wzSize[MAX_PATH]; + WCHAR wzMaxSize[MAX_PATH]; + WCHAR wzGrow[MAX_PATH]; +}; + + +// functions +HRESULT DAPI SqlConnectDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out IDBCreateSession** ppidbSession + ); +HRESULT DAPI SqlStartTransaction( + __in IDBCreateSession* pidbSession, + __out IDBCreateCommand** ppidbCommand, + __out ITransaction** ppit + ); +HRESULT DAPI SqlEndTransaction( + __in ITransaction* pit, + __in BOOL fCommit + ); +HRESULT DAPI SqlDatabaseExists( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionDatabaseExists( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlDatabaseEnsureExists( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionDatabaseEnsureExists( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlCreateDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionCreateDatabase( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlDropDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionDropDatabase( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlSessionExecuteQuery( + __in IDBCreateSession* pidbSession, + __in __sql_command LPCWSTR wzSql, + __out_opt IRowset** ppirs, + __out_opt DBROWCOUNT* pcRows, + __out_opt BSTR* pbstrErrorDescription + ); +HRESULT DAPI SqlCommandExecuteQuery( + __in IDBCreateCommand* pidbCommand, + __in __sql_command LPCWSTR wzSql, + __out IRowset** ppirs, + __out DBROWCOUNT* pcRows + ); +HRESULT DAPI SqlGetErrorInfo( + __in IUnknown* pObjectWithError, + __in REFIID IID_InterfaceWithError, + __in DWORD dwLocaleId, + __out_opt BSTR* pbstrErrorSource, + __out_opt BSTR* pbstrErrorDescription + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/srputil.h b/src/libs/dutil/WixToolset.DUtil/inc/srputil.h new file mode 100644 index 00000000..95e96231 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/srputil.h @@ -0,0 +1,45 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum SRP_ACTION +{ + SRP_ACTION_UNKNOWN, + SRP_ACTION_UNINSTALL, + SRP_ACTION_INSTALL, + SRP_ACTION_MODIFY, +} SRP_ACTION; + + +/******************************************************************** + SrpInitialize - initializes system restore point functionality. + +*******************************************************************/ +DAPI_(HRESULT) SrpInitialize( + __in BOOL fInitializeComSecurity + ); + +/******************************************************************** + SrpUninitialize - uninitializes system restore point functionality. + +*******************************************************************/ +DAPI_(void) SrpUninitialize(); + +/******************************************************************** + SrpCreateRestorePoint - creates a system restore point. + +*******************************************************************/ +DAPI_(HRESULT) SrpCreateRestorePoint( + __in_z LPCWSTR wzApplicationName, + __in SRP_ACTION action + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libs/dutil/WixToolset.DUtil/inc/strutil.h b/src/libs/dutil/WixToolset.DUtil/inc/strutil.h new file mode 100644 index 00000000..1cff9ab8 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/strutil.h @@ -0,0 +1,316 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseStr(pwz) if (pwz) { StrFree(pwz); } +#define ReleaseNullStr(pwz) if (pwz) { StrFree(pwz); pwz = NULL; } +#define ReleaseBSTR(bstr) if (bstr) { ::SysFreeString(bstr); } +#define ReleaseNullBSTR(bstr) if (bstr) { ::SysFreeString(bstr); bstr = NULL; } +#define ReleaseStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); } } +#define ReleaseNullStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); c = 0; rg = NULL; } } +#define ReleaseNullStrSecure(pwz) if (pwz) { StrSecureZeroFreeString(pwz); pwz = NULL; } + +#define DeclareConstBSTR(bstr_const, wz) const WCHAR bstr_const[] = { 0x00, 0x00, sizeof(wz)-sizeof(WCHAR), 0x00, wz } +#define UseConstBSTR(bstr_const) const_cast(bstr_const + 4) + +HRESULT DAPI StrAlloc( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in SIZE_T cch + ); +HRESULT DAPI StrAllocSecure( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in SIZE_T cch + ); +HRESULT DAPI StrTrimCapacity( + __deref_out_z LPWSTR* ppwz + ); +HRESULT DAPI StrTrimWhitespace( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource + ); +HRESULT DAPI StrAnsiAlloc( + __deref_out_ecount_part(cch, 0) LPSTR* ppz, + __in SIZE_T cch + ); +HRESULT DAPI StrAnsiTrimCapacity( + __deref_out_z LPSTR* ppz + ); +HRESULT DAPI StrAnsiTrimWhitespace( + __deref_out_z LPSTR* ppz, + __in_z LPCSTR szSource + ); +HRESULT DAPI StrAllocString( + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); +HRESULT DAPI StrAllocStringSecure( + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); +HRESULT DAPI StrAnsiAllocString( + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in UINT uiCodepage + ); +HRESULT DAPI StrAllocStringAnsi( + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, + __in_z LPCSTR szSource, + __in SIZE_T cchSource, + __in UINT uiCodepage + ); +HRESULT DAPI StrAnsiAllocStringAnsi( + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, + __in_z LPCSTR szSource, + __in SIZE_T cchSource + ); +HRESULT DAPI StrAllocPrefix( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzPrefix, + __in SIZE_T cchPrefix + ); +HRESULT DAPI StrAllocConcat( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); +HRESULT DAPI StrAllocConcatSecure( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); +HRESULT DAPI StrAnsiAllocConcat( + __deref_out_z LPSTR* ppz, + __in_z LPCSTR pzSource, + __in SIZE_T cchSource + ); +HRESULT __cdecl StrAllocFormatted( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); +HRESULT __cdecl StrAllocConcatFormatted( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); +HRESULT __cdecl StrAllocConcatFormattedSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); +HRESULT __cdecl StrAllocFormattedSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); +HRESULT __cdecl StrAnsiAllocFormatted( + __deref_out_z LPSTR* ppsz, + __in __format_string LPCSTR szFormat, + ... + ); +HRESULT DAPI StrAllocFormattedArgs( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ); +HRESULT DAPI StrAllocFormattedArgsSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ); +HRESULT DAPI StrAnsiAllocFormattedArgs( + __deref_out_z LPSTR* ppsz, + __in __format_string LPCSTR szFormat, + __in va_list args + ); +HRESULT DAPI StrAllocFromError( + __inout LPWSTR *ppwzMessage, + __in HRESULT hrError, + __in_opt HMODULE hModule, + ... + ); + +HRESULT DAPI StrMaxLength( + __in LPCVOID p, + __out SIZE_T* pcbch + ); +HRESULT DAPI StrSize( + __in LPCVOID p, + __out SIZE_T* pcbb + ); + +HRESULT DAPI StrFree( + __in LPVOID p + ); + + +HRESULT DAPI StrReplaceStringAll( + __inout LPWSTR* ppwzOriginal, + __in_z LPCWSTR wzOldSubString, + __in_z LPCWSTR wzNewSubString + ); +HRESULT DAPI StrReplaceString( + __inout LPWSTR* ppwzOriginal, + __inout DWORD* pdwStartIndex, + __in_z LPCWSTR wzOldSubString, + __in_z LPCWSTR wzNewSubString + ); + +HRESULT DAPI StrHexEncode( + __in_ecount(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __out_ecount(cchDest) LPWSTR wzDest, + __in SIZE_T cchDest + ); +HRESULT DAPI StrAllocHexEncode( + __in_ecount(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest + ); +HRESULT DAPI StrHexDecode( + __in_z LPCWSTR wzSource, + __out_bcount(cbDest) BYTE* pbDest, + __in SIZE_T cbDest + ); +HRESULT DAPI StrAllocHexDecode( + __in_z LPCWSTR wzSource, + __out_bcount(*pcbDest) BYTE** ppbDest, + __out_opt DWORD* pcbDest + ); + +HRESULT DAPI StrAllocBase85Encode( + __in_bcount_opt(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __deref_out_z LPWSTR* pwzDest + ); +HRESULT DAPI StrAllocBase85Decode( + __in_z LPCWSTR wzSource, + __deref_out_bcount(*pcbDest) BYTE** ppbDest, + __out SIZE_T* pcbDest +); + +HRESULT DAPI MultiSzLen( + __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz, + __out SIZE_T* pcch + ); +HRESULT DAPI MultiSzPrepend( + __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz, + __inout_opt SIZE_T* pcchMultiSz, + __in __nullnullterminated LPCWSTR pwzInsert + ); +HRESULT DAPI MultiSzFindSubstring( + __in __nullnullterminated LPCWSTR pwzMultiSz, + __in __nullnullterminated LPCWSTR pwzSubstring, + __out_opt DWORD_PTR* pdwIndex, + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn + ); +HRESULT DAPI MultiSzFindString( + __in __nullnullterminated LPCWSTR pwzMultiSz, + __in __nullnullterminated LPCWSTR pwzString, + __out_opt DWORD_PTR* pdwIndex, + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound + ); +HRESULT DAPI MultiSzRemoveString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __in DWORD_PTR dwIndex + ); +HRESULT DAPI MultiSzInsertString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __inout_opt SIZE_T* pcchMultiSz, + __in DWORD_PTR dwIndex, + __in_z LPCWSTR pwzInsert + ); +HRESULT DAPI MultiSzReplaceString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __in DWORD_PTR dwIndex, + __in_z LPCWSTR pwzString + ); + +LPCWSTR DAPI wcsistr( + __in_z LPCWSTR wzString, + __in_z LPCWSTR wzCharSet + ); + +HRESULT DAPI StrStringToInt16( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out SHORT* psOut + ); +HRESULT DAPI StrStringToUInt16( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out USHORT* pusOut + ); +HRESULT DAPI StrStringToInt32( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out INT* piOut + ); +HRESULT DAPI StrStringToUInt32( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out UINT* puiOut + ); +HRESULT DAPI StrStringToInt64( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out LONGLONG* pllOut + ); +HRESULT DAPI StrStringToUInt64( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out ULONGLONG* pullOut + ); +void DAPI StrStringToUpper( + __inout_z LPWSTR wzIn + ); +void DAPI StrStringToLower( + __inout_z LPWSTR wzIn + ); +HRESULT DAPI StrAllocStringToUpperInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); +HRESULT DAPI StrAllocStringToLowerInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); + +HRESULT DAPI StrArrayAllocString( + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, + __inout LPUINT pcStrArray, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ); + +HRESULT DAPI StrArrayFree( + __in_ecount(cStrArray) LPWSTR *rgsczStrArray, + __in UINT cStrArray + ); + +HRESULT DAPI StrSplitAllocArray( + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, + __inout LPUINT pcStrArray, + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzDelim + ); + +HRESULT DAPI StrSecureZeroString( + __in LPWSTR pwz + ); +HRESULT DAPI StrSecureZeroFreeString( + __in LPWSTR pwz + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/svcutil.h b/src/libs/dutil/WixToolset.DUtil/inc/svcutil.h new file mode 100644 index 00000000..80d6326c --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/svcutil.h @@ -0,0 +1,21 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ReleaseServiceHandle(h) if (h) { ::CloseServiceHandle(h); h = NULL; } + + +HRESULT DAPI SvcQueryConfig( + __in SC_HANDLE sch, + __out QUERY_SERVICE_CONFIGW** ppConfig + ); + + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h b/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h new file mode 100644 index 00000000..d3dd6d21 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h @@ -0,0 +1,765 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseTheme(p) if (p) { ThemeFree(p); p = NULL; } + +typedef HRESULT(CALLBACK *PFNTHM_EVALUATE_VARIABLE_CONDITION)( + __in_z LPCWSTR wzCondition, + __out BOOL* pf, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_FORMAT_VARIABLE_STRING)( + __in_z LPCWSTR wzFormat, + __inout LPWSTR* psczOut, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_NUMERIC)( + __in_z LPCWSTR wzVariable, + __out LONGLONG* pllValue, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_NUMERIC)( + __in_z LPCWSTR wzVariable, + __in LONGLONG llValue, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_STRING)( + __in_z LPCWSTR wzVariable, + __inout LPWSTR* psczValue, + __in_opt LPVOID pvContext + ); +typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_STRING)( + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue, + __in BOOL fFormatted, + __in_opt LPVOID pvContext + ); + +typedef enum THEME_ACTION_TYPE +{ + THEME_ACTION_TYPE_BROWSE_DIRECTORY, + THEME_ACTION_TYPE_CHANGE_PAGE, + THEME_ACTION_TYPE_CLOSE_WINDOW, +} THEME_ACTION_TYPE; + +typedef enum THEME_CONTROL_DATA +{ + THEME_CONTROL_DATA_HOVER = 1, +} THEME_CONTROL_DATA; + +typedef enum THEME_CONTROL_TYPE +{ + THEME_CONTROL_TYPE_UNKNOWN, + THEME_CONTROL_TYPE_BILLBOARD, + THEME_CONTROL_TYPE_BUTTON, + THEME_CONTROL_TYPE_CHECKBOX, + THEME_CONTROL_TYPE_COMBOBOX, + THEME_CONTROL_TYPE_COMMANDLINK, + THEME_CONTROL_TYPE_EDITBOX, + THEME_CONTROL_TYPE_HYPERLINK, + THEME_CONTROL_TYPE_HYPERTEXT, + THEME_CONTROL_TYPE_IMAGE, + THEME_CONTROL_TYPE_LABEL, + THEME_CONTROL_TYPE_PANEL, + THEME_CONTROL_TYPE_PROGRESSBAR, + THEME_CONTROL_TYPE_RADIOBUTTON, + THEME_CONTROL_TYPE_RICHEDIT, + THEME_CONTROL_TYPE_STATIC, + THEME_CONTROL_TYPE_LISTVIEW, + THEME_CONTROL_TYPE_TREEVIEW, + THEME_CONTROL_TYPE_TAB, +} THEME_CONTROL_TYPE; + +typedef enum THEME_SHOW_PAGE_REASON +{ + THEME_SHOW_PAGE_REASON_DEFAULT, + THEME_SHOW_PAGE_REASON_CANCEL, + THEME_SHOW_PAGE_REASON_REFRESH, +} THEME_SHOW_PAGE_REASON; + +typedef enum THEME_WINDOW_INITIAL_POSITION +{ + THEME_WINDOW_INITIAL_POSITION_DEFAULT, + THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES, +} THEME_WINDOW_INITIAL_POSITION; + + +struct THEME_COLUMN +{ + LPWSTR pszName; + UINT uStringId; + int nDefaultDpiBaseWidth; + int nBaseWidth; + int nWidth; + BOOL fExpands; +}; + + +struct THEME_TAB +{ + LPWSTR pszName; + UINT uStringId; +}; + +struct THEME_ACTION +{ + LPWSTR sczCondition; + THEME_ACTION_TYPE type; + union + { + struct + { + LPWSTR sczVariableName; + } BrowseDirectory; + struct + { + LPWSTR sczPageName; + BOOL fCancel; + } ChangePage; + }; +}; + +struct THEME_CONDITIONAL_TEXT +{ + LPWSTR sczCondition; + LPWSTR sczText; +}; + +// THEME_ASSIGN_CONTROL_ID - Used to apply a specific id to a named control (usually +// to set the WM_COMMAND). +struct THEME_ASSIGN_CONTROL_ID +{ + WORD wId; // id to apply to control + LPCWSTR wzName; // name of control to match +}; + +const DWORD THEME_FIRST_ASSIGN_CONTROL_ID = 1024; // Recommended first control id to be assigned. + +struct THEME_CONTROL +{ + THEME_CONTROL_TYPE type; + + WORD wId; + WORD wPageId; + + LPWSTR sczName; // optional name for control, used to apply control id and link the control to a variable. + LPWSTR sczText; + LPWSTR sczTooltip; + LPWSTR sczNote; // optional text for command link + int nDefaultDpiX; + int nDefaultDpiY; + int nDefaultDpiHeight; + int nDefaultDpiWidth; + int nX; + int nY; + int nHeight; + int nWidth; + int nSourceX; + int nSourceY; + UINT uStringId; + + LPWSTR sczEnableCondition; + LPWSTR sczVisibleCondition; + BOOL fDisableVariableFunctionality; + + HBITMAP hImage; + HICON hIcon; + + // Don't free these; it's just a handle to the central image lists stored in THEME. The handle is freed once, there. + HIMAGELIST rghImageList[4]; + + DWORD dwStyle; + DWORD dwExtendedStyle; + DWORD dwInternalStyle; + + DWORD dwFontId; + + // child controls + DWORD cControls; + THEME_CONTROL* rgControls; + + // Used by billboard controls + WORD wBillboardInterval; + BOOL fBillboardLoops; + + // Used by button and command link controls + THEME_ACTION* rgActions; + DWORD cActions; + THEME_ACTION* pDefaultAction; + + // Used by hyperlink and owner-drawn button controls + DWORD dwFontHoverId; + DWORD dwFontSelectedId; + + // Used by listview controls + THEME_COLUMN *ptcColumns; + DWORD cColumns; + + // Used by radio button controls + BOOL fLastRadioButton; + LPWSTR sczValue; + LPWSTR sczVariable; + + // Used by tab controls + THEME_TAB *pttTabs; + DWORD cTabs; + + // Used by controls that have text + DWORD cConditionalText; + THEME_CONDITIONAL_TEXT* rgConditionalText; + + // Used by command link controls + DWORD cConditionalNotes; + THEME_CONDITIONAL_TEXT* rgConditionalNotes; + + // state variables that should be ignored + HWND hWnd; + DWORD dwData; // type specific data +}; + + +struct THEME_IMAGELIST +{ + LPWSTR sczName; + + HIMAGELIST hImageList; +}; + +struct THEME_SAVEDVARIABLE +{ + LPWSTR wzName; + LPWSTR sczValue; +}; + +struct THEME_PAGE +{ + WORD wId; + LPWSTR sczName; + + DWORD cControlIndices; + + DWORD cSavedVariables; + THEME_SAVEDVARIABLE* rgSavedVariables; +}; + +struct THEME_FONT_INSTANCE +{ + UINT nDpi; + HFONT hFont; +}; + +struct THEME_FONT +{ + LONG lfHeight; + LONG lfWeight; + BYTE lfUnderline; + BYTE lfQuality; + LPWSTR sczFaceName; + + COLORREF crForeground; + HBRUSH hForeground; + COLORREF crBackground; + HBRUSH hBackground; + + DWORD cFontInstances; + THEME_FONT_INSTANCE* rgFontInstances; +}; + + +struct THEME +{ + WORD wId; + + BOOL fAutoResize; + BOOL fForceResize; + + DWORD dwStyle; + DWORD dwFontId; + HANDLE hIcon; + LPWSTR sczCaption; + int nDefaultDpiHeight; + int nDefaultDpiMinimumHeight; + int nDefaultDpiWidth; + int nDefaultDpiMinimumWidth; + int nHeight; + int nMinimumHeight; + int nWidth; + int nMinimumWidth; + int nWindowHeight; + int nWindowWidth; + int nSourceX; + int nSourceY; + UINT uStringId; + + HBITMAP hImage; + + DWORD cFonts; + THEME_FONT* rgFonts; + + DWORD cPages; + THEME_PAGE* rgPages; + + DWORD cImageLists; + THEME_IMAGELIST* rgImageLists; + + DWORD cControls; + THEME_CONTROL* rgControls; + + // internal state variables -- do not use outside ThmUtil.cpp + HWND hwndParent; // parent for loaded controls + HWND hwndHover; // current hwnd hovered over + DWORD dwCurrentPageId; + HWND hwndTooltip; + + UINT nDpi; + + // callback functions + PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition; + PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString; + PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable; + PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable; + PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable; + PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable; + + LPVOID pvVariableContext; +}; + + +/******************************************************************** + ThemeInitialize - initialized theme management. + +*******************************************************************/ +HRESULT DAPI ThemeInitialize( + __in_opt HMODULE hModule + ); + +/******************************************************************** + ThemeUninitialize - uninitialize theme management. + +*******************************************************************/ +void DAPI ThemeUninitialize(); + +/******************************************************************** + ThemeLoadFromFile - loads a theme from a loose file. + + *******************************************************************/ +HRESULT DAPI ThemeLoadFromFile( + __in_z LPCWSTR wzThemeFile, + __out THEME** ppTheme + ); + +/******************************************************************** + ThemeLoadFromResource - loads a theme from a module's data resource. + + NOTE: The resource data must be UTF-8 encoded. +*******************************************************************/ +HRESULT DAPI ThemeLoadFromResource( + __in_opt HMODULE hModule, + __in_z LPCSTR szResource, + __out THEME** ppTheme + ); + +/******************************************************************** + ThemeFree - frees any memory associated with a theme. + +*******************************************************************/ +void DAPI ThemeFree( + __in THEME* pTheme + ); + +/******************************************************************** +ThemeRegisterVariableCallbacks - registers a context and callbacks + for working with variables. + +*******************************************************************/ +HRESULT DAPI ThemeRegisterVariableCallbacks( + __in THEME* pTheme, + __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition, + __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString, + __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable, + __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable, + __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable, + __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable, + __in_opt LPVOID pvContext + ); + +/******************************************************************** + ThemeCreateParentWindow - creates a parent window for the theme. + +*******************************************************************/ +HRESULT DAPI ThemeCreateParentWindow( + __in THEME* pTheme, + __in DWORD dwExStyle, + __in LPCWSTR szClassName, + __in LPCWSTR szWindowName, + __in DWORD dwStyle, + __in int x, + __in int y, + __in_opt HWND hwndParent, + __in_opt HINSTANCE hInstance, + __in_opt LPVOID lpParam, + __in THEME_WINDOW_INITIAL_POSITION initialPosition, + __out_opt HWND* phWnd + ); + +/******************************************************************** + ThemeLoadControls - creates the windows for all the theme controls + using the window created in ThemeCreateParentWindow. + +*******************************************************************/ +HRESULT DAPI ThemeLoadControls( + __in THEME* pTheme, + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, + __in DWORD cAssignControlIds + ); + +/******************************************************************** + ThemeUnloadControls - resets all the theme control windows so the theme + controls can be reloaded. + +*******************************************************************/ +void DAPI ThemeUnloadControls( + __in THEME* pTheme + ); + +/******************************************************************** + ThemeLocalize - Localizes all of the strings in the theme. + +*******************************************************************/ +HRESULT DAPI ThemeLocalize( + __in THEME *pTheme, + __in const WIX_LOCALIZATION *pLocStringSet + ); + +HRESULT DAPI ThemeLoadStrings( + __in THEME* pTheme, + __in HMODULE hResModule + ); + +/******************************************************************** + ThemeLoadRichEditFromFile - Attach a richedit control to a RTF file. + + *******************************************************************/ +HRESULT DAPI ThemeLoadRichEditFromFile( + __in THEME* pTheme, + __in DWORD dwControl, + __in_z LPCWSTR wzFileName, + __in HMODULE hModule + ); + +/******************************************************************** + ThemeLoadRichEditFromResource - Attach a richedit control to resource data. + + *******************************************************************/ +HRESULT DAPI ThemeLoadRichEditFromResource( + __in THEME* pTheme, + __in DWORD dwControl, + __in_z LPCSTR szResourceName, + __in HMODULE hModule + ); + +/******************************************************************** + ThemeLoadRichEditFromResourceToHWnd - Attach a richedit control (by + HWND) to resource data. + + *******************************************************************/ +HRESULT DAPI ThemeLoadRichEditFromResourceToHWnd( + __in HWND hWnd, + __in_z LPCSTR szResourceName, + __in HMODULE hModule + ); + +/******************************************************************** + ThemeHandleKeyboardMessage - will translate the message using the active + accelerator table. + +*******************************************************************/ +BOOL DAPI ThemeHandleKeyboardMessage( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in MSG* pMsg + ); + +/******************************************************************** + ThemeDefWindowProc - replacement for DefWindowProc() when using theme. + +*******************************************************************/ +LRESULT CALLBACK ThemeDefWindowProc( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); + +/******************************************************************** + ThemeGetPageIds - gets the page ids for the theme via page names. + +*******************************************************************/ +void DAPI ThemeGetPageIds( + __in const THEME* pTheme, + __in_ecount(cGetPages) LPCWSTR* rgwzFindNames, + __inout_ecount(cGetPages) DWORD* rgdwPageIds, + __in DWORD cGetPages + ); + +/******************************************************************** + ThemeGetPage - gets a theme page by id. + + *******************************************************************/ +THEME_PAGE* DAPI ThemeGetPage( + __in const THEME* pTheme, + __in DWORD dwPage + ); + +/******************************************************************** + ThemeShowPage - shows or hides all of the controls in the page at one time. + + *******************************************************************/ +HRESULT DAPI ThemeShowPage( + __in THEME* pTheme, + __in DWORD dwPage, + __in int nCmdShow + ); + +/******************************************************************** +ThemeShowPageEx - shows or hides all of the controls in the page at one time. + When using variables, TSPR_CANCEL reverts any changes made. + TSPR_REFRESH forces reevaluation of conditions. + It is expected that the current page is hidden before + showing a new page. + +*******************************************************************/ +HRESULT DAPI ThemeShowPageEx( + __in THEME* pTheme, + __in DWORD dwPage, + __in int nCmdShow, + __in THEME_SHOW_PAGE_REASON reason + ); + + +/******************************************************************** +ThemeShowChild - shows a control's specified child control, hiding the rest. + +*******************************************************************/ +void DAPI ThemeShowChild( + __in THEME* pTheme, + __in THEME_CONTROL* pParentControl, + __in DWORD dwIndex + ); + +/******************************************************************** + ThemeControlExists - check if a control with the specified id exists. + + *******************************************************************/ +BOOL DAPI ThemeControlExists( + __in const THEME* pTheme, + __in DWORD dwControl + ); + +/******************************************************************** + ThemeControlEnable - enables/disables a control. + + *******************************************************************/ +void DAPI ThemeControlEnable( + __in THEME* pTheme, + __in DWORD dwControl, + __in BOOL fEnable + ); + +/******************************************************************** + ThemeControlEnabled - returns whether a control is enabled/disabled. + + *******************************************************************/ +BOOL DAPI ThemeControlEnabled( + __in THEME* pTheme, + __in DWORD dwControl + ); + +/******************************************************************** + ThemeControlElevates - sets/removes the shield icon on a control. + + *******************************************************************/ +void DAPI ThemeControlElevates( + __in THEME* pTheme, + __in DWORD dwControl, + __in BOOL fElevates + ); + +/******************************************************************** + ThemeShowControl - shows/hides a control. + + *******************************************************************/ +void DAPI ThemeShowControl( + __in THEME* pTheme, + __in DWORD dwControl, + __in int nCmdShow + ); + +/******************************************************************** +ThemeShowControlEx - shows/hides a control with support for +conditional text and notes. + +*******************************************************************/ +void DAPI ThemeShowControlEx( + __in THEME* pTheme, + __in DWORD dwControl, + __in int nCmdShow + ); + +/******************************************************************** + ThemeControlVisible - returns whether a control is visible. + + *******************************************************************/ +BOOL DAPI ThemeControlVisible( + __in THEME* pTheme, + __in DWORD dwControl + ); + +BOOL DAPI ThemePostControlMessage( + __in THEME* pTheme, + __in DWORD dwControl, + __in UINT Msg, + __in WPARAM wParam, + __in LPARAM lParam + ); + +LRESULT DAPI ThemeSendControlMessage( + __in const THEME* pTheme, + __in DWORD dwControl, + __in UINT Msg, + __in WPARAM wParam, + __in LPARAM lParam + ); + +/******************************************************************** + ThemeDrawBackground - draws the theme background. + +*******************************************************************/ +HRESULT DAPI ThemeDrawBackground( + __in THEME* pTheme, + __in PAINTSTRUCT* pps + ); + +/******************************************************************** + ThemeDrawControl - draw an owner drawn control. + +*******************************************************************/ +HRESULT DAPI ThemeDrawControl( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis + ); + +/******************************************************************** + ThemeHoverControl - mark a control as hover. + +*******************************************************************/ +BOOL DAPI ThemeHoverControl( + __in THEME* pTheme, + __in HWND hwndParent, + __in HWND hwndControl + ); + +/******************************************************************** + ThemeIsControlChecked - gets whether a control is checked. Only + really useful for checkbox controls. + +*******************************************************************/ +BOOL DAPI ThemeIsControlChecked( + __in THEME* pTheme, + __in DWORD dwControl + ); + +/******************************************************************** + ThemeSetControlColor - sets the color of text for a control. + +*******************************************************************/ +BOOL DAPI ThemeSetControlColor( + __in THEME* pTheme, + __in HDC hdc, + __in HWND hWnd, + __out HBRUSH* phBackgroundBrush + ); + +/******************************************************************** + ThemeSetProgressControl - sets the current percentage complete in a + progress bar control. + +*******************************************************************/ +HRESULT DAPI ThemeSetProgressControl( + __in THEME* pTheme, + __in DWORD dwControl, + __in DWORD dwProgressPercentage + ); + +/******************************************************************** + ThemeSetProgressControlColor - sets the current color of a + progress bar control. + +*******************************************************************/ +HRESULT DAPI ThemeSetProgressControlColor( + __in THEME* pTheme, + __in DWORD dwControl, + __in DWORD dwColorIndex + ); + +/******************************************************************** + ThemeSetTextControl - sets the text of a control. + +*******************************************************************/ +HRESULT DAPI ThemeSetTextControl( + __in const THEME* pTheme, + __in DWORD dwControl, + __in_z_opt LPCWSTR wzText + ); + +/******************************************************************** +ThemeSetTextControl - sets the text of a control and optionally + invalidates the control. + +*******************************************************************/ +HRESULT DAPI ThemeSetTextControlEx( + __in const THEME* pTheme, + __in DWORD dwControl, + __in BOOL fUpdate, + __in_z_opt LPCWSTR wzText + ); + +/******************************************************************** + ThemeGetTextControl - gets the text of a control. + +*******************************************************************/ +HRESULT DAPI ThemeGetTextControl( + __in const THEME* pTheme, + __in DWORD dwControl, + __inout_z LPWSTR* psczText + ); + +/******************************************************************** + ThemeUpdateCaption - updates the caption in the theme. + +*******************************************************************/ +HRESULT DAPI ThemeUpdateCaption( + __in THEME* pTheme, + __in_z LPCWSTR wzCaption + ); + +/******************************************************************** + ThemeSetFocus - set the focus to the control supplied or the next + enabled control if it is disabled. + +*******************************************************************/ +void DAPI ThemeSetFocus( + __in THEME* pTheme, + __in DWORD dwControl + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libs/dutil/WixToolset.DUtil/inc/timeutil.h b/src/libs/dutil/WixToolset.DUtil/inc/timeutil.h new file mode 100644 index 00000000..3655c00a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/timeutil.h @@ -0,0 +1,38 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI TimeFromString( + __in_z LPCWSTR wzTime, + __out FILETIME* pFileTime + ); +HRESULT DAPI TimeFromString3339( + __in_z LPCWSTR wzTime, + __out FILETIME* pFileTime + ); +HRESULT DAPI TimeCurrentTime( + __deref_out_z LPWSTR* ppwz, + __in BOOL fGMT + ); +HRESULT DAPI TimeCurrentDateTime( + __deref_out_z LPWSTR* ppwz, + __in BOOL fGMT + ); +HRESULT DAPI TimeSystemDateTime( + __deref_out_z LPWSTR* ppwz, + __in const SYSTEMTIME *pst, + __in BOOL fGMT + ); +HRESULT DAPI TimeSystemToDateTimeString( + __deref_out_z LPWSTR* ppwz, + __in const SYSTEMTIME *pst, + __in LCID locale + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/uncutil.h b/src/libs/dutil/WixToolset.DUtil/inc/uncutil.h new file mode 100644 index 00000000..6516a801 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/uncutil.h @@ -0,0 +1,20 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************* + UncConvertFromMountedDrive - Converts the string in-place from a + mounted drive path to a UNC path +*******************************************************************/ +DAPI_(HRESULT) UncConvertFromMountedDrive( + __inout LPWSTR *psczUNCPath, + __in LPCWSTR sczMountedDrivePath + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/uriutil.h b/src/libs/dutil/WixToolset.DUtil/inc/uriutil.h new file mode 100644 index 00000000..d6dfdd6b --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/uriutil.h @@ -0,0 +1,100 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include "wininet.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum URI_PROTOCOL +{ + URI_PROTOCOL_UNKNOWN, + URI_PROTOCOL_FILE, + URI_PROTOCOL_FTP, + URI_PROTOCOL_HTTP, + URI_PROTOCOL_HTTPS, + URI_PROTOCOL_LOCAL, + URI_PROTOCOL_UNC +} URI_PROTOCOL; + +typedef struct _URI_INFO +{ + INTERNET_SCHEME scheme; + LPWSTR sczHostName; + INTERNET_PORT port; + LPWSTR sczUser; + LPWSTR sczPassword; + LPWSTR sczPath; + LPWSTR sczQueryString; +} URI_INFO; + + +HRESULT DAPI UriCanonicalize( + __inout_z LPWSTR* psczUri + ); + +HRESULT DAPI UriCrack( + __in_z LPCWSTR wzUri, + __out_opt INTERNET_SCHEME* pScheme, + __deref_opt_out_z LPWSTR* psczHostName, + __out_opt INTERNET_PORT* pPort, + __deref_opt_out_z LPWSTR* psczUser, + __deref_opt_out_z LPWSTR* psczPassword, + __deref_opt_out_z LPWSTR* psczPath, + __deref_opt_out_z LPWSTR* psczQueryString + ); + +HRESULT DAPI UriCrackEx( + __in_z LPCWSTR wzUri, + __in URI_INFO* pUriInfo + ); + +void DAPI UriInfoUninitialize( + __in URI_INFO* pUriInfo + ); + +HRESULT DAPI UriCreate( + __inout_z LPWSTR* psczUri, + __in INTERNET_SCHEME scheme, + __in_z_opt LPWSTR wzHostName, + __in INTERNET_PORT port, + __in_z_opt LPWSTR wzUser, + __in_z_opt LPWSTR wzPassword, + __in_z_opt LPWSTR wzPath, + __in_z_opt LPWSTR wzQueryString + ); + +HRESULT DAPI UriCanonicalize( + __inout_z LPWSTR* psczUri + ); + +HRESULT DAPI UriFile( + __deref_out_z LPWSTR* psczFile, + __in_z LPCWSTR wzUri + ); + +HRESULT DAPI UriProtocol( + __in_z LPCWSTR wzUri, + __out URI_PROTOCOL* pProtocol + ); + +HRESULT DAPI UriRoot( + __in_z LPCWSTR wzUri, + __out LPWSTR* ppwzRoot, + __out_opt URI_PROTOCOL* pProtocol + ); + +HRESULT DAPI UriResolve( + __in_z LPCWSTR wzUri, + __in_opt LPCWSTR wzBaseUri, + __out LPWSTR* ppwzResolvedUri, + __out_opt URI_PROTOCOL* pResolvedProtocol + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libs/dutil/WixToolset.DUtil/inc/userutil.h b/src/libs/dutil/WixToolset.DUtil/inc/userutil.h new file mode 100644 index 00000000..2c86d229 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/userutil.h @@ -0,0 +1,32 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI UserBuildDomainUserName( + __out_ecount_z(cchDest) LPWSTR wzDest, + __in int cchDest, + __in_z LPCWSTR pwzName, + __in_z LPCWSTR pwzDomain + ); + +HRESULT DAPI UserCheckIsMember( + __in_z LPCWSTR pwzName, + __in_z LPCWSTR pwzDomain, + __in_z LPCWSTR pwzGroupName, + __in_z LPCWSTR pwzGroupDomain, + __out LPBOOL lpfMember + ); + +HRESULT DAPI UserCreateADsPath( + __in_z LPCWSTR wzObjectDomain, + __in_z LPCWSTR wzObjectName, + __out BSTR *pbstrAdsPath + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/verutil.h b/src/libs/dutil/WixToolset.DUtil/inc/verutil.h new file mode 100644 index 00000000..5247bb61 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/verutil.h @@ -0,0 +1,93 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleaseVerutilVersion(p) if (p) { VerFreeVersion(p); p = NULL; } + +typedef struct _VERUTIL_VERSION_RELEASE_LABEL +{ + BOOL fNumeric; + DWORD dwValue; + SIZE_T cchLabelOffset; + int cchLabel; +} VERUTIL_VERSION_RELEASE_LABEL; + +typedef struct _VERUTIL_VERSION +{ + LPWSTR sczVersion; + DWORD dwMajor; + DWORD dwMinor; + DWORD dwPatch; + DWORD dwRevision; + DWORD cReleaseLabels; + VERUTIL_VERSION_RELEASE_LABEL* rgReleaseLabels; + SIZE_T cchMetadataOffset; + BOOL fInvalid; +} VERUTIL_VERSION; + +/******************************************************************* + VerCompareParsedVersions - compares the Verutil versions. + +*******************************************************************/ +HRESULT DAPI VerCompareParsedVersions( + __in_opt VERUTIL_VERSION* pVersion1, + __in_opt VERUTIL_VERSION* pVersion2, + __out int* pnResult + ); + +/******************************************************************* + VerCompareStringVersions - parses the strings with VerParseVersion and then + compares the Verutil versions with VerCompareParsedVersions. + +*******************************************************************/ +HRESULT DAPI VerCompareStringVersions( + __in_z LPCWSTR wzVersion1, + __in_z LPCWSTR wzVersion2, + __in BOOL fStrict, + __out int* pnResult + ); + +/******************************************************************** + VerCopyVersion - copies the given Verutil version. + +*******************************************************************/ +HRESULT DAPI VerCopyVersion( + __in VERUTIL_VERSION* pSource, + __out VERUTIL_VERSION** ppVersion + ); + +/******************************************************************** + VerFreeVersion - frees any memory associated with a Verutil version. + +*******************************************************************/ +void DAPI VerFreeVersion( + __in VERUTIL_VERSION* pVersion + ); + +/******************************************************************* + VerParseVersion - parses the string into a Verutil version. + +*******************************************************************/ +HRESULT DAPI VerParseVersion( + __in_z LPCWSTR wzVersion, + __in SIZE_T cchVersion, + __in BOOL fStrict, + __out VERUTIL_VERSION** ppVersion + ); + +/******************************************************************* + VerParseVersion - parses the QWORD into a Verutil version. + +*******************************************************************/ +HRESULT DAPI VerVersionFromQword( + __in DWORD64 qwVersion, + __out VERUTIL_VERSION** ppVersion + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/wiutil.h b/src/libs/dutil/WixToolset.DUtil/inc/wiutil.h new file mode 100644 index 00000000..9c2de209 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/wiutil.h @@ -0,0 +1,402 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// constants + +#define IDNOACTION 0 +#define WIU_MB_OKIGNORECANCELRETRY 0xE + +#define MAX_DARWIN_KEY 73 +#define MAX_DARWIN_COLUMN 255 + +#define WIU_LOG_DEFAULT INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | \ + INSTALLLOGMODE_USER | INSTALLLOGMODE_INFO | INSTALLLOGMODE_RESOLVESOURCE | \ + INSTALLLOGMODE_OUTOFDISKSPACE | INSTALLLOGMODE_ACTIONSTART | \ + INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA | INSTALLLOGMODE_PROPERTYDUMP + +#define ReleaseMsi(h) if (h) { ::MsiCloseHandle(h); } +#define ReleaseNullMsi(h) if (h) { ::MsiCloseHandle(h); h = NULL; } + + +typedef enum WIU_RESTART +{ + WIU_RESTART_NONE, + WIU_RESTART_REQUIRED, + WIU_RESTART_INITIATED, +} WIU_RESTART; + +typedef enum WIU_MSI_EXECUTE_MESSAGE_TYPE +{ + WIU_MSI_EXECUTE_MESSAGE_NONE, + WIU_MSI_EXECUTE_MESSAGE_PROGRESS, + WIU_MSI_EXECUTE_MESSAGE_ERROR, + WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE, + WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE, +} WIU_MSI_EXECUTE_MESSAGE_TYPE; + + +// structures + +typedef struct _WIU_MSI_EXECUTE_MESSAGE +{ + WIU_MSI_EXECUTE_MESSAGE_TYPE type; + DWORD dwAllowedResults; + + DWORD cData; + LPCWSTR* rgwzData; + + INT nResultRecommendation; // recommended return result for this message based on analysis of real world installs. + + union + { + struct + { + DWORD dwPercentage; + } progress; + struct + { + DWORD dwErrorCode; + LPCWSTR wzMessage; + } error; + struct + { + INSTALLMESSAGE mt; + LPCWSTR wzMessage; + } msiMessage; + struct + { + DWORD cFiles; + LPCWSTR* rgwzFiles; + } msiFilesInUse; + }; +} WIU_MSI_EXECUTE_MESSAGE; + +typedef struct _WIU_MSI_PROGRESS +{ + DWORD dwTotal; + DWORD dwCompleted; + DWORD dwStep; + BOOL fMoveForward; + BOOL fEnableActionData; + BOOL fScriptInProgress; +} WIU_MSI_PROGRESS; + + +typedef int (*PFN_MSIEXECUTEMESSAGEHANDLER)( + __in WIU_MSI_EXECUTE_MESSAGE* pMessage, + __in_opt LPVOID pvContext + ); + +typedef struct _WIU_MSI_EXECUTE_CONTEXT +{ + BOOL fRollback; + PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler; + LPVOID pvContext; + WIU_MSI_PROGRESS rgMsiProgress[64]; + DWORD dwCurrentProgressIndex; + + INSTALLUILEVEL previousInstallUILevel; + HWND hwndPreviousParentWindow; + INSTALLUI_HANDLERW pfnPreviousExternalUI; + INSTALLUI_HANDLER_RECORD pfnPreviousExternalUIRecord; + + BOOL fSetPreviousExternalUIRecord; + BOOL fSetPreviousExternalUI; +} WIU_MSI_EXECUTE_CONTEXT; + + +// typedefs +typedef UINT (WINAPI *PFN_MSIENABLELOGW)( + __in DWORD dwLogMode, + __in_z LPCWSTR szLogFile, + __in DWORD dwLogAttributes + ); +typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOW)( + __in LPCWSTR szProductCode, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout LPDWORD pcchValue + ); +typedef INSTALLSTATE (WINAPI *PFN_MSIGETCOMPONENTPATHW)( + __in LPCWSTR szProduct, + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ); +typedef INSTALLSTATE (WINAPI *PFN_MSILOCATECOMPONENTW)( + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ); +typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOEXW)( + __in LPCWSTR szProductCode, + __in_opt LPCWSTR szUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ); +typedef INSTALLSTATE (WINAPI *PFN_MSIQUERYFEATURESTATEW)( + __in LPCWSTR szProduct, + __in LPCWSTR szFeature + ); +typedef UINT (WINAPI *PFN_MSIGETPATCHINFOEXW)( + __in_z LPCWSTR wzPatchCode, + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out_opt LPWSTR wzValue, + __inout DWORD* pcchValue + ); +typedef UINT (WINAPI *PFN_MSIDETERMINEPATCHSEQUENCEW)( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT context, + __in DWORD cPatchInfo, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo + ); +typedef UINT (WINAPI *PFN_MSIDETERMINEAPPLICABLEPATCHESW)( + __in_z LPCWSTR wzProductPackagePath, + __in DWORD cPatchInfo, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo + ); +typedef UINT (WINAPI *PFN_MSIINSTALLPRODUCTW)( + __in LPCWSTR szPackagePath, + __in_opt LPCWSTR szCommandLine + ); +typedef UINT (WINAPI *PFN_MSICONFIGUREPRODUCTEXW)( + __in LPCWSTR szProduct, + __in int iInstallLevel, + __in INSTALLSTATE eInstallState, + __in_opt LPCWSTR szCommandLine + ); +typedef UINT (WINAPI *PFN_MSIREMOVEPATCHESW)( + __in_z LPCWSTR wzPatchList, + __in_z LPCWSTR wzProductCode, + __in INSTALLTYPE eUninstallType, + __in_z_opt LPCWSTR szPropertyList + ); +typedef INSTALLUILEVEL (WINAPI *PFN_MSISETINTERNALUI)( + __in INSTALLUILEVEL dwUILevel, + __inout_opt HWND *phWnd + ); +typedef UINT (WINAPI *PFN_MSISETEXTERNALUIRECORD)( + __in_opt INSTALLUI_HANDLER_RECORD puiHandler, + __in DWORD dwMessageFilter, + __in_opt LPVOID pvContext, + __out_opt PINSTALLUI_HANDLER_RECORD ppuiPrevHandler + ); +typedef INSTALLUI_HANDLERW (WINAPI *PFN_MSISETEXTERNALUIW)( + __in_opt INSTALLUI_HANDLERW puiHandler, + __in DWORD dwMessageFilter, + __in_opt LPVOID pvContext + ); +typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSW)( + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf + ); +typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSEXW)( + __in_z_opt LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in DWORD dwContext, + __in DWORD dwIndex, + __out_opt WCHAR wzInstalledProductCode[39], + __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, + __out_opt LPWSTR wzSid, + __inout_opt LPDWORD pcchSid + ); + +typedef UINT (WINAPI *PFN_MSIENUMRELATEDPRODUCTSW)( + __in LPCWSTR lpUpgradeCode, + __reserved DWORD dwReserved, + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf + ); +typedef UINT (WINAPI *PFN_MSISOURCELISTADDSOURCEEXW)( + __in LPCWSTR szProductCodeOrPatchCode, + __in_opt LPCWSTR szUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in DWORD dwOptions, + __in LPCWSTR szSource, + __in_opt DWORD dwIndex + ); +typedef UINT(WINAPI* PFN_MSIBEGINTRANSACTIONW)( + __in LPCWSTR szName, + __in DWORD dwTransactionAttributes, + __out MSIHANDLE* phTransactionHandle, + __out HANDLE* phChangeOfOwnerEvent + ); +typedef UINT(WINAPI* PFN_MSIENDTRANSACTION)( + __in DWORD dwTransactionState + ); + + +HRESULT DAPI WiuInitialize( + ); +void DAPI WiuUninitialize( + ); +void DAPI WiuFunctionOverride( + __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW, + __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW, + __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW, + __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW, + __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW, + __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW, + __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW, + __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW, + __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI, + __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW, + __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW, + __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord, + __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW + ); +HRESULT DAPI WiuGetComponentPath( + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzComponentId, + __out INSTALLSTATE* pInstallState, + __out_z LPWSTR* psczValue + ); +HRESULT DAPI WiuLocateComponent( + __in_z LPCWSTR wzComponentId, + __out INSTALLSTATE* pInstallState, + __out_z LPWSTR* psczValue + ); +HRESULT DAPI WiuQueryFeatureState( + __in_z LPCWSTR wzProduct, + __in_z LPCWSTR wzFeature, + __out INSTALLSTATE* pInstallState + ); +HRESULT DAPI WiuGetProductInfo( + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ); +HRESULT DAPI WiuGetProductInfoEx( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ); +HRESULT DAPI WiuGetProductProperty( + __in MSIHANDLE hProduct, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ); +HRESULT DAPI WiuGetPatchInfoEx( + __in_z LPCWSTR wzPatchCode, + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ); +HRESULT DAPI WiuDeterminePatchSequence( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT context, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo, + __in DWORD cPatchInfo + ); +HRESULT DAPI WiuDetermineApplicablePatches( + __in_z LPCWSTR wzProductPackagePath, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo, + __in DWORD cPatchInfo + ); +HRESULT DAPI WiuEnumProducts( + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode + ); +HRESULT DAPI WiuEnumProductsEx( + __in_z_opt LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in DWORD dwContext, + __in DWORD dwIndex, + __out_opt WCHAR wzInstalledProductCode[39], + __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, + __out_opt LPWSTR wzSid, + __inout_opt LPDWORD pcchSid + ); +HRESULT DAPI WiuEnumRelatedProducts( + __in_z LPCWSTR wzUpgradeCode, + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode + ); +HRESULT DAPI WiuEnumRelatedProductCodes( + __in_z LPCWSTR wzUpgradeCode, + __deref_out_ecount_opt(*pcRelatedProducts) LPWSTR** prgsczProductCodes, + __out DWORD* pcRelatedProducts, + __in BOOL fReturnHighestVersionOnly + ); +HRESULT DAPI WiuEnableLog( + __in DWORD dwLogMode, + __in_z LPCWSTR wzLogFile, + __in DWORD dwLogAttributes + ); +HRESULT DAPI WiuInitializeInternalUI( + __in INSTALLUILEVEL internalUILevel, + __in_opt HWND hwndParent, + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ); +HRESULT DAPI WiuInitializeExternalUI( + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in INSTALLUILEVEL internalUILevel, + __in_opt HWND hwndParent, + __in LPVOID pvContext, + __in BOOL fRollback, + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ); +void DAPI WiuUninitializeExternalUI( + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ); +HRESULT DAPI WiuConfigureProductEx( + __in_z LPCWSTR wzProduct, + __in int iInstallLevel, + __in INSTALLSTATE eInstallState, + __in_z LPCWSTR wzCommandLine, + __out WIU_RESTART* pRestart + ); +HRESULT DAPI WiuInstallProduct( + __in_z LPCWSTR wzPackagPath, + __in_z LPCWSTR wzCommandLine, + __out WIU_RESTART* pRestart + ); +HRESULT DAPI WiuRemovePatches( + __in_z LPCWSTR wzPatchList, + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzPropertyList, + __out WIU_RESTART* pRestart + ); +HRESULT DAPI WiuSourceListAddSourceEx( + __in_z LPCWSTR wzProductCodeOrPatchCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in DWORD dwCode, + __in_z LPCWSTR wzSource, + __in_opt DWORD dwIndex + ); +HRESULT DAPI WiuBeginTransaction( + __in_z LPCWSTR szName, + __in DWORD dwTransactionAttributes, + __out MSIHANDLE* phTransactionHandle, + __out HANDLE* phChangeOfOwnerEvent, + __in DWORD dwLogMode, + __in_z LPCWSTR szLogPath + ); +HRESULT DAPI WiuEndTransaction( + __in DWORD dwTransactionState, + __in DWORD dwLogMode, + __in_z LPCWSTR szLogPath + ); +BOOL DAPI WiuIsMsiTransactionSupported( + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/wuautil.h b/src/libs/dutil/WixToolset.DUtil/inc/wuautil.h new file mode 100644 index 00000000..b239c4e6 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/wuautil.h @@ -0,0 +1,19 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +HRESULT DAPI WuaPauseAutomaticUpdates(); + +HRESULT DAPI WuaResumeAutomaticUpdates(); + +HRESULT DAPI WuaRestartRequired( + __out BOOL* pfRestartRequired + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/xmlutil.h b/src/libs/dutil/WixToolset.DUtil/inc/xmlutil.h new file mode 100644 index 00000000..ba92ada9 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/xmlutil.h @@ -0,0 +1,167 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument = {0x2933BF90, 0x7B36, 0x11d2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument20 = {0xF6D90F11, 0x9C73, 0x11D3, {0xB3, 0x2E, 0x00, 0xC0, 0x4F, 0x99, 0x0B, 0xB4}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument26 = {0xf5078f1b, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument30 = {0xf5078f32, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument40 = {0x88d969c0, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument50 = {0x88d969e5, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument60 = {0x88d96a05, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; +extern __declspec(selectany) const CLSID XmlUtil_CLSID_XMLSchemaCache = {0x88d969c2, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}}; + +extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument = {0x2933BF81, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; +extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument2 = {0x2933BF95, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}}; +extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMSchemaCollection = {0x373984C8, 0xB845, 0x449B, {0x91, 0xE7, 0x45, 0xAC, 0x83, 0x03, 0x6A, 0xDE}}; + +typedef enum XML_LOAD_ATTRIBUTE +{ + XML_LOAD_PRESERVE_WHITESPACE = 1, +} XML_LOAD_ATTRIBUTE; + + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT DAPI XmlInitialize(); +void DAPI XmlUninitialize(); + +HRESULT DAPI XmlCreateElement( + __in IXMLDOMDocument *pixdDocument, + __in_z LPCWSTR wzElementName, + __out IXMLDOMElement **ppixnElement + ); +HRESULT DAPI XmlCreateDocument( + __in_opt LPCWSTR pwzElementName, + __out IXMLDOMDocument** ppixdDocument, + __out_opt IXMLDOMElement** ppixeRootElement = NULL + ); +HRESULT DAPI XmlLoadDocument( + __in_z LPCWSTR wzDocument, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlLoadDocumentEx( + __in_z LPCWSTR wzDocument, + __in DWORD dwAttributes, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlLoadDocumentFromFile( + __in_z LPCWSTR wzPath, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlLoadDocumentFromBuffer( + __in_bcount(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlLoadDocumentFromFileEx( + __in_z LPCWSTR wzPath, + __in DWORD dwAttributes, + __out IXMLDOMDocument** ppixdDocument + ); +HRESULT DAPI XmlSelectSingleNode( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR wzXPath, + __out IXMLDOMNode **ppixnChild + ); +HRESULT DAPI XmlSetAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __in_z LPCWSTR pwzAttributeValue + ); +HRESULT DAPI XmlCreateTextNode( + __in IXMLDOMDocument *pixdDocument, + __in_z LPCWSTR wzText, + __out IXMLDOMText **ppixnTextNode + ); +HRESULT DAPI XmlGetText( + __in IXMLDOMNode* pixnNode, + __deref_out_z BSTR* pbstrText + ); +HRESULT DAPI XmlGetAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __deref_out_z BSTR* pbstrAttributeValue + ); +HRESULT DAPI XmlGetAttributeEx( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR wzAttribute, + __deref_out_z LPWSTR* psczAttributeValue + ); +HRESULT DAPI XmlGetYesNoAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR wzAttribute, + __out BOOL* pfYes + ); +HRESULT DAPI XmlGetAttributeNumber( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __out DWORD* pdwValue + ); +HRESULT DAPI XmlGetAttributeNumberBase( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __in int nBase, + __out DWORD* pdwValue + ); +HRESULT DAPI XmlGetAttributeLargeNumber( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __out DWORD64* pdw64Value + ); +HRESULT DAPI XmlGetNamedItem( + __in IXMLDOMNamedNodeMap *pixnmAttributes, + __in_opt LPCWSTR wzName, + __out IXMLDOMNode **ppixnNamedItem + ); +HRESULT DAPI XmlSetText( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzText + ); +HRESULT DAPI XmlSetTextNumber( + __in IXMLDOMNode *pixnNode, + __in DWORD dwValue + ); +HRESULT DAPI XmlCreateChild( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR pwzElementType, + __out IXMLDOMNode** ppixnChild + ); +HRESULT DAPI XmlRemoveAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute + ); +HRESULT DAPI XmlSelectNodes( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR wzXPath, + __out IXMLDOMNodeList **ppixnChild + ); +HRESULT DAPI XmlNextAttribute( + __in IXMLDOMNamedNodeMap* pixnnm, + __out IXMLDOMNode** pixnAttribute, + __deref_opt_out_z_opt BSTR* pbstrAttribute + ); +HRESULT DAPI XmlNextElement( + __in IXMLDOMNodeList* pixnl, + __out IXMLDOMNode** pixnElement, + __deref_opt_out_z_opt BSTR* pbstrElement + ); +HRESULT DAPI XmlRemoveChildren( + __in IXMLDOMNode* pixnSource, + __in_z LPCWSTR pwzXPath + ); +HRESULT DAPI XmlSaveDocument( + __in IXMLDOMDocument* pixdDocument, + __inout LPCWSTR wzPath + ); +HRESULT DAPI XmlSaveDocumentToBuffer( + __in IXMLDOMDocument* pixdDocument, + __deref_out_bcount(*pcbDest) BYTE** ppbDest, + __out DWORD* pcbDest + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/inetutil.cpp b/src/libs/dutil/WixToolset.DUtil/inetutil.cpp new file mode 100644 index 00000000..8dace55f --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inetutil.cpp @@ -0,0 +1,155 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define InetExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__) +#define InetExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INETUTIL, p, x, e, s, __VA_ARGS__) +#define InetExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INETUTIL, p, x, s, __VA_ARGS__) +#define InetExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INETUTIL, p, x, e, s, __VA_ARGS__) +#define InetExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INETUTIL, p, x, s, __VA_ARGS__) +#define InetExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INETUTIL, e, x, s, __VA_ARGS__) +#define InetExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INETUTIL, g, x, s, __VA_ARGS__) + + +/******************************************************************* + InternetGetSizeByHandle - returns size of file by url handle + +*******************************************************************/ +extern "C" HRESULT DAPI InternetGetSizeByHandle( + __in HINTERNET hiFile, + __out LONGLONG* pllSize + ) +{ + Assert(pllSize); + + HRESULT hr = S_OK; + DWORD dwSize = 0; + DWORD cb = 0; + + cb = sizeof(dwSize); + if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, reinterpret_cast(&dwSize), &cb, NULL)) + { + InetExitOnLastError(hr, "Failed to get size for internet file handle"); + } + + *pllSize = dwSize; +LExit: + return hr; +} + + +/******************************************************************* + InetGetCreateTimeByHandle - returns url creation time + +******************************************************************/ +extern "C" HRESULT DAPI InternetGetCreateTimeByHandle( + __in HINTERNET hiFile, + __out LPFILETIME pft + ) +{ + Assert(pft); + + HRESULT hr = S_OK; + SYSTEMTIME st = {0 }; + DWORD cb = sizeof(SYSTEMTIME); + + if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, reinterpret_cast(&st), &cb, NULL)) + { + InetExitWithLastError(hr, "failed to get create time for internet file handle"); + } + + if (!::SystemTimeToFileTime(&st, pft)) + { + InetExitWithLastError(hr, "failed to convert system time to file time"); + } + +LExit: + return hr; +} + + +/******************************************************************* + InternetQueryInfoString - query info string + +*******************************************************************/ +extern "C" HRESULT DAPI InternetQueryInfoString( + __in HINTERNET hRequest, + __in DWORD dwInfo, + __deref_out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + SIZE_T cbOriginal = 0; + DWORD cbValue = 0; + DWORD dwIndex = 0; + + // If nothing was provided start off with some arbitrary size. + if (!*psczValue) + { + hr = StrAlloc(psczValue, 64); + InetExitOnFailure(hr, "Failed to allocate memory for value."); + } + + hr = StrSize(*psczValue, &cbOriginal); + InetExitOnFailure(hr, "Failed to get size of value."); + + cbValue = (DWORD)min(DWORD_MAX, cbOriginal); + + if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), &cbValue, &dwIndex)) + { + DWORD er = ::GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == er) + { + cbValue += sizeof(WCHAR); // add one character for the null terminator. + + hr = StrAlloc(psczValue, cbValue / sizeof(WCHAR)); + InetExitOnFailure(hr, "Failed to allocate value."); + + if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast(*psczValue), &cbValue, &dwIndex)) + { + er = ::GetLastError(); + } + else + { + er = ERROR_SUCCESS; + } + } + + hr = HRESULT_FROM_WIN32(er); + InetExitOnRootFailure(hr, "Failed to get query information."); + } + +LExit: + return hr; +} + + +/******************************************************************* + InternetQueryInfoNumber - query info number + +*******************************************************************/ +extern "C" HRESULT DAPI InternetQueryInfoNumber( + __in HINTERNET hRequest, + __in DWORD dwInfo, + __inout LONG* plInfo + ) +{ + HRESULT hr = S_OK; + DWORD cbCode = sizeof(LONG); + DWORD dwIndex = 0; + + if (!::HttpQueryInfoW(hRequest, dwInfo | HTTP_QUERY_FLAG_NUMBER, static_cast(plInfo), &cbCode, &dwIndex)) + { + InetExitWithLastError(hr, "Failed to get query information."); + } + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/iniutil.cpp b/src/libs/dutil/WixToolset.DUtil/iniutil.cpp new file mode 100644 index 00000000..70b62995 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/iniutil.cpp @@ -0,0 +1,768 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define IniExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__) +#define IniExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__) +#define IniExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__) +#define IniExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__) +#define IniExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INIUTIL, e, x, s, __VA_ARGS__) +#define IniExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INIUTIL, g, x, s, __VA_ARGS__) + +const LPCWSTR wzSectionSeparator = L"\\"; + +struct INI_STRUCT +{ + LPWSTR sczPath; // the path to the INI file to be parsed + + LPWSTR sczOpenTagPrefix; // For regular ini, this would be '[' + LPWSTR sczOpenTagPostfix; // For regular ini, this would be ']' + + LPWSTR sczValuePrefix; // for regular ini, this would be NULL + LPWSTR sczValueSeparator; // for regular ini, this would be '=' + + LPWSTR *rgsczValueSeparatorExceptions; + DWORD cValueSeparatorExceptions; + + LPWSTR sczCommentLinePrefix; // for regular ini, this would be ';' + + INI_VALUE *rgivValues; + DWORD cValues; + + LPWSTR *rgsczLines; + DWORD cLines; + + FILE_ENCODING feEncoding; + BOOL fModified; +}; + +const int INI_HANDLE_BYTES = sizeof(INI_STRUCT); + +static HRESULT GetSectionPrefixFromName( + __in_z LPCWSTR wzName, + __deref_inout_z LPWSTR* psczOutput + ); +static void UninitializeIniValue( + INI_VALUE *pivValue + ); + +extern "C" HRESULT DAPI IniInitialize( + __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle + ) +{ + HRESULT hr = S_OK; + + // Allocate the handle + *piHandle = static_cast(MemAlloc(sizeof(INI_STRUCT), TRUE)); + IniExitOnNull(*piHandle, hr, E_OUTOFMEMORY, "Failed to allocate ini object"); + +LExit: + return hr; +} + +extern "C" void DAPI IniUninitialize( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle + ) +{ + INI_STRUCT *pi = static_cast(piHandle); + + ReleaseStr(pi->sczPath); + ReleaseStr(pi->sczOpenTagPrefix); + ReleaseStr(pi->sczOpenTagPostfix); + ReleaseStr(pi->sczValuePrefix); + ReleaseStr(pi->sczValueSeparator); + + for (DWORD i = 0; i < pi->cValueSeparatorExceptions; ++i) + { + ReleaseStr(pi->rgsczValueSeparatorExceptions + i); + } + + ReleaseStr(pi->sczCommentLinePrefix); + + for (DWORD i = 0; i < pi->cValues; ++i) + { + UninitializeIniValue(pi->rgivValues + i); + } + ReleaseMem(pi->rgivValues); + + ReleaseStrArray(pi->rgsczLines, pi->cLines); + + ReleaseMem(pi); +} + +extern "C" HRESULT DAPI IniSetOpenTag( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzOpenTagPrefix, + __in_z_opt LPCWSTR wzOpenTagPostfix + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + if (wzOpenTagPrefix) + { + hr = StrAllocString(&pi->sczOpenTagPrefix, wzOpenTagPrefix, 0); + IniExitOnFailure(hr, "Failed to copy open tag prefix to ini struct: %ls", wzOpenTagPrefix); + } + else + { + ReleaseNullStr(pi->sczOpenTagPrefix); + } + + if (wzOpenTagPostfix) + { + hr = StrAllocString(&pi->sczOpenTagPostfix, wzOpenTagPostfix, 0); + IniExitOnFailure(hr, "Failed to copy open tag postfix to ini struct: %ls", wzOpenTagPostfix); + } + else + { + ReleaseNullStr(pi->sczOpenTagPrefix); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetValueStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzValuePrefix, + __in_z_opt LPCWSTR wzValueSeparator + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + if (wzValuePrefix) + { + hr = StrAllocString(&pi->sczValuePrefix, wzValuePrefix, 0); + IniExitOnFailure(hr, "Failed to copy value prefix to ini struct: %ls", wzValuePrefix); + } + else + { + ReleaseNullStr(pi->sczValuePrefix); + } + + if (wzValueSeparator) + { + hr = StrAllocString(&pi->sczValueSeparator, wzValueSeparator, 0); + IniExitOnFailure(hr, "Failed to copy value separator to ini struct: %ls", wzValueSeparator); + } + else + { + ReleaseNullStr(pi->sczValueSeparator); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetValueSeparatorException( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z LPCWSTR wzValueNamePrefix + ) +{ + HRESULT hr = S_OK; + DWORD dwInsertedIndex = 0; + + INI_STRUCT *pi = static_cast(piHandle); + + hr = MemEnsureArraySize(reinterpret_cast(&pi->rgsczValueSeparatorExceptions), pi->cValueSeparatorExceptions + 1, sizeof(LPWSTR), 5); + IniExitOnFailure(hr, "Failed to increase array size for value separator exceptions"); + dwInsertedIndex = pi->cValueSeparatorExceptions; + ++pi->cValueSeparatorExceptions; + + hr = StrAllocString(&pi->rgsczValueSeparatorExceptions[dwInsertedIndex], wzValueNamePrefix, 0); + IniExitOnFailure(hr, "Failed to copy value separator exception"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetCommentStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzLinePrefix + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + if (wzLinePrefix) + { + hr = StrAllocString(&pi->sczCommentLinePrefix, wzLinePrefix, 0); + IniExitOnFailure(hr, "Failed to copy comment line prefix to ini struct: %ls", wzLinePrefix); + } + else + { + ReleaseNullStr(pi->sczCommentLinePrefix); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniParse( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzPath, + __out_opt FILE_ENCODING *pfeEncodingFound + ) +{ + HRESULT hr = S_OK; + DWORD dwValuePrefixLength = 0; + DWORD dwValueSeparatorExceptionLength = 0; + LPWSTR sczContents = NULL; + LPWSTR sczCurrentSection = NULL; + LPWSTR sczName = NULL; + LPWSTR sczNameTrimmed = NULL; + LPWSTR sczValue = NULL; + LPWSTR sczValueTrimmed = NULL; + LPWSTR wzOpenTagPrefix = NULL; + LPWSTR wzOpenTagPostfix = NULL; + LPWSTR wzValuePrefix = NULL; + LPWSTR wzValueNameStart = NULL; + LPWSTR wzValueSeparator = NULL; + LPWSTR wzCommentLinePrefix = NULL; + LPWSTR wzValueBegin = NULL; + LPCWSTR wzTemp = NULL; + + INI_STRUCT *pi = static_cast(piHandle); + + BOOL fSections = (NULL != pi->sczOpenTagPrefix) && (NULL != pi->sczOpenTagPostfix); + BOOL fValuePrefix = (NULL != pi->sczValuePrefix); + + hr = StrAllocString(&pi->sczPath, wzPath, 0); + IniExitOnFailure(hr, "Failed to copy path to ini struct: %ls", wzPath); + + hr = FileToString(pi->sczPath, &sczContents, &pi->feEncoding); + IniExitOnFailure(hr, "Failed to convert file to string: %ls", pi->sczPath); + + if (pfeEncodingFound) + { + *pfeEncodingFound = pi->feEncoding; + } + + if (!sczContents || !*sczContents) + { + // Empty string, nothing to parse + ExitFunction1(hr = S_OK); + } + + dwValuePrefixLength = lstrlenW(pi->sczValuePrefix); + hr = StrSplitAllocArray(&pi->rgsczLines, reinterpret_cast(&pi->cLines), sczContents, L"\n"); + IniExitOnFailure(hr, "Failed to split INI file into lines"); + + for (DWORD i = 0; i < pi->cLines; ++i) + { + if (!*pi->rgsczLines[i] || '\r' == *pi->rgsczLines[i]) + { + continue; + } + + if (pi->sczCommentLinePrefix) + { + wzCommentLinePrefix = wcsstr(pi->rgsczLines[i], pi->sczCommentLinePrefix); + + if (wzCommentLinePrefix && wzCommentLinePrefix <= pi->rgsczLines[i] + 1) + { + continue; + } + } + + if (pi->sczOpenTagPrefix) + { + wzOpenTagPrefix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPrefix); + if (wzOpenTagPrefix) + { + // If there is an open tag prefix but there is anything but whitespace before it, then it's NOT an open tag prefix + // This is important, for example, to support values with names like "Array[0]=blah" in INI format + for (wzTemp = pi->rgsczLines[i]; wzTemp < wzOpenTagPrefix; ++wzTemp) + { + if (*wzTemp != L' ' && *wzTemp != L'\t') + { + wzOpenTagPrefix = NULL; + break; + } + } + } + } + + if (pi->sczOpenTagPostfix) + { + wzOpenTagPostfix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPostfix); + } + + if (pi->sczValuePrefix) + { + wzValuePrefix = wcsstr(pi->rgsczLines[i], pi->sczValuePrefix); + if (wzValuePrefix != NULL) + { + wzValueNameStart = wzValuePrefix + dwValuePrefixLength; + } + } + else + { + wzValueNameStart = pi->rgsczLines[i]; + } + + if (pi->sczValueSeparator && NULL != wzValueNameStart && *wzValueNameStart != L'\0') + { + dwValueSeparatorExceptionLength = 0; + for (DWORD j = 0; j < pi->cValueSeparatorExceptions; ++j) + { + if (pi->rgsczLines[i] == wcsstr(pi->rgsczLines[i], pi->rgsczValueSeparatorExceptions[j])) + { + dwValueSeparatorExceptionLength = lstrlenW(pi->rgsczValueSeparatorExceptions[j]); + break; + } + } + + wzValueSeparator = wcsstr(wzValueNameStart + dwValueSeparatorExceptionLength, pi->sczValueSeparator); + } + + // Don't keep the endline + if (pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] == L'\r') + { + pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] = L'\0'; + } + + if (fSections && wzOpenTagPrefix && wzOpenTagPostfix && wzOpenTagPrefix < wzOpenTagPostfix && (NULL == wzCommentLinePrefix || wzOpenTagPrefix < wzCommentLinePrefix)) + { + // There is an section starting here, let's keep track of it and move on + hr = StrAllocString(&sczCurrentSection, wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix), wzOpenTagPostfix - (wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix))); + IniExitOnFailure(hr, "Failed to record section name for line: %ls of INI file: %ls", pi->rgsczLines[i], pi->sczPath); + + // Sections will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output + ReleaseNullStr(pi->rgsczLines[i]); + } + else if (wzValueSeparator && (NULL == wzCommentLinePrefix || wzValueSeparator < wzCommentLinePrefix) + && (!fValuePrefix || wzValuePrefix)) + { + if (fValuePrefix) + { + wzValueBegin = wzValuePrefix + lstrlenW(pi->sczValuePrefix); + } + else + { + wzValueBegin = pi->rgsczLines[i]; + } + + hr = MemEnsureArraySize(reinterpret_cast(&pi->rgivValues), pi->cValues + 1, sizeof(INI_VALUE), 100); + IniExitOnFailure(hr, "Failed to increase array size for value array"); + + if (sczCurrentSection) + { + hr = StrAllocString(&sczName, sczCurrentSection, 0); + IniExitOnFailure(hr, "Failed to copy current section name"); + + hr = StrAllocConcat(&sczName, wzSectionSeparator, 0); + IniExitOnFailure(hr, "Failed to copy current section name"); + } + + hr = StrAllocConcat(&sczName, wzValueBegin, wzValueSeparator - wzValueBegin); + IniExitOnFailure(hr, "Failed to copy name"); + + hr = StrAllocString(&sczValue, wzValueSeparator + lstrlenW(pi->sczValueSeparator), 0); + IniExitOnFailure(hr, "Failed to copy value"); + + hr = StrTrimWhitespace(&sczNameTrimmed, sczName); + IniExitOnFailure(hr, "Failed to trim whitespace from name"); + + hr = StrTrimWhitespace(&sczValueTrimmed, sczValue); + IniExitOnFailure(hr, "Failed to trim whitespace from value"); + + pi->rgivValues[pi->cValues].wzName = const_cast(sczNameTrimmed); + sczNameTrimmed = NULL; + pi->rgivValues[pi->cValues].wzValue = const_cast(sczValueTrimmed); + sczValueTrimmed = NULL; + pi->rgivValues[pi->cValues].dwLineNumber = i + 1; + + ++pi->cValues; + + // Values will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output + ReleaseNullStr(pi->rgsczLines[i]); + } + else + { + // Must be a comment, so ignore it and keep it in the list to output + } + + ReleaseNullStr(sczName); + } + +LExit: + ReleaseStr(sczCurrentSection); + ReleaseStr(sczContents); + ReleaseStr(sczName); + ReleaseStr(sczNameTrimmed); + ReleaseStr(sczValue); + ReleaseStr(sczValueTrimmed); + + return hr; +} + +extern "C" HRESULT DAPI IniGetValueList( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues, + __out DWORD *pcValues + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + *prgivValues = pi->rgivValues; + *pcValues = pi->cValues; + + return hr; +} + +extern "C" HRESULT DAPI IniGetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __deref_out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + INI_VALUE *pValue = NULL; + + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1)) + { + pValue = pi->rgivValues + i; + break; + } + } + + if (NULL == pValue) + { + hr = E_NOTFOUND; + IniExitOnFailure(hr, "Failed to check for INI value: %ls", wzValueName); + } + + if (NULL == pValue->wzValue) + { + ExitFunction1(hr = E_NOTFOUND); + } + + hr = StrAllocString(psczValue, pValue->wzValue, 0); + IniExitOnFailure(hr, "Failed to make copy of value while looking up INI value named: %ls", wzValueName); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __in_z_opt LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczSectionPrefix = NULL; // includes section name and backslash + LPWSTR sczName = NULL; + LPWSTR sczValue = NULL; + DWORD dwInsertIndex = DWORD_MAX; + + INI_STRUCT *pi = static_cast(piHandle); + INI_VALUE *pValue = NULL; + + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1)) + { + pValue = pi->rgivValues + i; + break; + } + } + + // We're killing the value + if (NULL == wzValue) + { + if (pValue && pValue->wzValue) + { + pi->fModified = TRUE; + sczValue = const_cast(pValue->wzValue); + pValue->wzValue = NULL; + ReleaseNullStr(sczValue); + } + + ExitFunction(); + } + else + { + if (pValue) + { + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, pValue->wzValue, -1, wzValue, -1)) + { + pi->fModified = TRUE; + hr = StrAllocString(const_cast(&pValue->wzValue), wzValue, 0); + IniExitOnFailure(hr, "Failed to update value INI value named: %ls", wzValueName); + } + + ExitFunction1(hr = S_OK); + } + else + { + if (wzValueName) + { + hr = GetSectionPrefixFromName(wzValueName, &sczSectionPrefix); + IniExitOnFailure(hr, "Failed to get section prefix from value name: %ls", wzValueName); + } + + // If we have a section prefix, figure out the index to insert it (at the end of the section it belongs in) + if (sczSectionPrefix) + { + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (0 == wcsncmp(pi->rgivValues[i].wzName, sczSectionPrefix, lstrlenW(sczSectionPrefix))) + { + dwInsertIndex = i; + } + else if (DWORD_MAX != dwInsertIndex) + { + break; + } + } + } + else + { + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (NULL == wcsstr(pi->rgivValues[i].wzName, wzSectionSeparator)) + { + dwInsertIndex = i; + } + else if (DWORD_MAX != dwInsertIndex) + { + break; + } + } + } + + // Otherwise, just add it to the end + if (DWORD_MAX == dwInsertIndex) + { + dwInsertIndex = pi->cValues; + } + + pi->fModified = TRUE; + hr = MemInsertIntoArray(reinterpret_cast(&pi->rgivValues), dwInsertIndex, 1, pi->cValues + 1, sizeof(INI_VALUE), 100); + IniExitOnFailure(hr, "Failed to insert value into array"); + + hr = StrAllocString(&sczName, wzValueName, 0); + IniExitOnFailure(hr, "Failed to copy name"); + + hr = StrAllocString(&sczValue, wzValue, 0); + IniExitOnFailure(hr, "Failed to copy value"); + + pi->rgivValues[dwInsertIndex].wzName = const_cast(sczName); + sczName = NULL; + pi->rgivValues[dwInsertIndex].wzValue = const_cast(sczValue); + sczValue = NULL; + + ++pi->cValues; + } + } + +LExit: + ReleaseStr(sczName); + ReleaseStr(sczValue); + + return hr; +} + +extern "C" HRESULT DAPI IniWriteFile( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzPath, + __in FILE_ENCODING feOverrideEncoding + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCurrentSectionPrefix = NULL; + LPWSTR sczNewSectionPrefix = NULL; + LPWSTR sczContents = NULL; + LPCWSTR wzName = NULL; + DWORD dwLineArrayIndex = 1; + FILE_ENCODING feEncoding; + + INI_STRUCT *pi = static_cast(piHandle); + + if (FILE_ENCODING_UNSPECIFIED == feOverrideEncoding) + { + feEncoding = pi->feEncoding; + } + else + { + feEncoding = feOverrideEncoding; + } + + if (FILE_ENCODING_UNSPECIFIED == feEncoding) + { + feEncoding = FILE_ENCODING_UTF16_WITH_BOM; + } + + if (!pi->fModified) + { + ExitFunction1(hr = S_OK); + } + if (NULL == wzPath && NULL == pi->sczPath) + { + ExitFunction1(hr = E_NOTFOUND); + } + + BOOL fSections = (pi->sczOpenTagPrefix) && (pi->sczOpenTagPostfix); + + hr = StrAllocString(&sczContents, L"", 0); + IniExitOnFailure(hr, "Failed to begin contents string as empty string"); + + // Insert any beginning lines we didn't understand like comments + if (0 < pi->cLines) + { + while (pi->rgsczLines[dwLineArrayIndex]) + { + hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex], 0); + IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + + ++dwLineArrayIndex; + } + } + + for (DWORD i = 0; i < pi->cValues; ++i) + { + // Skip if this value was killed off + if (NULL == pi->rgivValues[i].wzValue) + { + continue; + } + + // Now generate any lines for the current value like value line and maybe also a new section line before it + + // First see if we need to write a section line + hr = GetSectionPrefixFromName(pi->rgivValues[i].wzName, &sczNewSectionPrefix); + IniExitOnFailure(hr, "Failed to get section prefix from name: %ls", pi->rgivValues[i].wzName); + + // If the new section prefix is different, write a section out for it + if (fSections && sczNewSectionPrefix && (NULL == sczCurrentSectionPrefix || CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczNewSectionPrefix, -1, sczCurrentSectionPrefix, -1))) + { + hr = StrAllocConcat(&sczContents, pi->sczOpenTagPrefix, 0); + IniExitOnFailure(hr, "Failed to concat open tag prefix to string"); + + // Exclude section separator (i.e. backslash) from new section prefix + hr = StrAllocConcat(&sczContents, sczNewSectionPrefix, lstrlenW(sczNewSectionPrefix)-lstrlenW(wzSectionSeparator)); + IniExitOnFailure(hr, "Failed to concat section name to string"); + + hr = StrAllocConcat(&sczContents, pi->sczOpenTagPostfix, 0); + IniExitOnFailure(hr, "Failed to concat open tag postfix to string"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + + ReleaseNullStr(sczCurrentSectionPrefix); + sczCurrentSectionPrefix = sczNewSectionPrefix; + sczNewSectionPrefix = NULL; + } + + // Inserting lines we read before the current value if appropriate + while (pi->rgivValues[i].dwLineNumber > dwLineArrayIndex) + { + // Skip any lines were purposely forgot + if (NULL == pi->rgsczLines[dwLineArrayIndex]) + { + ++dwLineArrayIndex; + continue; + } + + hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex++], 0); + IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + } + + wzName = pi->rgivValues[i].wzName; + if (fSections) + { + wzName += lstrlenW(sczCurrentSectionPrefix); + } + + // OK, now just write the name/value pair, if it isn't deleted + if (pi->sczValuePrefix) + { + hr = StrAllocConcat(&sczContents, pi->sczValuePrefix, 0); + IniExitOnFailure(hr, "Failed to concat value prefix to ini output buffer"); + } + + hr = StrAllocConcat(&sczContents, wzName, 0); + IniExitOnFailure(hr, "Failed to concat value name to ini output buffer"); + + hr = StrAllocConcat(&sczContents, pi->sczValueSeparator, 0); + IniExitOnFailure(hr, "Failed to concat value separator to ini output buffer"); + + hr = StrAllocConcat(&sczContents, pi->rgivValues[i].wzValue, 0); + IniExitOnFailure(hr, "Failed to concat value to ini output buffer"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + } + + // If no path was specified, use the path to the file we parsed + if (NULL == wzPath) + { + wzPath = pi->sczPath; + } + + hr = FileFromString(wzPath, 0, sczContents, feEncoding); + IniExitOnFailure(hr, "Failed to write INI contents out to file: %ls", wzPath); + +LExit: + ReleaseStr(sczContents); + ReleaseStr(sczCurrentSectionPrefix); + ReleaseStr(sczNewSectionPrefix); + + return hr; +} + +static void UninitializeIniValue( + INI_VALUE *pivValue + ) +{ + ReleaseStr(const_cast(pivValue->wzName)); + ReleaseStr(const_cast(pivValue->wzValue)); +} + +static HRESULT GetSectionPrefixFromName( + __in_z LPCWSTR wzName, + __deref_inout_z LPWSTR* psczOutput + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzSectionDelimiter = NULL; + + ReleaseNullStr(*psczOutput); + + wzSectionDelimiter = wcsstr(wzName, wzSectionSeparator); + if (wzSectionDelimiter && wzSectionDelimiter != wzName) + { + hr = StrAllocString(psczOutput, wzName, wzSectionDelimiter - wzName + 1); + IniExitOnFailure(hr, "Failed to copy section prefix"); + } + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/jsonutil.cpp b/src/libs/dutil/WixToolset.DUtil/jsonutil.cpp new file mode 100644 index 00000000..3450ba59 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/jsonutil.cpp @@ -0,0 +1,687 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define JsonExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__) +#define JsonExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__) +#define JsonExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__) +#define JsonExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__) +#define JsonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__) +#define JsonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_JSONUTIL, e, x, s, __VA_ARGS__) +#define JsonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_JSONUTIL, g, x, s, __VA_ARGS__) + +const DWORD JSON_STACK_INCREMENT = 5; + +// Prototypes +static HRESULT DoStart( + __in JSON_WRITER* pWriter, + __in JSON_TOKEN tokenStart, + __in_z LPCWSTR wzStartString + ); +static HRESULT DoEnd( + __in JSON_WRITER* pWriter, + __in JSON_TOKEN tokenEnd, + __in_z LPCWSTR wzEndString + ); +static HRESULT DoKey( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzKey + ); +static HRESULT DoValue( + __in JSON_WRITER* pWriter, + __in_z_opt LPCWSTR wzValue + ); +static HRESULT EnsureTokenStack( + __in JSON_WRITER* pWriter + ); +static HRESULT SerializeJsonString( + __out_z LPWSTR* psczJsonString, + __in_z LPCWSTR wzString + ); + + + +DAPI_(HRESULT) JsonInitializeReader( + __in_z LPCWSTR wzJson, + __in JSON_READER* pReader + ) +{ + HRESULT hr = S_OK; + + memset(pReader, 0, sizeof(JSON_READER)); + ::InitializeCriticalSection(&pReader->cs); + + hr = StrAllocString(&pReader->sczJson, wzJson, 0); + JsonExitOnFailure(hr, "Failed to allocate json string."); + + pReader->pwz = pReader->sczJson; + +LExit: + return hr; +} + + +DAPI_(void) JsonUninitializeReader( + __in JSON_READER* pReader + ) +{ + ReleaseStr(pReader->sczJson); + + ::DeleteCriticalSection(&pReader->cs); + memset(pReader, 0, sizeof(JSON_READER)); +} + + +static HRESULT NextToken( + __in JSON_READER* pReader, + __out JSON_TOKEN* pToken + ) +{ + HRESULT hr = S_OK; + + // Skip whitespace. + while (L' ' == *pReader->pwz || + L'\t' == *pReader->pwz || + L'\r' == *pReader->pwz || + L'\n' == *pReader->pwz || + L',' == *pReader->pwz) + { + ++pReader->pwz; + } + + switch (*pReader->pwz) + { + case L'{': + *pToken = JSON_TOKEN_OBJECT_START; + break; + + case L':': // begin object value + *pToken = JSON_TOKEN_OBJECT_VALUE; + break; + + case L'}': // end object + *pToken = JSON_TOKEN_OBJECT_END; + break; + + case L'[': // begin array + *pToken = JSON_TOKEN_ARRAY_START; + break; + + case L']': // end array + *pToken = JSON_TOKEN_ARRAY_END; + break; + + case L'"': // string + *pToken = JSON_TOKEN_OBJECT_START == *pToken ? JSON_TOKEN_VALUE : JSON_TOKEN_OBJECT_KEY; + break; + + case L't': // true + case L'f': // false + case L'n': // null + case L'-': // number + case L'0': + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'8': + case L'9': + *pToken = JSON_TOKEN_VALUE; + break; + + case L'\0': + hr = E_NOMOREITEMS; + break; + + default: + hr = E_INVALIDSTATE; + } + + return hr; +} + + +DAPI_(HRESULT) JsonReadNext( + __in JSON_READER* pReader, + __out JSON_TOKEN* pToken, + __out JSON_VALUE* pValue + ) +{ + HRESULT hr = S_OK; + //WCHAR wz; + //JSON_TOKEN token = JSON_TOKEN_NONE; + + ::EnterCriticalSection(&pReader->cs); + + hr = NextToken(pReader, pToken); + if (E_NOMOREITEMS == hr) + { + ExitFunction(); + } + JsonExitOnFailure(hr, "Failed to get next token."); + + if (JSON_TOKEN_VALUE == *pToken) + { + hr = JsonReadValue(pReader, pValue); + } + else + { + ++pReader->pwz; + } + +LExit: + ::LeaveCriticalSection(&pReader->cs); + return hr; +} + + +DAPI_(HRESULT) JsonReadValue( + __in JSON_READER* /*pReader*/, + __in JSON_VALUE* /*pValue*/ + ) +{ + HRESULT hr = S_OK; + +//LExit: + return hr; +} + + +DAPI_(HRESULT) JsonInitializeWriter( + __in JSON_WRITER* pWriter + ) +{ + memset(pWriter, 0, sizeof(JSON_WRITER)); + ::InitializeCriticalSection(&pWriter->cs); + + return S_OK; +} + + +DAPI_(void) JsonUninitializeWriter( + __in JSON_WRITER* pWriter + ) +{ + ReleaseMem(pWriter->rgTokenStack); + ReleaseStr(pWriter->sczJson); + + ::DeleteCriticalSection(&pWriter->cs); + memset(pWriter, 0, sizeof(JSON_WRITER)); +} + + +DAPI_(HRESULT) JsonWriteBool( + __in JSON_WRITER* pWriter, + __in BOOL fValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + hr = StrAllocString(&sczValue, fValue ? L"true" : L"false", 0); + JsonExitOnFailure(hr, "Failed to convert boolean to string."); + + hr = DoValue(pWriter, sczValue); + JsonExitOnFailure(hr, "Failed to add boolean to JSON."); + +LExit: + ReleaseStr(sczValue); + return hr; +} + + +DAPI_(HRESULT) JsonWriteNumber( + __in JSON_WRITER* pWriter, + __in DWORD dwValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + hr = StrAllocFormatted(&sczValue, L"%u", dwValue); + JsonExitOnFailure(hr, "Failed to convert number to string."); + + hr = DoValue(pWriter, sczValue); + JsonExitOnFailure(hr, "Failed to add number to JSON."); + +LExit: + ReleaseStr(sczValue); + return hr; +} + + +DAPI_(HRESULT) JsonWriteString( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczJsonString = NULL; + + hr = SerializeJsonString(&sczJsonString, wzValue); + JsonExitOnFailure(hr, "Failed to allocate string JSON."); + + hr = DoValue(pWriter, sczJsonString); + JsonExitOnFailure(hr, "Failed to add string to JSON."); + +LExit: + ReleaseStr(sczJsonString); + return hr; +} + + +DAPI_(HRESULT) JsonWriteArrayStart( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + + hr = DoStart(pWriter, JSON_TOKEN_ARRAY_START, L"["); + JsonExitOnFailure(hr, "Failed to start JSON array."); + +LExit: + return hr; +} + + +DAPI_(HRESULT) JsonWriteArrayEnd( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + + hr = DoEnd(pWriter, JSON_TOKEN_ARRAY_END, L"]"); + JsonExitOnFailure(hr, "Failed to end JSON array."); + +LExit: + return hr; +} + + +DAPI_(HRESULT) JsonWriteObjectStart( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + + hr = DoStart(pWriter, JSON_TOKEN_OBJECT_START, L"{"); + JsonExitOnFailure(hr, "Failed to start JSON object."); + +LExit: + return hr; +} + + +DAPI_(HRESULT) JsonWriteObjectKey( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzKey + ) +{ + HRESULT hr = S_OK; + LPWSTR sczObjectKey = NULL; + + hr = StrAllocFormatted(&sczObjectKey, L"\"%ls\":", wzKey); + JsonExitOnFailure(hr, "Failed to allocate JSON object key."); + + hr = DoKey(pWriter, sczObjectKey); + JsonExitOnFailure(hr, "Failed to add object key to JSON."); + +LExit: + ReleaseStr(sczObjectKey); + return hr; +} + + +DAPI_(HRESULT) JsonWriteObjectEnd( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + + hr = DoEnd(pWriter, JSON_TOKEN_OBJECT_END, L"}"); + JsonExitOnFailure(hr, "Failed to end JSON object."); + +LExit: + return hr; +} + + +static HRESULT DoStart( + __in JSON_WRITER* pWriter, + __in JSON_TOKEN tokenStart, + __in_z LPCWSTR wzStartString + ) +{ + Assert(JSON_TOKEN_ARRAY_START == tokenStart || JSON_TOKEN_OBJECT_START == tokenStart); + + HRESULT hr = S_OK; + JSON_TOKEN token = JSON_TOKEN_NONE; + BOOL fNeedComma = FALSE; + BOOL fPushToken = TRUE; + + ::EnterCriticalSection(&pWriter->cs); + + hr = EnsureTokenStack(pWriter); + JsonExitOnFailure(hr, "Failed to ensure token stack for start."); + + token = pWriter->rgTokenStack[pWriter->cTokens - 1]; + switch (token) + { + case JSON_TOKEN_NONE: + token = tokenStart; + fPushToken = FALSE; + break; + + case JSON_TOKEN_ARRAY_START: // array start changes to array value. + token = JSON_TOKEN_ARRAY_VALUE; + break; + + case JSON_TOKEN_ARRAY_VALUE: + case JSON_TOKEN_ARRAY_END: + case JSON_TOKEN_OBJECT_END: + fNeedComma = TRUE; + break; + + default: // everything else is not allowed. + hr = E_UNEXPECTED; + break; + } + JsonExitOnRootFailure(hr, "Cannot start array or object to JSON serializer now."); + + if (fNeedComma) + { + hr = StrAllocConcat(&pWriter->sczJson, L",", 0); + JsonExitOnFailure(hr, "Failed to add comma for start array or object to JSON."); + } + + hr = StrAllocConcat(&pWriter->sczJson, wzStartString, 0); + JsonExitOnFailure(hr, "Failed to start JSON array or object."); + + pWriter->rgTokenStack[pWriter->cTokens - 1] = token; + if (fPushToken) + { + pWriter->rgTokenStack[pWriter->cTokens] = tokenStart; + ++pWriter->cTokens; + } + +LExit: + ::LeaveCriticalSection(&pWriter->cs); + return hr; +} + + +static HRESULT DoEnd( + __in JSON_WRITER* pWriter, + __in JSON_TOKEN tokenEnd, + __in_z LPCWSTR wzEndString + ) +{ + HRESULT hr = S_OK; + + ::EnterCriticalSection(&pWriter->cs); + + if (!pWriter->rgTokenStack || 0 == pWriter->cTokens) + { + hr = E_UNEXPECTED; + JsonExitOnRootFailure(hr, "Failure to pop token because the stack is empty."); + } + else + { + JSON_TOKEN token = pWriter->rgTokenStack[pWriter->cTokens - 1]; + if ((JSON_TOKEN_ARRAY_END == tokenEnd && JSON_TOKEN_ARRAY_START != token && JSON_TOKEN_ARRAY_VALUE != token) || + (JSON_TOKEN_OBJECT_END == tokenEnd && JSON_TOKEN_OBJECT_START != token && JSON_TOKEN_OBJECT_VALUE != token)) + { + hr = E_UNEXPECTED; + JsonExitOnRootFailure(hr, "Failure to pop token because the stack did not match the expected token: %d", tokenEnd); + } + } + + hr = StrAllocConcat(&pWriter->sczJson, wzEndString, 0); + JsonExitOnFailure(hr, "Failed to end JSON array or object."); + + --pWriter->cTokens; + +LExit: + ::LeaveCriticalSection(&pWriter->cs); + return hr; +} + + +static HRESULT DoKey( + __in JSON_WRITER* pWriter, + __in_z LPCWSTR wzKey + ) +{ + HRESULT hr = S_OK; + JSON_TOKEN token = JSON_TOKEN_NONE; + BOOL fNeedComma = FALSE; + + ::EnterCriticalSection(&pWriter->cs); + + hr = EnsureTokenStack(pWriter); + JsonExitOnFailure(hr, "Failed to ensure token stack for key."); + + token = pWriter->rgTokenStack[pWriter->cTokens - 1]; + switch (token) + { + case JSON_TOKEN_OBJECT_START: + token = JSON_TOKEN_OBJECT_KEY; + break; + + case JSON_TOKEN_OBJECT_VALUE: + token = JSON_TOKEN_OBJECT_KEY; + fNeedComma = TRUE; + break; + + default: // everything else is not allowed. + hr = E_UNEXPECTED; + break; + } + JsonExitOnRootFailure(hr, "Cannot add key to JSON serializer now."); + + if (fNeedComma) + { + hr = StrAllocConcat(&pWriter->sczJson, L",", 0); + JsonExitOnFailure(hr, "Failed to add comma for key to JSON."); + } + + hr = StrAllocConcat(&pWriter->sczJson, wzKey, 0); + JsonExitOnFailure(hr, "Failed to add key to JSON."); + + pWriter->rgTokenStack[pWriter->cTokens - 1] = token; + +LExit: + ::LeaveCriticalSection(&pWriter->cs); + return hr; +} + + +static HRESULT DoValue( + __in JSON_WRITER* pWriter, + __in_z_opt LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + JSON_TOKEN token = JSON_TOKEN_NONE; + BOOL fNeedComma = FALSE; + + ::EnterCriticalSection(&pWriter->cs); + + hr = EnsureTokenStack(pWriter); + JsonExitOnFailure(hr, "Failed to ensure token stack for value."); + + token = pWriter->rgTokenStack[pWriter->cTokens - 1]; + switch (token) + { + case JSON_TOKEN_ARRAY_START: + token = JSON_TOKEN_ARRAY_VALUE; + break; + + case JSON_TOKEN_ARRAY_VALUE: + fNeedComma = TRUE; + break; + + case JSON_TOKEN_OBJECT_KEY: + token = JSON_TOKEN_OBJECT_VALUE; + break; + + case JSON_TOKEN_NONE: + token = JSON_TOKEN_VALUE; + break; + + default: // everything else is not allowed. + hr = E_UNEXPECTED; + break; + } + JsonExitOnRootFailure(hr, "Cannot add value to JSON serializer now."); + + if (fNeedComma) + { + hr = StrAllocConcat(&pWriter->sczJson, L",", 0); + JsonExitOnFailure(hr, "Failed to add comma for value to JSON."); + } + + if (wzValue) + { + hr = StrAllocConcat(&pWriter->sczJson, wzValue, 0); + JsonExitOnFailure(hr, "Failed to add value to JSON."); + } + else + { + hr = StrAllocConcat(&pWriter->sczJson, L"null", 0); + JsonExitOnFailure(hr, "Failed to add null value to JSON."); + } + + pWriter->rgTokenStack[pWriter->cTokens - 1] = token; + +LExit: + ::LeaveCriticalSection(&pWriter->cs); + return hr; +} + + +static HRESULT EnsureTokenStack( + __in JSON_WRITER* pWriter + ) +{ + HRESULT hr = S_OK; + DWORD cNumAlloc = pWriter->cTokens != 0 ? pWriter->cTokens : 0; + + hr = MemEnsureArraySize(reinterpret_cast(&pWriter->rgTokenStack), cNumAlloc, sizeof(JSON_TOKEN), JSON_STACK_INCREMENT); + JsonExitOnFailure(hr, "Failed to allocate JSON token stack."); + + if (0 == pWriter->cTokens) + { + pWriter->rgTokenStack[0] = JSON_TOKEN_NONE; + ++pWriter->cTokens; + } + +LExit: + return hr; +} + + +static HRESULT SerializeJsonString( + __out_z LPWSTR* psczJsonString, + __in_z LPCWSTR wzString + ) +{ + HRESULT hr = S_OK; + DWORD cchRequired = 3; // start with enough space for null terminated empty quoted string (aka: ""\0) + + for (LPCWSTR pch = wzString; *pch; ++pch) + { + // If it is a special JSON character, add space for the escape backslash. + if (L'"' == *pch || L'\\' == *pch || L'/' == *pch || L'\b' == *pch || L'\f' == *pch || L'\n' == *pch || L'\r' == *pch || L'\t' == *pch) + { + ++cchRequired; + } + + ++cchRequired; + } + + hr = StrAlloc(psczJsonString, cchRequired); + JsonExitOnFailure(hr, "Failed to allocate space for JSON string."); + + LPWSTR pchTarget = *psczJsonString; + + *pchTarget = L'\"'; + ++pchTarget; + + for (LPCWSTR pch = wzString; *pch; ++pch, ++pchTarget) + { + // If it is a special JSON character, handle it or just add the character as is. + switch (*pch) + { + case L'"': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'"'; + break; + + case L'\\': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'\\'; + break; + + case L'/': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'/'; + break; + + case L'\b': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'b'; + break; + + case L'\f': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'f'; + break; + + case L'\n': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'n'; + break; + + case L'\r': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L'r'; + break; + + case L'\t': + *pchTarget = L'\\'; + ++pchTarget; + *pchTarget = L't'; + break; + + default: + *pchTarget = *pch; + break; + } + + } + + *pchTarget = L'\"'; + ++pchTarget; + *pchTarget = L'\0'; + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/locutil.cpp b/src/libs/dutil/WixToolset.DUtil/locutil.cpp new file mode 100644 index 00000000..c4567c03 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/locutil.cpp @@ -0,0 +1,628 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define LocExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__) +#define LocExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_LOCUTIL, p, x, e, s, __VA_ARGS__) +#define LocExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, p, x, s, __VA_ARGS__) +#define LocExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_LOCUTIL, p, x, e, s, __VA_ARGS__) +#define LocExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, p, x, s, __VA_ARGS__) +#define LocExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_LOCUTIL, e, x, s, __VA_ARGS__) +#define LocExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_LOCUTIL, g, x, s, __VA_ARGS__) + +// prototypes +static HRESULT ParseWxl( + __in IXMLDOMDocument* pixd, + __out WIX_LOCALIZATION** ppWixLoc + ); +static HRESULT ParseWxlStrings( + __in IXMLDOMElement* pElement, + __in WIX_LOCALIZATION* pWixLoc + ); +static HRESULT ParseWxlControls( + __in IXMLDOMElement* pElement, + __in WIX_LOCALIZATION* pWixLoc + ); +static HRESULT ParseWxlString( + __in IXMLDOMNode* pixn, + __in DWORD dwIdx, + __in WIX_LOCALIZATION* pWixLoc + ); +static HRESULT ParseWxlControl( + __in IXMLDOMNode* pixn, + __in DWORD dwIdx, + __in WIX_LOCALIZATION* pWixLoc + ); + +// from Winnls.h +#ifndef MUI_LANGUAGE_ID +#define MUI_LANGUAGE_ID 0x4 // Use traditional language ID convention +#endif +#ifndef MUI_MERGE_USER_FALLBACK +#define MUI_MERGE_USER_FALLBACK 0x20 // GetThreadPreferredUILanguages merges in user preferred languages +#endif +#ifndef MUI_MERGE_SYSTEM_FALLBACK +#define MUI_MERGE_SYSTEM_FALLBACK 0x10 // GetThreadPreferredUILanguages merges in parent and base languages +#endif +typedef WINBASEAPI BOOL (WINAPI *GET_THREAD_PREFERRED_UI_LANGUAGES) ( + __in DWORD dwFlags, + __out PULONG pulNumLanguages, + __out_ecount_opt(*pcchLanguagesBuffer) PZZWSTR pwszLanguagesBuffer, + __inout PULONG pcchLanguagesBuffer +); + +extern "C" HRESULT DAPI LocProbeForFile( + __in_z LPCWSTR wzBasePath, + __in_z LPCWSTR wzLocFileName, + __in_z_opt LPCWSTR wzLanguage, + __inout LPWSTR* psczPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczProbePath = NULL; + LANGID langid = 0; + LPWSTR sczLangIdFile = NULL; + LPWSTR sczLangsBuff = NULL; + GET_THREAD_PREFERRED_UI_LANGUAGES pvfnGetThreadPreferredUILanguages = + reinterpret_cast( + GetProcAddress(GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages")); + + // If a language was specified, look for a loc file in that as a directory. + if (wzLanguage && *wzLanguage) + { + hr = PathConcat(wzBasePath, wzLanguage, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat base path to language."); + + hr = PathConcat(sczProbePath, wzLocFileName, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat loc file name to probe path."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + } + + if (pvfnGetThreadPreferredUILanguages) + { + ULONG nLangs; + ULONG cchLangs = 0; + DWORD dwFlags = MUI_LANGUAGE_ID | MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK; + if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs)) + { + LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size."); + } + + hr = StrAlloc(&sczLangsBuff, cchLangs); + LocExitOnFailure(hr, "Failed to allocate buffer for languages"); + + nLangs = 0; + if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs)) + { + LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list."); + } + + LPWSTR szLangs = sczLangsBuff; + for (ULONG i = 0; i < nLangs; ++i, szLangs += 5) + { + // StrHexDecode assumes low byte is first. We'll need to swap the bytes once we parse out the value. + hr = StrHexDecode(szLangs, reinterpret_cast(&langid), sizeof(langid)); + LocExitOnFailure(hr, "Failed to parse langId."); + + langid = MAKEWORD(HIBYTE(langid), LOBYTE(langid)); + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + LocExitOnFailure(hr, "Failed to format user preferred langid."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat user preferred langid file name to base path."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + } + } + + langid = ::GetUserDefaultUILanguage(); + + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + LocExitOnFailure(hr, "Failed to format user langid."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat user langid file name to base path."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + + if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) + { + langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); + + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + LocExitOnFailure(hr, "Failed to format user langid (default sublang)."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + } + + langid = ::GetSystemDefaultUILanguage(); + + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + LocExitOnFailure(hr, "Failed to format system langid."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat system langid file name to base path."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + + if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid) + { + langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT); + + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); + LocExitOnFailure(hr, "Failed to format user langid (default sublang)."); + + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang)."); + + if (FileExistsEx(sczProbePath, NULL)) + { + ExitFunction(); + } + } + + // Finally, look for the loc file in the base path. + hr = PathConcat(wzBasePath, wzLocFileName, &sczProbePath); + LocExitOnFailure(hr, "Failed to concat loc file name to base path."); + + if (!FileExistsEx(sczProbePath, NULL)) + { + hr = E_FILENOTFOUND; + } + +LExit: + if (SUCCEEDED(hr)) + { + hr = StrAllocString(psczPath, sczProbePath, 0); + } + + ReleaseStr(sczLangIdFile); + ReleaseStr(sczProbePath); + ReleaseStr(sczLangsBuff); + + return hr; +} + +extern "C" HRESULT DAPI LocLoadFromFile( + __in_z LPCWSTR wzWxlFile, + __out WIX_LOCALIZATION** ppWixLoc + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixd = NULL; + + hr = XmlLoadDocumentFromFile(wzWxlFile, &pixd); + LocExitOnFailure(hr, "Failed to load WXL file as XML document."); + + hr = ParseWxl(pixd, ppWixLoc); + LocExitOnFailure(hr, "Failed to parse WXL."); + +LExit: + ReleaseObject(pixd); + + return hr; +} + +extern "C" HRESULT DAPI LocLoadFromResource( + __in HMODULE hModule, + __in_z LPCSTR szResource, + __out WIX_LOCALIZATION** ppWixLoc + ) +{ + HRESULT hr = S_OK; + LPVOID pvResource = NULL; + DWORD cbResource = 0; + LPWSTR sczXml = NULL; + IXMLDOMDocument* pixd = NULL; + + hr = ResReadData(hModule, szResource, &pvResource, &cbResource); + LocExitOnFailure(hr, "Failed to read theme from resource."); + + hr = StrAllocStringAnsi(&sczXml, reinterpret_cast(pvResource), cbResource, CP_UTF8); + LocExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); + + hr = XmlLoadDocument(sczXml, &pixd); + LocExitOnFailure(hr, "Failed to load theme resource as XML document."); + + hr = ParseWxl(pixd, ppWixLoc); + LocExitOnFailure(hr, "Failed to parse WXL."); + +LExit: + ReleaseObject(pixd); + ReleaseStr(sczXml); + + return hr; +} + +extern "C" void DAPI LocFree( + __in_opt WIX_LOCALIZATION* pWixLoc + ) +{ + if (pWixLoc) + { + for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx) + { + ReleaseStr(pWixLoc->rgLocStrings[idx].wzId); + ReleaseStr(pWixLoc->rgLocStrings[idx].wzText); + } + + for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx) + { + ReleaseStr(pWixLoc->rgLocControls[idx].wzControl); + ReleaseStr(pWixLoc->rgLocControls[idx].wzText); + } + + ReleaseMem(pWixLoc->rgLocStrings); + ReleaseMem(pWixLoc->rgLocControls); + ReleaseMem(pWixLoc); + } +} + +extern "C" HRESULT DAPI LocLocalizeString( + __in const WIX_LOCALIZATION* pWixLoc, + __inout LPWSTR* ppsczInput + ) +{ + Assert(ppsczInput && pWixLoc); + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i) + { + hr = StrReplaceStringAll(ppsczInput, pWixLoc->rgLocStrings[i].wzId, pWixLoc->rgLocStrings[i].wzText); + LocExitOnFailure(hr, "Localizing string failed."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI LocGetControl( + __in const WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __out LOC_CONTROL** ppLocControl + ) +{ + HRESULT hr = S_OK; + LOC_CONTROL* pLocControl = NULL; + + for (DWORD i = 0; i < pWixLoc->cLocControls; ++i) + { + pLocControl = &pWixLoc->rgLocControls[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocControl->wzControl, -1, wzId, -1)) + { + *ppLocControl = pLocControl; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +extern "C" HRESULT DAPI LocGetString( + __in const WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __out LOC_STRING** ppLocString + ) +{ + HRESULT hr = E_NOTFOUND; + LOC_STRING* pLocString = NULL; + + for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i) + { + pLocString = pWixLoc->rgLocStrings + i; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocString->wzId, -1, wzId, -1)) + { + *ppLocString = pLocString; + hr = S_OK; + break; + } + } + + return hr; +} + +extern "C" HRESULT DAPI LocAddString( + __in WIX_LOCALIZATION* pWixLoc, + __in_z LPCWSTR wzId, + __in_z LPCWSTR wzLocString, + __in BOOL bOverridable + ) +{ + HRESULT hr = S_OK; + + ++pWixLoc->cLocStrings; + pWixLoc->rgLocStrings = static_cast(MemReAlloc(pWixLoc->rgLocStrings, sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE)); + LocExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to reallocate memory for localization strings."); + + LOC_STRING* pLocString = pWixLoc->rgLocStrings + (pWixLoc->cLocStrings - 1); + + hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", wzId); + LocExitOnFailure(hr, "Failed to set localization string Id."); + + hr = StrAllocString(&pLocString->wzText, wzLocString, 0); + LocExitOnFailure(hr, "Failed to set localization string Text."); + + pLocString->bOverridable = bOverridable; + +LExit: + return hr; +} + +// helper functions + +static HRESULT ParseWxl( + __in IXMLDOMDocument* pixd, + __out WIX_LOCALIZATION** ppWixLoc + ) +{ + HRESULT hr = S_OK; + IXMLDOMElement *pWxlElement = NULL; + WIX_LOCALIZATION* pWixLoc = NULL; + + pWixLoc = static_cast(MemAlloc(sizeof(WIX_LOCALIZATION), TRUE)); + LocExitOnNull(pWixLoc, hr, E_OUTOFMEMORY, "Failed to allocate memory for Wxl file."); + + // read the WixLocalization tag + hr = pixd->get_documentElement(&pWxlElement); + LocExitOnFailure(hr, "Failed to get localization element."); + + // get the Language attribute if present + pWixLoc->dwLangId = WIX_LOCALIZATION_LANGUAGE_NOT_SET; + hr = XmlGetAttributeNumber(pWxlElement, L"Language", &pWixLoc->dwLangId); + if (S_FALSE == hr) + { + hr = S_OK; + } + LocExitOnFailure(hr, "Failed to get Language value."); + + // store the strings and controls in a node list + hr = ParseWxlStrings(pWxlElement, pWixLoc); + LocExitOnFailure(hr, "Parsing localization strings failed."); + + hr = ParseWxlControls(pWxlElement, pWixLoc); + LocExitOnFailure(hr, "Parsing localization controls failed."); + + *ppWixLoc = pWixLoc; + pWixLoc = NULL; + +LExit: + ReleaseObject(pWxlElement); + ReleaseMem(pWixLoc); + + return hr; +} + + +static HRESULT ParseWxlStrings( + __in IXMLDOMElement* pElement, + __in WIX_LOCALIZATION* pWixLoc + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + IXMLDOMNodeList* pixnl = NULL; + DWORD dwIdx = 0; + + hr = XmlSelectNodes(pElement, L"String", &pixnl); + LocExitOnLastError(hr, "Failed to get String child nodes of Wxl File."); + + hr = pixnl->get_length(reinterpret_cast(&pWixLoc->cLocStrings)); + LocExitOnLastError(hr, "Failed to get number of String child nodes in Wxl File."); + + if (0 < pWixLoc->cLocStrings) + { + pWixLoc->rgLocStrings = static_cast(MemAlloc(sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE)); + LocExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to allocate memory for localization strings."); + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) + { + hr = ParseWxlString(pixn, dwIdx, pWixLoc); + LocExitOnFailure(hr, "Failed to parse localization string."); + + ++dwIdx; + ReleaseNullObject(pixn); + } + + hr = S_OK; + LocExitOnFailure(hr, "Failed to enumerate all localization strings."); + } + +LExit: + if (FAILED(hr) && pWixLoc->rgLocStrings) + { + for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx) + { + ReleaseStr(pWixLoc->rgLocStrings[idx].wzId); + ReleaseStr(pWixLoc->rgLocStrings[idx].wzText); + } + + ReleaseMem(pWixLoc->rgLocStrings); + } + + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + +static HRESULT ParseWxlControls( + __in IXMLDOMElement* pElement, + __in WIX_LOCALIZATION* pWixLoc + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + IXMLDOMNodeList* pixnl = NULL; + DWORD dwIdx = 0; + + hr = XmlSelectNodes(pElement, L"UI|Control", &pixnl); + LocExitOnLastError(hr, "Failed to get UI child nodes of Wxl File."); + + hr = pixnl->get_length(reinterpret_cast(&pWixLoc->cLocControls)); + LocExitOnLastError(hr, "Failed to get number of UI child nodes in Wxl File."); + + if (0 < pWixLoc->cLocControls) + { + pWixLoc->rgLocControls = static_cast(MemAlloc(sizeof(LOC_CONTROL) * pWixLoc->cLocControls, TRUE)); + LocExitOnNull(pWixLoc->rgLocControls, hr, E_OUTOFMEMORY, "Failed to allocate memory for localized controls."); + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) + { + hr = ParseWxlControl(pixn, dwIdx, pWixLoc); + LocExitOnFailure(hr, "Failed to parse localized control."); + + ++dwIdx; + ReleaseNullObject(pixn); + } + + hr = S_OK; + LocExitOnFailure(hr, "Failed to enumerate all localized controls."); + } + +LExit: + if (FAILED(hr) && pWixLoc->rgLocControls) + { + for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx) + { + ReleaseStr(pWixLoc->rgLocControls[idx].wzControl); + ReleaseStr(pWixLoc->rgLocControls[idx].wzText); + } + + ReleaseMem(pWixLoc->rgLocControls); + } + + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + +static HRESULT ParseWxlString( + __in IXMLDOMNode* pixn, + __in DWORD dwIdx, + __in WIX_LOCALIZATION* pWixLoc + ) +{ + HRESULT hr = S_OK; + LOC_STRING* pLocString = NULL; + BSTR bstrText = NULL; + + pLocString = pWixLoc->rgLocStrings + dwIdx; + + // Id + hr = XmlGetAttribute(pixn, L"Id", &bstrText); + LocExitOnFailure(hr, "Failed to get Xml attribute Id in Wxl file."); + + hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", bstrText); + LocExitOnFailure(hr, "Failed to duplicate Xml attribute Id in Wxl file."); + + ReleaseNullBSTR(bstrText); + + // Overrideable + hr = XmlGetAttribute(pixn, L"Overridable", &bstrText); + LocExitOnFailure(hr, "Failed to get Xml attribute Overridable."); + + if (S_OK == hr) + { + pLocString->bOverridable = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrText, -1, L"yes", -1); + } + + ReleaseNullBSTR(bstrText); + + // Text + hr = XmlGetText(pixn, &bstrText); + LocExitOnFailure(hr, "Failed to get Xml text in Wxl file."); + + hr = StrAllocString(&pLocString->wzText, bstrText, 0); + LocExitOnFailure(hr, "Failed to duplicate Xml text in Wxl file."); + +LExit: + ReleaseBSTR(bstrText); + + return hr; +} + +static HRESULT ParseWxlControl( + __in IXMLDOMNode* pixn, + __in DWORD dwIdx, + __in WIX_LOCALIZATION* pWixLoc + ) +{ + HRESULT hr = S_OK; + LOC_CONTROL* pLocControl = NULL; + BSTR bstrText = NULL; + + pLocControl = pWixLoc->rgLocControls + dwIdx; + + // Id + hr = XmlGetAttribute(pixn, L"Control", &bstrText); + LocExitOnFailure(hr, "Failed to get Xml attribute Control in Wxl file."); + + hr = StrAllocString(&pLocControl->wzControl, bstrText, 0); + LocExitOnFailure(hr, "Failed to duplicate Xml attribute Control in Wxl file."); + + ReleaseNullBSTR(bstrText); + + // X + pLocControl->nX = LOC_CONTROL_NOT_SET; + hr = XmlGetAttributeNumber(pixn, L"X", reinterpret_cast(&pLocControl->nX)); + LocExitOnFailure(hr, "Failed to get control X attribute."); + + // Y + pLocControl->nY = LOC_CONTROL_NOT_SET; + hr = XmlGetAttributeNumber(pixn, L"Y", reinterpret_cast(&pLocControl->nY)); + LocExitOnFailure(hr, "Failed to get control Y attribute."); + + // Width + pLocControl->nWidth = LOC_CONTROL_NOT_SET; + hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast(&pLocControl->nWidth)); + LocExitOnFailure(hr, "Failed to get control width attribute."); + + // Height + pLocControl->nHeight = LOC_CONTROL_NOT_SET; + hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pLocControl->nHeight)); + LocExitOnFailure(hr, "Failed to get control height attribute."); + + // Text + hr = XmlGetText(pixn, &bstrText); + LocExitOnFailure(hr, "Failed to get control text in Wxl file."); + + hr = StrAllocString(&pLocControl->wzText, bstrText, 0); + LocExitOnFailure(hr, "Failed to duplicate control text in Wxl file."); + +LExit: + ReleaseBSTR(bstrText); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/logutil.cpp b/src/libs/dutil/WixToolset.DUtil/logutil.cpp new file mode 100644 index 00000000..ac68036a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/logutil.cpp @@ -0,0 +1,961 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define LoguExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__) +#define LoguExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__) +#define LoguExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__) +#define LoguExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__) +#define LoguExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__) +#define LoguExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_LOGUTIL, e, x, s, __VA_ARGS__) +#define LoguExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_LOGUTIL, g, x, s, __VA_ARGS__) + +// globals +static HMODULE LogUtil_hModule = NULL; +static BOOL LogUtil_fDisabled = FALSE; +static HANDLE LogUtil_hLog = INVALID_HANDLE_VALUE; +static LPWSTR LogUtil_sczLogPath = NULL; +static LPSTR LogUtil_sczPreInitBuffer = NULL; +static REPORT_LEVEL LogUtil_rlCurrent = REPORT_STANDARD; +static CRITICAL_SECTION LogUtil_csLog = { }; +static BOOL LogUtil_fInitializedCriticalSection = FALSE; + +// Customization of certain parts of the string, within a line +static LPWSTR LogUtil_sczSpecialBeginLine = NULL; +static LPWSTR LogUtil_sczSpecialEndLine = NULL; +static LPWSTR LogUtil_sczSpecialAfterTimeStamp = NULL; + +static LPCSTR LOGUTIL_UNKNOWN = "unknown"; +static LPCSTR LOGUTIL_WARNING = "warning"; +static LPCSTR LOGUTIL_STANDARD = "standard"; +static LPCSTR LOGUTIL_VERBOSE = "verbose"; +static LPCSTR LOGUTIL_DEBUG = "debug"; +static LPCSTR LOGUTIL_NONE = "none"; + +// prototypes +static HRESULT LogIdWork( + __in REPORT_LEVEL rl, + __in_opt HMODULE hModule, + __in DWORD dwLogId, + __in va_list args, + __in BOOL fLOGUTIL_NEWLINE + ); +static HRESULT LogStringWorkArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args, + __in BOOL fLOGUTIL_NEWLINE + ); +static HRESULT LogStringWork( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_z LPCWSTR sczString, + __in BOOL fLOGUTIL_NEWLINE + ); + +// Hook to allow redirecting LogStringWorkRaw function calls +static PFN_LOGSTRINGWORKRAW s_vpfLogStringWorkRaw = NULL; +static LPVOID s_vpvLogStringWorkRawContext = NULL; + + +/******************************************************************** + IsLogInitialized - Checks if log is currently initialized. +********************************************************************/ +extern "C" BOOL DAPI IsLogInitialized() +{ + return LogUtil_fInitializedCriticalSection; +} + +/******************************************************************** + IsLogOpen - Checks if log is currently initialized and open. +********************************************************************/ +extern "C" BOOL DAPI IsLogOpen() +{ + return (INVALID_HANDLE_VALUE != LogUtil_hLog && NULL != LogUtil_sczLogPath); +} + + +/******************************************************************** + LogInitialize - initializes the logutil API + +********************************************************************/ +extern "C" void DAPI LogInitialize( + __in HMODULE hModule + ) +{ + AssertSz(INVALID_HANDLE_VALUE == LogUtil_hLog && !LogUtil_sczLogPath, "LogInitialize() or LogOpen() - already called."); + + LogUtil_hModule = hModule; + LogUtil_fDisabled = FALSE; + + ::InitializeCriticalSection(&LogUtil_csLog); + LogUtil_fInitializedCriticalSection = TRUE; +} + + +/******************************************************************** + LogOpen - creates an application log file + + NOTE: if wzExt is null then wzLog is path to desired log else wzLog and wzExt are used to generate log name +********************************************************************/ +extern "C" HRESULT DAPI LogOpen( + __in_z_opt LPCWSTR wzDirectory, + __in_z LPCWSTR wzLog, + __in_z_opt LPCWSTR wzPostfix, + __in_z_opt LPCWSTR wzExt, + __in BOOL fAppend, + __in BOOL fHeader, + __out_z_opt LPWSTR* psczLogPath + ) +{ + HRESULT hr = S_OK; + BOOL fEnteredCriticalSection = FALSE; + LPWSTR sczLogDirectory = NULL; + + ::EnterCriticalSection(&LogUtil_csLog); + fEnteredCriticalSection = TRUE; + + if (wzExt && *wzExt) + { + hr = PathCreateTimeBasedTempFile(wzDirectory, wzLog, wzPostfix, wzExt, &LogUtil_sczLogPath, &LogUtil_hLog); + LoguExitOnFailure(hr, "Failed to create log based on current system time."); + } + else + { + hr = PathConcat(wzDirectory, wzLog, &LogUtil_sczLogPath); + LoguExitOnFailure(hr, "Failed to combine the log path."); + + hr = PathGetDirectory(LogUtil_sczLogPath, &sczLogDirectory); + LoguExitOnFailure(hr, "Failed to get log directory."); + + hr = DirEnsureExists(sczLogDirectory, NULL); + LoguExitOnFailure(hr, "Failed to ensure log file directory exists: %ls", sczLogDirectory); + + LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, (fAppend) ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == LogUtil_hLog) + { + LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); + } + + if (fAppend) + { + ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END); + } + } + + LogUtil_fDisabled = FALSE; + + if (fHeader) + { + LogHeader(); + } + + if (NULL != LogUtil_sczPreInitBuffer) + { + // Log anything that was logged before LogOpen() was called. + LogStringWorkRaw(LogUtil_sczPreInitBuffer); + ReleaseNullStr(LogUtil_sczPreInitBuffer); + } + + if (psczLogPath) + { + hr = StrAllocString(psczLogPath, LogUtil_sczLogPath, 0); + LoguExitOnFailure(hr, "Failed to copy log path."); + } + +LExit: + if (fEnteredCriticalSection) + { + ::LeaveCriticalSection(&LogUtil_csLog); + } + + ReleaseStr(sczLogDirectory); + + return hr; +} + + +/******************************************************************** + LogDisable - closes any open files and disables in memory logging. + +********************************************************************/ +void DAPI LogDisable() +{ + ::EnterCriticalSection(&LogUtil_csLog); + + LogUtil_fDisabled = TRUE; + + ReleaseFileHandle(LogUtil_hLog); + ReleaseNullStr(LogUtil_sczLogPath); + ReleaseNullStr(LogUtil_sczPreInitBuffer); + + ::LeaveCriticalSection(&LogUtil_csLog); +} + + +/******************************************************************** + LogRedirect - Redirects all logging strings to the specified + function - or set NULL to disable the hook +********************************************************************/ +void DAPI LogRedirect( + __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw, + __in_opt LPVOID pvContext + ) +{ + s_vpfLogStringWorkRaw = vpfLogStringWorkRaw; + s_vpvLogStringWorkRawContext = pvContext; +} + + +/******************************************************************** + LogRename - Renames a logfile, moving its contents to a new path, + and re-opening the file for appending at the new + location +********************************************************************/ +HRESULT DAPI LogRename( + __in_z LPCWSTR wzNewPath + ) +{ + HRESULT hr = S_OK; + BOOL fEnteredCriticalSection = FALSE; + + ::EnterCriticalSection(&LogUtil_csLog); + fEnteredCriticalSection = TRUE; + + ReleaseFileHandle(LogUtil_hLog); + + hr = FileEnsureMove(LogUtil_sczLogPath, wzNewPath, TRUE, TRUE); + LoguExitOnFailure(hr, "Failed to move logfile to new location: %ls", wzNewPath); + + hr = StrAllocString(&LogUtil_sczLogPath, wzNewPath, 0); + LoguExitOnFailure(hr, "Failed to store new logfile path: %ls", wzNewPath); + + LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == LogUtil_hLog) + { + LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath); + } + + // Enable "append" mode by moving file pointer to the end + ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END); + +LExit: + if (fEnteredCriticalSection) + { + ::LeaveCriticalSection(&LogUtil_csLog); + } + + return hr; +} + + +extern "C" void DAPI LogClose( + __in BOOL fFooter + ) +{ + if (INVALID_HANDLE_VALUE != LogUtil_hLog && fFooter) + { + LogFooter(); + } + + ReleaseFileHandle(LogUtil_hLog); + ReleaseNullStr(LogUtil_sczLogPath); + ReleaseNullStr(LogUtil_sczPreInitBuffer); +} + + +extern "C" void DAPI LogUninitialize( + __in BOOL fFooter + ) +{ + LogClose(fFooter); + + if (LogUtil_fInitializedCriticalSection) + { + ::DeleteCriticalSection(&LogUtil_csLog); + LogUtil_fInitializedCriticalSection = FALSE; + } + + LogUtil_hModule = NULL; + LogUtil_fDisabled = FALSE; + + ReleaseNullStr(LogUtil_sczSpecialBeginLine); + ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp); + ReleaseNullStr(LogUtil_sczSpecialEndLine); +} + + +/******************************************************************** + LogIsOpen - returns whether log file is open or note + +********************************************************************/ +extern "C" BOOL DAPI LogIsOpen() +{ + return INVALID_HANDLE_VALUE != LogUtil_hLog; +} + + +/******************************************************************** + LogSetSpecialParams - sets a special beginline string, endline + string, post-timestamp string, etc. +********************************************************************/ +HRESULT DAPI LogSetSpecialParams( + __in_z_opt LPCWSTR wzSpecialBeginLine, + __in_z_opt LPCWSTR wzSpecialAfterTimeStamp, + __in_z_opt LPCWSTR wzSpecialEndLine + ) +{ + HRESULT hr = S_OK; + + // Handle special string to be prepended before every full line + if (NULL == wzSpecialBeginLine) + { + ReleaseNullStr(LogUtil_sczSpecialBeginLine); + } + else + { + hr = StrAllocConcat(&LogUtil_sczSpecialBeginLine, wzSpecialBeginLine, 0); + LoguExitOnFailure(hr, "Failed to allocate copy of special beginline string"); + } + + // Handle special string to be appended to every time stamp + if (NULL == wzSpecialAfterTimeStamp) + { + ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp); + } + else + { + hr = StrAllocConcat(&LogUtil_sczSpecialAfterTimeStamp, wzSpecialAfterTimeStamp, 0); + LoguExitOnFailure(hr, "Failed to allocate copy of special post-timestamp string"); + } + + // Handle special string to be appended before every full line + if (NULL == wzSpecialEndLine) + { + ReleaseNullStr(LogUtil_sczSpecialEndLine); + } + else + { + hr = StrAllocConcat(&LogUtil_sczSpecialEndLine, wzSpecialEndLine, 0); + LoguExitOnFailure(hr, "Failed to allocate copy of special endline string"); + } + +LExit: + return hr; +} + +/******************************************************************** + LogSetLevel - sets the logging level + + NOTE: returns previous logging level +********************************************************************/ +extern "C" REPORT_LEVEL DAPI LogSetLevel( + __in REPORT_LEVEL rl, + __in BOOL fLogChange + ) +{ + AssertSz(REPORT_ERROR != rl, "REPORT_ERROR is not a valid logging level to set"); + + REPORT_LEVEL rlPrev = LogUtil_rlCurrent; + + if (LogUtil_rlCurrent != rl) + { + LogUtil_rlCurrent = rl; + + if (fLogChange) + { + LPCSTR szLevel = LOGUTIL_UNKNOWN; + switch (LogUtil_rlCurrent) + { + case REPORT_WARNING: + szLevel = LOGUTIL_WARNING; + break; + case REPORT_STANDARD: + szLevel = LOGUTIL_STANDARD; + break; + case REPORT_VERBOSE: + szLevel = LOGUTIL_VERBOSE; + break; + case REPORT_DEBUG: + szLevel = LOGUTIL_DEBUG; + break; + case REPORT_NONE: + szLevel = LOGUTIL_NONE; + break; + } + + LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel); + } + } + + return rlPrev; +} + + +/******************************************************************** + LogGetLevel - gets the current logging level + +********************************************************************/ +extern "C" REPORT_LEVEL DAPI LogGetLevel() +{ + return LogUtil_rlCurrent; +} + + +/******************************************************************** + LogGetPath - gets the current log path + +********************************************************************/ +extern "C" HRESULT DAPI LogGetPath( + __out_ecount_z(cchLogPath) LPWSTR pwzLogPath, + __in DWORD cchLogPath + ) +{ + Assert(pwzLogPath); + + HRESULT hr = S_OK; + + if (NULL == LogUtil_sczLogPath) // they can't have a path if there isn't one! + { + ExitFunction1(hr = E_UNEXPECTED); + } + + hr = ::StringCchCopyW(pwzLogPath, cchLogPath, LogUtil_sczLogPath); + +LExit: + return hr; +} + + +/******************************************************************** + LogGetHandle - gets the current log file handle + +********************************************************************/ +extern "C" HANDLE DAPI LogGetHandle() +{ + return LogUtil_hLog; +} + + +/******************************************************************** + LogString - write a string to the log + + NOTE: use printf formatting ("%ls", "%d", etc.) +********************************************************************/ +extern "C" HRESULT DAPIV LogString( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, szFormat); + hr = LogStringArgs(rl, szFormat, args); + va_end(args); + + return hr; +} + +extern "C" HRESULT DAPI LogStringArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); + HRESULT hr = S_OK; + + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) + { + ExitFunction1(hr = S_FALSE); + } + + hr = LogStringWorkArgs(rl, szFormat, args, FALSE); + +LExit: + return hr; +} + +/******************************************************************** + LogStringLine - write a string plus LOGUTIL_NEWLINE to the log + + NOTE: use printf formatting ("%ls", "%d", etc.) +********************************************************************/ +extern "C" HRESULT DAPIV LogStringLine( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, szFormat); + hr = LogStringLineArgs(rl, szFormat, args); + va_end(args); + + return hr; +} + +extern "C" HRESULT DAPI LogStringLineArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); + HRESULT hr = S_OK; + + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) + { + ExitFunction1(hr = S_FALSE); + } + + hr = LogStringWorkArgs(rl, szFormat, args, TRUE); + +LExit: + return hr; +} + +/******************************************************************** + LogIdModuleArgs - write a string embedded in a MESSAGETABLE to the log + + NOTE: uses format string from MESSAGETABLE resource +********************************************************************/ + +extern "C" HRESULT DAPI LogIdModuleArgs( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + __in va_list args + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); + HRESULT hr = S_OK; + + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) + { + ExitFunction1(hr = S_FALSE); + } + + hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI LogIdModule( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + ... + ) +{ + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level"); + HRESULT hr = S_OK; + va_list args; + + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl) + { + ExitFunction1(hr = S_FALSE); + } + + va_start(args, hModule); + hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE); + va_end(args); + +LExit: + return hr; +} + + + + +/******************************************************************** + LogError - write an error to the log + + NOTE: use printf formatting ("%ls", "%d", etc.) +********************************************************************/ +extern "C" HRESULT DAPIV LogErrorString( + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + + va_list args; + va_start(args, szFormat); + hr = LogErrorStringArgs(hrError, szFormat, args); + va_end(args); + + return hr; +} + +extern "C" HRESULT DAPI LogErrorStringArgs( + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFormat = NULL; + LPWSTR sczMessage = NULL; + + hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP); + LoguExitOnFailure(hr, "Failed to convert format string to wide character string"); + + // format the string as a unicode string - this is necessary to be able to include + // international characters in our output string. This does have the counterintuitive effect + // that the caller's "%s" is interpreted differently + // (so callers should use %hs for LPSTR and %ls for LPWSTR) + hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args); + LoguExitOnFailure(hr, "Failed to format error message: \"%ls\"", sczFormat); + + hr = LogStringLine(REPORT_ERROR, "Error 0x%x: %ls", hrError, sczMessage); + +LExit: + ReleaseStr(sczFormat); + ReleaseStr(sczMessage); + + return hr; +} + + +/******************************************************************** + LogErrorIdModule - write an error string embedded in a MESSAGETABLE to the log + + NOTE: uses format string from MESSAGETABLE resource + can log no more than three strings in the error message +********************************************************************/ +extern "C" HRESULT DAPI LogErrorIdModule( + __in HRESULT hrError, + __in DWORD dwLogId, + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzString1 = NULL, + __in_z_opt LPCWSTR wzString2 = NULL, + __in_z_opt LPCWSTR wzString3 = NULL + ) +{ + HRESULT hr = S_OK; + WCHAR wzError[11]; + WORD cStrings = 1; // guaranteed wzError is in the list + + hr = ::StringCchPrintfW(wzError, countof(wzError), L"0x%08x", hrError); + LoguExitOnFailure(hr, "failed to format error code: \"0%08x\"", hrError); + + cStrings += wzString1 ? 1 : 0; + cStrings += wzString2 ? 1 : 0; + cStrings += wzString3 ? 1 : 0; + + hr = LogIdModule(REPORT_ERROR, dwLogId, hModule, wzError, wzString1, wzString2, wzString3); + LoguExitOnFailure(hr, "Failed to log id module."); + +LExit: + return hr; +} + +/******************************************************************** + LogHeader - write a standard header to the log + +********************************************************************/ +extern "C" HRESULT DAPI LogHeader() +{ + HRESULT hr = S_OK; + WCHAR wzComputerName[MAX_PATH]; + DWORD cchComputerName = countof(wzComputerName); + WCHAR wzPath[MAX_PATH]; + DWORD dwMajorVersion = 0; + DWORD dwMinorVersion = 0; + LPCSTR szLevel = LOGUTIL_UNKNOWN; + LPWSTR sczCurrentDateTime = NULL; + + // + // get the interesting data + // + if (!::GetModuleFileNameW(NULL, wzPath, countof(wzPath))) + { + memset(wzPath, 0, sizeof(wzPath)); + } + + hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion); + if (FAILED(hr)) + { + dwMajorVersion = 0; + dwMinorVersion = 0; + } + + if (!::GetComputerNameW(wzComputerName, &cchComputerName)) + { + ::SecureZeroMemory(wzComputerName, sizeof(wzComputerName)); + } + + TimeCurrentDateTime(&sczCurrentDateTime, FALSE); + + // + // write data to the log + // + LogStringLine(REPORT_STANDARD, "=== Logging started: %ls ===", sczCurrentDateTime); + LogStringLine(REPORT_STANDARD, "Executable: %ls v%d.%d.%d.%d", wzPath, dwMajorVersion >> 16, dwMajorVersion & 0xFFFF, dwMinorVersion >> 16, dwMinorVersion & 0xFFFF); + LogStringLine(REPORT_STANDARD, "Computer : %ls", wzComputerName); + switch (LogUtil_rlCurrent) + { + case REPORT_WARNING: + szLevel = LOGUTIL_WARNING; + break; + case REPORT_STANDARD: + szLevel = LOGUTIL_STANDARD; + break; + case REPORT_VERBOSE: + szLevel = LOGUTIL_VERBOSE; + break; + case REPORT_DEBUG: + szLevel = LOGUTIL_DEBUG; + break; + case REPORT_NONE: + szLevel = LOGUTIL_NONE; + break; + } + LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel); + + hr = S_OK; + + ReleaseStr(sczCurrentDateTime); + + return hr; +} + + +/******************************************************************** + LogFooterWork - write a standard footer to the log + +********************************************************************/ + +static HRESULT LogFooterWork( + __in_z __format_string LPCSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + + va_list args; + va_start(args, szFormat); + hr = LogStringWorkArgs(REPORT_STANDARD, szFormat, args, TRUE); + va_end(args); + + return hr; +} + +extern "C" HRESULT DAPI LogFooter() +{ + HRESULT hr = S_OK; + LPWSTR sczCurrentDateTime = NULL; + TimeCurrentDateTime(&sczCurrentDateTime, FALSE); + hr = LogFooterWork("=== Logging stopped: %ls ===", sczCurrentDateTime); + ReleaseStr(sczCurrentDateTime); + return hr; +} + +/******************************************************************** + LogStringWorkRaw - Write a raw, unformatted string to the log + +********************************************************************/ +extern "C" HRESULT LogStringWorkRaw( + __in_z LPCSTR szLogData + ) +{ + Assert(szLogData && *szLogData); + + HRESULT hr = S_OK; + size_t cchLogData = 0; + DWORD cbLogData = 0; + DWORD cbTotal = 0; + DWORD cbWrote = 0; + + hr = ::StringCchLengthA(szLogData, STRSAFE_MAX_CCH, &cchLogData); + LoguExitOnRootFailure(hr, "Failed to get length of raw string"); + + cbLogData = (DWORD)cchLogData; + + // If the log hasn't been initialized yet, store it in a buffer + if (INVALID_HANDLE_VALUE == LogUtil_hLog) + { + hr = StrAnsiAllocConcat(&LogUtil_sczPreInitBuffer, szLogData, 0); + LoguExitOnFailure(hr, "Failed to concatenate string to pre-init buffer"); + + ExitFunction1(hr = S_OK); + } + + // write the string + while (cbTotal < cbLogData) + { + if (!::WriteFile(LogUtil_hLog, reinterpret_cast(szLogData) + cbTotal, cbLogData - cbTotal, &cbWrote, NULL)) + { + LoguExitOnLastError(hr, "Failed to write output to log: %ls - %hs", LogUtil_sczLogPath, szLogData); + } + + cbTotal += cbWrote; + } + +LExit: + return hr; +} + +// +// private worker functions +// +static HRESULT LogIdWork( + __in REPORT_LEVEL rl, + __in_opt HMODULE hModule, + __in DWORD dwLogId, + __in va_list args, + __in BOOL fLOGUTIL_NEWLINE + ) +{ + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + DWORD cch = 0; + + // get the string for the id +#pragma prefast(push) +#pragma prefast(disable:25028) +#pragma prefast(disable:25068) + cch = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE, + static_cast(hModule), dwLogId, 0, reinterpret_cast(&pwz), 0, &args); +#pragma prefast(pop) + + if (0 == cch) + { + LoguExitOnLastError(hr, "failed to log id: %d", dwLogId); + } + + if (2 <= cch && L'\r' == pwz[cch-2] && L'\n' == pwz[cch-1]) + { + pwz[cch-2] = L'\0'; // remove newline from message table + } + + LogStringWork(rl, dwLogId, pwz, fLOGUTIL_NEWLINE); + +LExit: + if (pwz) + { + ::LocalFree(pwz); + } + + return hr; +} + + +static HRESULT LogStringWorkArgs( + __in REPORT_LEVEL rl, + __in_z __format_string LPCSTR szFormat, + __in va_list args, + __in BOOL fLOGUTIL_NEWLINE + ) +{ + Assert(szFormat && *szFormat); + + HRESULT hr = S_OK; + LPWSTR sczFormat = NULL; + LPWSTR sczMessage = NULL; + + hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP); + LoguExitOnFailure(hr, "Failed to convert format string to wide character string"); + + // format the string as a unicode string + hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args); + LoguExitOnFailure(hr, "Failed to format message: \"%ls\"", sczFormat); + + hr = LogStringWork(rl, 0, sczMessage, fLOGUTIL_NEWLINE); + LoguExitOnFailure(hr, "Failed to write formatted string to log:%ls", sczMessage); + +LExit: + ReleaseStr(sczFormat); + ReleaseStr(sczMessage); + + return hr; +} + + +static HRESULT LogStringWork( + __in REPORT_LEVEL rl, + __in DWORD dwLogId, + __in_z LPCWSTR sczString, + __in BOOL fLOGUTIL_NEWLINE + ) +{ + Assert(sczString && *sczString); + + HRESULT hr = S_OK; + BOOL fEnteredCriticalSection = FALSE; + LPWSTR scz = NULL; + LPCWSTR wzLogData = NULL; + LPSTR sczMultiByte = NULL; + + // If logging is disabled, just bail. + if (LogUtil_fDisabled) + { + ExitFunction(); + } + + ::EnterCriticalSection(&LogUtil_csLog); + fEnteredCriticalSection = TRUE; + + if (fLOGUTIL_NEWLINE) + { + // get the process and thread id. + DWORD dwProcessId = ::GetCurrentProcessId(); + DWORD dwThreadId = ::GetCurrentThreadId(); + + // get the time relative to GMT. + SYSTEMTIME st = { }; + ::GetLocalTime(&st); + + DWORD dwId = dwLogId & 0xFFFFFFF; + DWORD dwType = dwLogId & 0xF0000000; + LPSTR szType = (0xE0000000 == dwType || REPORT_ERROR == rl) ? "e" : (0xA0000000 == dwType || REPORT_WARNING == rl) ? "w" : "i"; + + // add line prefix and trailing newline + hr = StrAllocFormatted(&scz, L"%ls[%04X:%04X][%04hu-%02hu-%02huT%02hu:%02hu:%02hu]%hs%03d:%ls %ls%ls", LogUtil_sczSpecialBeginLine ? LogUtil_sczSpecialBeginLine : L"", + dwProcessId, dwThreadId, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, szType, dwId, + LogUtil_sczSpecialAfterTimeStamp ? LogUtil_sczSpecialAfterTimeStamp : L"", sczString, LogUtil_sczSpecialEndLine ? LogUtil_sczSpecialEndLine : L"\r\n"); + LoguExitOnFailure(hr, "Failed to format line prefix."); + } + + wzLogData = scz ? scz : sczString; + + // Convert to UTF-8 before writing out to the log file + hr = StrAnsiAllocString(&sczMultiByte, wzLogData, 0, CP_UTF8); + LoguExitOnFailure(hr, "Failed to convert log string to UTF-8"); + + if (s_vpfLogStringWorkRaw) + { + hr = s_vpfLogStringWorkRaw(sczMultiByte, s_vpvLogStringWorkRawContext); + LoguExitOnFailure(hr, "Failed to write string to log using redirected function: %ls", sczString); + } + else + { + hr = LogStringWorkRaw(sczMultiByte); + LoguExitOnFailure(hr, "Failed to write string to log using default function: %ls", sczString); + } + +LExit: + if (fEnteredCriticalSection) + { + ::LeaveCriticalSection(&LogUtil_csLog); + } + + ReleaseStr(scz); + ReleaseStr(sczMultiByte); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/memutil.cpp b/src/libs/dutil/WixToolset.DUtil/memutil.cpp new file mode 100644 index 00000000..c805a9c0 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/memutil.cpp @@ -0,0 +1,336 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define MemExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__) +#define MemExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__) +#define MemExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__) +#define MemExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__) +#define MemExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__) +#define MemExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MEMUTIL, e, x, s, __VA_ARGS__) +#define MemExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MEMUTIL, g, x, s, __VA_ARGS__) + + +#if DEBUG +static BOOL vfMemInitialized = FALSE; +#endif + +extern "C" HRESULT DAPI MemInitialize() +{ +#if DEBUG + vfMemInitialized = TRUE; +#endif + return S_OK; +} + +extern "C" void DAPI MemUninitialize() +{ +#if DEBUG + vfMemInitialized = FALSE; +#endif +} + +extern "C" LPVOID DAPI MemAlloc( + __in SIZE_T cbSize, + __in BOOL fZero + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + AssertSz(0 < cbSize, "MemAlloc() called with invalid size"); + return ::HeapAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, cbSize); +} + + +extern "C" LPVOID DAPI MemReAlloc( + __in LPVOID pv, + __in SIZE_T cbSize, + __in BOOL fZero + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + AssertSz(0 < cbSize, "MemReAlloc() called with invalid size"); + return ::HeapReAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, pv, cbSize); +} + + +extern "C" HRESULT DAPI MemReAllocSecure( + __in LPVOID pv, + __in SIZE_T cbSize, + __in BOOL fZero, + __deref_out LPVOID* ppvNew + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + AssertSz(ppvNew, "MemReAllocSecure() called with uninitialized pointer"); + AssertSz(0 < cbSize, "MemReAllocSecure() called with invalid size"); + + HRESULT hr = S_OK; + DWORD dwFlags = HEAP_REALLOC_IN_PLACE_ONLY; + LPVOID pvNew = NULL; + + dwFlags |= fZero ? HEAP_ZERO_MEMORY : 0; + pvNew = ::HeapReAlloc(::GetProcessHeap(), dwFlags, pv, cbSize); + if (!pvNew) + { + pvNew = MemAlloc(cbSize, fZero); + if (pvNew) + { + const SIZE_T cbCurrent = MemSize(pv); + if (-1 == cbCurrent) + { + MemExitOnRootFailure(hr = E_INVALIDARG, "Failed to get memory size"); + } + + // HeapReAlloc may allocate more memory than requested. + const SIZE_T cbNew = MemSize(pvNew); + if (-1 == cbNew) + { + MemExitOnRootFailure(hr = E_INVALIDARG, "Failed to get memory size"); + } + + cbSize = cbNew; + if (cbSize > cbCurrent) + { + cbSize = cbCurrent; + } + + memcpy_s(pvNew, cbNew, pv, cbSize); + + SecureZeroMemory(pv, cbCurrent); + MemFree(pv); + } + } + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to reallocate memory"); + + *ppvNew = pvNew; + pvNew = NULL; + +LExit: + ReleaseMem(pvNew); + + return hr; +} + + +extern "C" HRESULT DAPI MemAllocArray( + __inout LPVOID* ppvArray, + __in SIZE_T cbArrayType, + __in DWORD dwItemCount + ) +{ + return MemReAllocArray(ppvArray, 0, cbArrayType, dwItemCount); +} + + +extern "C" HRESULT DAPI MemReAllocArray( + __inout LPVOID* ppvArray, + __in DWORD cArray, + __in SIZE_T cbArrayType, + __in DWORD dwNewItemCount + ) +{ + HRESULT hr = S_OK; + DWORD cNew = 0; + LPVOID pvNew = NULL; + SIZE_T cbNew = 0; + + hr = ::DWordAdd(cArray, dwNewItemCount, &cNew); + MemExitOnFailure(hr, "Integer overflow when calculating new element count."); + + hr = ::SIZETMult(cNew, cbArrayType, &cbNew); + MemExitOnFailure(hr, "Integer overflow when calculating new block size."); + + if (*ppvArray) + { + SIZE_T cbCurrent = MemSize(*ppvArray); + if (cbCurrent < cbNew) + { + pvNew = MemReAlloc(*ppvArray, cbNew, TRUE); + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate larger array."); + + *ppvArray = pvNew; + } + } + else + { + pvNew = MemAlloc(cbNew, TRUE); + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); + + *ppvArray = pvNew; + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI MemEnsureArraySize( + __deref_inout_bcount(cArray * cbArrayType) LPVOID* ppvArray, + __in DWORD cArray, + __in SIZE_T cbArrayType, + __in DWORD dwGrowthCount + ) +{ + HRESULT hr = S_OK; + DWORD cNew = 0; + LPVOID pvNew = NULL; + SIZE_T cbNew = 0; + + hr = ::DWordAdd(cArray, dwGrowthCount, &cNew); + MemExitOnFailure(hr, "Integer overflow when calculating new element count."); + + hr = ::SIZETMult(cNew, cbArrayType, &cbNew); + MemExitOnFailure(hr, "Integer overflow when calculating new block size."); + + if (*ppvArray) + { + SIZE_T cbUsed = cArray * cbArrayType; + SIZE_T cbCurrent = MemSize(*ppvArray); + if (cbCurrent < cbUsed) + { + pvNew = MemReAlloc(*ppvArray, cbNew, TRUE); + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate array larger."); + + *ppvArray = pvNew; + } + } + else + { + pvNew = MemAlloc(cbNew, TRUE); + MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array."); + + *ppvArray = pvNew; + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI MemInsertIntoArray( + __deref_inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray, + __in DWORD dwInsertIndex, + __in DWORD cInsertItems, + __in DWORD cExistingArray, + __in SIZE_T cbArrayType, + __in DWORD dwGrowthCount + ) +{ + HRESULT hr = S_OK; + DWORD i; + BYTE *pbArray = NULL; + + if (0 == cInsertItems) + { + ExitFunction1(hr = S_OK); + } + + hr = MemEnsureArraySize(ppvArray, cExistingArray + cInsertItems, cbArrayType, dwGrowthCount); + MemExitOnFailure(hr, "Failed to resize array while inserting items"); + + pbArray = reinterpret_cast(*ppvArray); + for (i = cExistingArray + cInsertItems - 1; i > dwInsertIndex; --i) + { + memcpy_s(pbArray + i * cbArrayType, cbArrayType, pbArray + (i - 1) * cbArrayType, cbArrayType); + } + + // Zero out the newly-inserted items + memset(pbArray + dwInsertIndex * cbArrayType, 0, cInsertItems * cbArrayType); + +LExit: + return hr; +} + +extern "C" void DAPI MemRemoveFromArray( + __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray, + __in DWORD dwRemoveIndex, + __in DWORD cRemoveItems, + __in DWORD cExistingArray, + __in SIZE_T cbArrayType, + __in BOOL fPreserveOrder + ) +{ + BYTE *pbArray = static_cast(pvArray); + DWORD cItemsLeftAfterRemoveIndex = (cExistingArray - cRemoveItems - dwRemoveIndex); + + if (fPreserveOrder) + { + memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (dwRemoveIndex + cRemoveItems) * cbArrayType, cItemsLeftAfterRemoveIndex * cbArrayType); + } + else + { + DWORD cItemsToMove = (cRemoveItems > cItemsLeftAfterRemoveIndex ? cItemsLeftAfterRemoveIndex : cRemoveItems); + memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (cExistingArray - cItemsToMove) * cbArrayType, cItemsToMove * cbArrayType); + } + + ZeroMemory(pbArray + (cExistingArray - cRemoveItems) * cbArrayType, cRemoveItems * cbArrayType); +} + +extern "C" void DAPI MemArraySwapItems( + __inout_bcount(cbArrayType) LPVOID pvArray, + __in DWORD dwIndex1, + __in DWORD dwIndex2, + __in SIZE_T cbArrayType + ) +{ + BYTE *pbArrayItem1 = static_cast(pvArray) + dwIndex1 * cbArrayType; + BYTE *pbArrayItem2 = static_cast(pvArray) + dwIndex2 * cbArrayType; + DWORD dwByteIndex = 0; + + if (dwIndex1 == dwIndex2) + { + return; + } + + // Use XOR swapping to avoid the need for a temporary item + while (dwByteIndex < cbArrayType) + { + // Try to do many bytes at a time in most cases + if (cbArrayType - dwByteIndex > sizeof(DWORD64)) + { + // x: X xor Y + *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + // y: X xor Y + *(reinterpret_cast(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + // x: X xor Y + *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + + dwByteIndex += sizeof(DWORD64); + } + else + { + // x: X xor Y + *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + // y: X xor Y + *(reinterpret_cast(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + // x: X xor Y + *(reinterpret_cast(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast(pbArrayItem2 + dwByteIndex)); + + dwByteIndex += sizeof(unsigned char); + } + } +} + +extern "C" HRESULT DAPI MemFree( + __in LPVOID pv + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + return ::HeapFree(::GetProcessHeap(), 0, pv) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()); +} + + +extern "C" SIZE_T DAPI MemSize( + __in LPCVOID pv + ) +{ +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash"); + return ::HeapSize(::GetProcessHeap(), 0, pv); +} diff --git a/src/libs/dutil/WixToolset.DUtil/metautil.cpp b/src/libs/dutil/WixToolset.DUtil/metautil.cpp new file mode 100644 index 00000000..f313fc1c --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/metautil.cpp @@ -0,0 +1,378 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// okay, this may look a little weird, but metautil.h cannot be in the +// pre-compiled header because we need to #define these things so the +// correct GUID's get pulled into this object file +#include +#include "metautil.h" + + +// Exit macros +#define MetaExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__) +#define MetaExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__) +#define MetaExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__) +#define MetaExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__) +#define MetaExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__) +#define MetaExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_METAUTIL, e, x, s, __VA_ARGS__) +#define MetaExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_METAUTIL, g, x, s, __VA_ARGS__) + + +// prototypes +static void Sort( + __in_ecount(cArray) DWORD dwArray[], + __in int cArray + ); + + +/******************************************************************** + MetaFindWebBase - finds a metabase base string that matches IP, Port and Header + +********************************************************************/ +extern "C" HRESULT DAPI MetaFindWebBase( + __in IMSAdminBaseW* piMetabase, + __in_z LPCWSTR wzIP, + __in int iPort, + __in_z LPCWSTR wzHeader, + __in BOOL fSecure, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ) +{ + Assert(piMetabase && cchWebBase); + + HRESULT hr = S_OK; + + BOOL fFound = FALSE; + + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + WCHAR wzSubkey[METADATA_MAX_NAME_LEN]; + DWORD dwIndex = 0; + + METADATA_RECORD mr; + METADATA_RECORD mrAddress; + + LPWSTR pwzExists = NULL; + LPWSTR pwzIPExists = NULL; + LPWSTR pwzPortExists = NULL; + int iPortExists = 0; + LPCWSTR pwzHeaderExists = NULL; + + memset(&mr, 0, sizeof(mr)); + mr.dwMDIdentifier = MD_KEY_TYPE; + mr.dwMDAttributes = METADATA_INHERIT; + mr.dwMDUserType = IIS_MD_UT_SERVER; + mr.dwMDDataType = ALL_METADATA; + + memset(&mrAddress, 0, sizeof(mrAddress)); + mrAddress.dwMDIdentifier = (fSecure) ? MD_SECURE_BINDINGS : MD_SERVER_BINDINGS; + mrAddress.dwMDAttributes = METADATA_INHERIT; + mrAddress.dwMDUserType = IIS_MD_UT_SERVER; + mrAddress.dwMDDataType = ALL_METADATA; + + // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb + for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex) + { + hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex); + if (FAILED(hr)) + break; + + ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey); + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr); + if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) + { + hr = S_FALSE; // didn't find anything, try next one + continue; + } + MetaExitOnFailure(hr, "failed to get key from metabase while searching for web servers"); + + // if we have an IIsWebServer store the key + if (0 == lstrcmpW(L"IIsWebServer", (LPCWSTR)mr.pbMDData)) + { + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mrAddress); + if (MD_ERROR_DATA_NOT_FOUND == hr) + hr = S_FALSE; + MetaExitOnFailure(hr, "failed to get address from metabase while searching for web servers"); + + // break down the first address into parts + pwzIPExists = reinterpret_cast(mrAddress.pbMDData); + pwzExists = wcsstr(pwzIPExists, L":"); + if (NULL == pwzExists) + continue; + + *pwzExists = L'\0'; + + pwzPortExists = pwzExists + 1; + pwzExists = wcsstr(pwzPortExists, L":"); + if (NULL == pwzExists) + continue; + + *pwzExists = L'\0'; + iPortExists = wcstol(pwzPortExists, NULL, 10); + + pwzHeaderExists = pwzExists + 1; + + // compare the passed in address with the address listed for this web + if (S_OK == hr && + (0 == lstrcmpW(wzIP, pwzIPExists) || 0 == lstrcmpW(wzIP, L"*")) && + iPort == iPortExists && + 0 == lstrcmpW(wzHeader, pwzHeaderExists)) + { + // if the passed in buffer wasn't big enough + hr = ::StringCchCopyW(wzWebBase, cchWebBase, wzKey); + MetaExitOnFailure(hr, "failed to copy in web base: %ls", wzKey); + + fFound = TRUE; + break; + } + } + } + + if (E_NOMOREITEMS == hr) + { + Assert(!fFound); + hr = S_FALSE; + } + +LExit: + MetaFreeValue(&mrAddress); + MetaFreeValue(&mr); + + if (!fFound && SUCCEEDED(hr)) + hr = S_FALSE; + + return hr; +} + + +/******************************************************************** + MetaFindFreeWebBase - finds the next metabase base string + +********************************************************************/ +extern "C" HRESULT DAPI MetaFindFreeWebBase( + __in IMSAdminBaseW* piMetabase, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + WCHAR wzSubkey[METADATA_MAX_NAME_LEN]; + DWORD dwSubKeys[100]; + int cSubKeys = 0; + DWORD dwIndex = 0; + + int i; + DWORD dwKey; + + METADATA_RECORD mr; + + memset(&mr, 0, sizeof(mr)); + mr.dwMDIdentifier = MD_KEY_TYPE; + mr.dwMDAttributes = 0; + mr.dwMDUserType = IIS_MD_UT_SERVER; + mr.dwMDDataType = STRING_METADATA; + + // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb + for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex) + { + hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex); + if (FAILED(hr)) + break; + + ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey); + + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr); + if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) + { + hr = S_FALSE; // didn't find anything, try next one + continue; + } + MetaExitOnFailure(hr, "failed to get key from metabase while searching for free web root"); + + // if we have a IIsWebServer get the address information + if (0 == lstrcmpW(L"IIsWebServer", reinterpret_cast(mr.pbMDData))) + { + if (cSubKeys >= countof(dwSubKeys)) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + MetaExitOnFailure(hr, "Insufficient buffer to track all sub-WebSites"); + } + + dwSubKeys[cSubKeys] = wcstol(wzSubkey, NULL, 10); + ++cSubKeys; + Sort(dwSubKeys, cSubKeys); + } + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + MetaExitOnFailure(hr, "failed to find free web root"); + + // find the lowest free web root + dwKey = 1; + for (i = 0; i < cSubKeys; ++i) + { + if (dwKey < dwSubKeys[i]) + break; + + dwKey = dwSubKeys[i] + 1; + } + + hr = ::StringCchPrintfW(wzWebBase, cchWebBase, L"/LM/W3SVC/%u", dwKey); +LExit: + MetaFreeValue(&mr); + return hr; +} + + +/******************************************************************** + MetaOpenKey - open key + +********************************************************************/ +extern "C" HRESULT DAPI MetaOpenKey( + __in IMSAdminBaseW* piMetabase, + __in METADATA_HANDLE mhKey, + __in_z LPCWSTR wzKey, + __in DWORD dwAccess, + __in DWORD cRetries, + __out METADATA_HANDLE* pmh + ) +{ + Assert(piMetabase && pmh); + + HRESULT hr = S_OK; + + // loop while the key is busy + do + { + hr = piMetabase->OpenKey(mhKey, wzKey, dwAccess, 10, pmh); + if (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr) + ::SleepEx(1000, TRUE); + } while (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr && 0 < cRetries--); + + return hr; +} + + +/******************************************************************** + MetaGetValue - finds the next metabase base string + + NOTE: piMetabase is optional +********************************************************************/ +extern "C" HRESULT DAPI MetaGetValue( + __in IMSAdminBaseW* piMetabase, + __in METADATA_HANDLE mhKey, + __in_z LPCWSTR wzKey, + __inout METADATA_RECORD* pmr + ) +{ + Assert(pmr); + + HRESULT hr = S_OK; + BOOL fInitialized = FALSE; + DWORD cbRequired = 0; + + if (!piMetabase) + { + hr = ::CoInitialize(NULL); + MetaExitOnFailure(hr, "failed to initialize COM"); + fInitialized = TRUE; + + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); + MetaExitOnFailure(hr, "failed to get IID_IMSAdminBaseW object"); + } + + if (!pmr->pbMDData) + { + pmr->dwMDDataLen = 256; + pmr->pbMDData = static_cast(MemAlloc(pmr->dwMDDataLen, TRUE)); + MetaExitOnNull(pmr->pbMDData, hr, E_OUTOFMEMORY, "failed to allocate memory for metabase value"); + } + else // set the size of the data to the actual size of the memory + { + SIZE_T cb = MemSize(pmr->pbMDData); + if (cb > DWORD_MAX) + { + MetaExitOnRootFailure(hr = E_INVALIDSTATE, "metabase data is too large: %Iu", cb); + } + pmr->dwMDDataLen = (DWORD)cb; + } + + hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired); + if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) + { + pmr->dwMDDataLen = cbRequired; + BYTE* pb = static_cast(MemReAlloc(pmr->pbMDData, pmr->dwMDDataLen, TRUE)); + MetaExitOnNull(pb, hr, E_OUTOFMEMORY, "failed to reallocate memory for metabase value"); + + pmr->pbMDData = pb; + hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired); + } + MetaExitOnFailure(hr, "failed to get metabase data"); + +LExit: + if (fInitialized) + { + ReleaseObject(piMetabase); + ::CoUninitialize(); + } + + return hr; +} + + +/******************************************************************** + MetaFreeValue - frees data in METADATA_RECORD remove MetaGetValue() + + NOTE: METADATA_RECORD must have been returned from MetaGetValue() above +********************************************************************/ +extern "C" void DAPI MetaFreeValue( + __in METADATA_RECORD* pmr + ) +{ + Assert(pmr); + + ReleaseNullMem(pmr->pbMDData); +} + + +// +// private +// + +/******************************************************************** + Sort - quick and dirty insertion sort + +********************************************************************/ +static void Sort( + __in_ecount(cArray) DWORD dwArray[], + __in int cArray + ) +{ + int i, j; + DWORD dwData; + + for (i = 1; i < cArray; ++i) + { + dwData = dwArray[i]; + + j = i - 1; + while (0 <= j && dwArray[j] > dwData) + { + dwArray[j + 1] = dwArray[j]; + j--; + } + + dwArray[j + 1] = dwData; + } +} diff --git a/src/libs/dutil/WixToolset.DUtil/monutil.cpp b/src/libs/dutil/WixToolset.DUtil/monutil.cpp new file mode 100644 index 00000000..6a7f0596 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/monutil.cpp @@ -0,0 +1,2019 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define MonExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__) +#define MonExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_MONUTIL, p, x, e, s, __VA_ARGS__) +#define MonExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__) +#define MonExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_MONUTIL, p, x, e, s, __VA_ARGS__) +#define MonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__) +#define MonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MONUTIL, e, x, s, __VA_ARGS__) +#define MonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MONUTIL, g, x, s, __VA_ARGS__) + +const int MON_THREAD_GROWTH = 5; +const int MON_ARRAY_GROWTH = 40; +const int MON_MAX_MONITORS_PER_THREAD = 63; +const int MON_THREAD_INIT_RETRIES = 1000; +const int MON_THREAD_INIT_RETRY_PERIOD_IN_MS = 10; +const int MON_THREAD_NETWORK_FAIL_RETRY_IN_MS = 1000*60; // if we know we failed to connect, retry every minute +const int MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS = 1000*60*20; // if we're just checking for remote servers dieing, check much less frequently +const int MON_THREAD_WAIT_REMOVE_DEVICE = 5000; +const LPCWSTR MONUTIL_WINDOW_CLASS = L"MonUtilClass"; + +enum MON_MESSAGE +{ + MON_MESSAGE_ADD = WM_APP + 1, + MON_MESSAGE_REMOVE, + MON_MESSAGE_REMOVED, // Sent by waiter thread back to coordinator thread to indicate a remove occurred + MON_MESSAGE_NETWORK_WAIT_FAILED, // Sent by waiter thread back to coordinator thread to indicate a network wait failed. Coordinator thread will periodically trigger retries (via MON_MESSAGE_NETWORK_STATUS_UPDATE messages). + MON_MESSAGE_NETWORK_WAIT_SUCCEEDED, // Sent by waiter thread back to coordinator thread to indicate a previously failing network wait is now succeeding. Coordinator thread will stop triggering retries if no other failing waits exist. + MON_MESSAGE_NETWORK_STATUS_UPDATE, // Some change to network connectivity occurred (a network connection was connected or disconnected for example) + MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any successful network waits. + // Annoyingly, this is necessary to catch the rare case that the remote server goes offline unexpectedly, such as by + // network cable unplugged or power loss - in this case there is no local network status change, and the wait will just never fire. + // So we very occasionally retry all successful network waits. When this occurs, we notify for changes, even though there may not have been any. + // This is because we have no way to detect if the old wait had failed (and changes were lost) due to the remote server going offline during that time or not. + // If we do this often, it can cause a lot of wasted work (which could be expensive for battery life), so the default is to do it very rarely (every 20 minutes). + MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any failed network waits + MON_MESSAGE_DRIVE_STATUS_UPDATE, // Some change to local drive has occurred (new drive created or plugged in, or removed) + MON_MESSAGE_DRIVE_QUERY_REMOVE, // User wants to unplug a drive, which MonUtil will always allow + MON_MESSAGE_STOP +}; + +enum MON_TYPE +{ + MON_NONE = 0, + MON_DIRECTORY = 1, + MON_REGKEY = 2 +}; + +struct MON_REQUEST +{ + MON_TYPE type; + DWORD dwMaxSilencePeriodInMs; + + // Handle to the main window for RegisterDeviceNotification() (same handle as owned by coordinator thread) + HWND hwnd; + // and handle to the notification (specific to this request) + HDEVNOTIFY hNotify; + + BOOL fRecursive; + void *pvContext; + + HRESULT hrStatus; + + LPWSTR sczOriginalPathRequest; + BOOL fNetwork; // This reflects either a UNC or mounted drive original request + DWORD dwPathHierarchyIndex; + LPWSTR *rgsczPathHierarchy; + DWORD cPathHierarchy; + + // If the notify fires, fPendingFire gets set to TRUE, and we wait to see if other writes are occurring, and only after the configured silence period do we notify of changes + // after notification, we set fPendingFire back to FALSE + BOOL fPendingFire; + BOOL fSkipDeltaAdd; + DWORD dwSilencePeriodInMs; + + union + { + struct + { + } directory; + struct + { + HKEY hkRoot; + HKEY hkSubKey; + REG_KEY_BITNESS kbKeyBitness; // Only used to pass on 32-bit, 64-bit, or default parameter + } regkey; + }; +}; + +struct MON_ADD_MESSAGE +{ + MON_REQUEST request; + HANDLE handle; +}; + +struct MON_REMOVE_MESSAGE +{ + MON_TYPE type; + BOOL fRecursive; + + union + { + struct + { + LPWSTR sczDirectory; + } directory; + struct + { + HKEY hkRoot; + LPWSTR sczSubKey; + REG_KEY_BITNESS kbKeyBitness; + } regkey; + }; +}; + +struct MON_WAITER_CONTEXT +{ + DWORD dwCoordinatorThreadId; + + HANDLE hWaiterThread; + DWORD dwWaiterThreadId; + BOOL fWaiterThreadMessageQueueInitialized; + + // Callbacks + PFN_MONGENERAL vpfMonGeneral; + PFN_MONDIRECTORY vpfMonDirectory; + PFN_MONREGKEY vpfMonRegKey; + + // Context for callbacks + LPVOID pvContext; + + // HANDLEs are in their own array for easy use with WaitForMultipleObjects() + // After initialization, the very first handle is just to wake the listener thread to have it re-wait on a new list + // Because this array is read by both coordinator thread and waiter thread, to avoid locking between both threads, it must start at the maximum size + HANDLE *rgHandles; + DWORD cHandles; + + // Requested things to monitor + MON_REQUEST *rgRequests; + DWORD cRequests; + + // Number of pending notifications + DWORD cRequestsPending; + + // Number of requests in a failed state (couldn't initiate wait) + DWORD cRequestsFailing; +}; + +// Info stored about each waiter by the coordinator +struct MON_WAITER_INFO +{ + DWORD cMonitorCount; + + MON_WAITER_CONTEXT *pWaiterContext; +}; + +// This struct is used when Thread A wants to send a task to another thread B (and get notified when the task finishes) +// You typically declare this struct in a manner that a pointer to it is valid as long as a thread that could respond is still running +// (even long after sender is no longer waiting, in case thread has huge message queue) +// and you must send 2 parameters in the message: +// 1) a pointer to this struct (which is always valid) +// 2) the original value of dwIteration +// The receiver of the message can compare the current value of dwSendIteration in the struct with what was sent in the message +// If values are different, we're too late and thread A is no longer waiting on this response +// otherwise, set dwResponseIteration to the same value, and call ::SetEvent() on hWait +// Thread A will then wakeup, and must verify that dwResponseIteration == dwSendIteration to ensure it isn't an earlier out-of-date reply +// replying to a newer wait +// pvContext is used to send a misc parameter related to processing data +struct MON_INTERNAL_TEMPORARY_WAIT +{ + // Should be incremented each time sender sends a pointer to this struct, so each request has a different iteration + DWORD dwSendIteration; + DWORD dwReceiveIteration; + HANDLE hWait; + void *pvContext; +}; + +struct MON_STRUCT +{ + HANDLE hCoordinatorThread; + DWORD dwCoordinatorThreadId; + BOOL fCoordinatorThreadMessageQueueInitialized; + + // Invisible window for receiving network status & drive added/removal messages + HWND hwnd; + // Used by window procedure for sending request and waiting for response from waiter threads + // such as in event of a request to remove a device + MON_INTERNAL_TEMPORARY_WAIT internalWait; + + // Callbacks + PFN_MONGENERAL vpfMonGeneral; + PFN_MONDRIVESTATUS vpfMonDriveStatus; + PFN_MONDIRECTORY vpfMonDirectory; + PFN_MONREGKEY vpfMonRegKey; + + // Context for callbacks + LPVOID pvContext; + + // Waiter thread array + MON_WAITER_INFO *rgWaiterThreads; + DWORD cWaiterThreads; +}; + +const int MON_HANDLE_BYTES = sizeof(MON_STRUCT); + +static DWORD WINAPI CoordinatorThread( + __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext + ); +// Initiates (or if *pHandle is non-null, continues) wait on the directory or subkey +// if the directory or subkey doesn't exist, instead calls it on the first existing parent directory or subkey +// writes to pRequest->dwPathHierarchyIndex with the array index that was waited on +static HRESULT InitiateWait( + __inout MON_REQUEST *pRequest, + __inout HANDLE *pHandle + ); +static DWORD WINAPI WaiterThread( + __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext + ); +static void Notify( + __in HRESULT hr, + __in MON_WAITER_CONTEXT *pWaiterContext, + __in MON_REQUEST *pRequest + ); +static void MonRequestDestroy( + __in MON_REQUEST *pRequest + ); +static void MonAddMessageDestroy( + __in_opt MON_ADD_MESSAGE *pMessage + ); +static void MonRemoveMessageDestroy( + __in_opt MON_REMOVE_MESSAGE *pMessage + ); +static BOOL GetRecursiveFlag( + __in MON_REQUEST *pRequest, + __in DWORD dwIndex + ); +static HRESULT FindRequestIndex( + __in MON_WAITER_CONTEXT *pWaiterContext, + __in MON_REMOVE_MESSAGE *pMessage, + __out DWORD *pdwIndex + ); +static HRESULT RemoveRequest( + __inout MON_WAITER_CONTEXT *pWaiterContext, + __in DWORD dwRequestIndex + ); +static REGSAM GetRegKeyBitness( + __in MON_REQUEST *pRequest + ); +static HRESULT DuplicateRemoveMessage( + __in MON_REMOVE_MESSAGE *pMessage, + __out MON_REMOVE_MESSAGE **ppMessage + ); +static LRESULT CALLBACK MonWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); +static HRESULT CreateMonWindow( + __in MON_STRUCT *pm, + __out HWND *pHwnd + ); +// if *phMonitor is non-NULL, closes the old wait before re-starting the new wait +static HRESULT WaitForNetworkChanges( + __inout HANDLE *phMonitor, + __in MON_STRUCT *pm + ); +static HRESULT UpdateWaitStatus( + __in HRESULT hrNewStatus, + __inout MON_WAITER_CONTEXT *pWaiterContext, + __in DWORD dwRequestIndex, + __out_opt DWORD *pdwNewRequestIndex + ); + +extern "C" HRESULT DAPI MonCreate( + __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle, + __in PFN_MONGENERAL vpfMonGeneral, + __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus, + __in_opt PFN_MONDIRECTORY vpfMonDirectory, + __in_opt PFN_MONREGKEY vpfMonRegKey, + __in_opt LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + DWORD dwRetries = MON_THREAD_INIT_RETRIES; + + MonExitOnNull(pHandle, hr, E_INVALIDARG, "Pointer to handle not specified while creating monitor"); + + // Allocate the struct + *pHandle = static_cast(MemAlloc(sizeof(MON_STRUCT), TRUE)); + MonExitOnNull(*pHandle, hr, E_OUTOFMEMORY, "Failed to allocate monitor object"); + + MON_STRUCT *pm = static_cast(*pHandle); + + pm->vpfMonGeneral = vpfMonGeneral; + pm->vpfMonDriveStatus = vpfMonDriveStatus; + pm->vpfMonDirectory = vpfMonDirectory; + pm->vpfMonRegKey = vpfMonRegKey; + pm->pvContext = pvContext; + + pm->hCoordinatorThread = ::CreateThread(NULL, 0, CoordinatorThread, pm, 0, &pm->dwCoordinatorThreadId); + if (!pm->hCoordinatorThread) + { + MonExitWithLastError(hr, "Failed to create waiter thread."); + } + + // Ensure the created thread initializes its message queue. It does this first thing, so if it doesn't within 10 seconds, there must be a huge problem. + while (!pm->fCoordinatorThreadMessageQueueInitialized && 0 < dwRetries) + { + ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS); + --dwRetries; + } + + if (0 == dwRetries) + { + hr = E_UNEXPECTED; + MonExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI MonAddDirectory( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in_z LPCWSTR wzDirectory, + __in BOOL fRecursive, + __in DWORD dwSilencePeriodInMs, + __in_opt LPVOID pvDirectoryContext + ) +{ + HRESULT hr = S_OK; + MON_STRUCT *pm = static_cast(handle); + LPWSTR sczDirectory = NULL; + LPWSTR sczOriginalPathRequest = NULL; + MON_ADD_MESSAGE *pMessage = NULL; + + hr = StrAllocString(&sczOriginalPathRequest, wzDirectory, 0); + MonExitOnFailure(hr, "Failed to convert directory string to UNC path"); + + hr = PathBackslashTerminate(&sczOriginalPathRequest); + MonExitOnFailure(hr, "Failed to ensure directory ends in backslash"); + + pMessage = reinterpret_cast(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE)); + MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + + if (sczOriginalPathRequest[0] == L'\\' && sczOriginalPathRequest[1] == L'\\') + { + pMessage->request.fNetwork = TRUE; + } + else + { + hr = UncConvertFromMountedDrive(&sczDirectory, sczOriginalPathRequest); + if (SUCCEEDED(hr)) + { + pMessage->request.fNetwork = TRUE; + } + } + + if (NULL == sczDirectory) + { + // Likely not a mounted drive - just copy the request then + hr = S_OK; + + hr = StrAllocString(&sczDirectory, sczOriginalPathRequest, 0); + MonExitOnFailure(hr, "Failed to copy original path request: %ls", sczOriginalPathRequest); + } + + pMessage->handle = INVALID_HANDLE_VALUE; + pMessage->request.type = MON_DIRECTORY; + pMessage->request.fRecursive = fRecursive; + pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs; + pMessage->request.hwnd = pm->hwnd; + pMessage->request.pvContext = pvDirectoryContext; + pMessage->request.sczOriginalPathRequest = sczOriginalPathRequest; + sczOriginalPathRequest = NULL; + + hr = PathGetHierarchyArray(sczDirectory, &pMessage->request.rgsczPathHierarchy, reinterpret_cast(&pMessage->request.cPathHierarchy)); + MonExitOnFailure(hr, "Failed to get hierarchy array for path %ls", sczDirectory); + + if (0 < pMessage->request.cPathHierarchy) + { + pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle); + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast(pMessage), 0)) + { + MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); + } + pMessage = NULL; + } + +LExit: + ReleaseStr(sczDirectory); + ReleaseStr(sczOriginalPathRequest); + MonAddMessageDestroy(pMessage); + + return hr; +} + +extern "C" HRESULT DAPI MonAddRegKey( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive, + __in DWORD dwSilencePeriodInMs, + __in_opt LPVOID pvRegKeyContext + ) +{ + HRESULT hr = S_OK; + MON_STRUCT *pm = static_cast(handle); + LPWSTR sczSubKey = NULL; + MON_ADD_MESSAGE *pMessage = NULL; + + hr = StrAllocString(&sczSubKey, wzSubKey, 0); + MonExitOnFailure(hr, "Failed to copy subkey string"); + + hr = PathBackslashTerminate(&sczSubKey); + MonExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); + + pMessage = reinterpret_cast(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE)); + MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + + pMessage->handle = ::CreateEventW(NULL, TRUE, FALSE, NULL); + MonExitOnNullWithLastError(pMessage->handle, hr, "Failed to create anonymous event for regkey monitor"); + + pMessage->request.type = MON_REGKEY; + pMessage->request.regkey.hkRoot = hkRoot; + pMessage->request.regkey.kbKeyBitness = kbKeyBitness; + pMessage->request.fRecursive = fRecursive; + pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs, + pMessage->request.hwnd = pm->hwnd; + pMessage->request.pvContext = pvRegKeyContext; + + hr = PathGetHierarchyArray(sczSubKey, &pMessage->request.rgsczPathHierarchy, reinterpret_cast(&pMessage->request.cPathHierarchy)); + MonExitOnFailure(hr, "Failed to get hierarchy array for subkey %ls", sczSubKey); + + if (0 < pMessage->request.cPathHierarchy) + { + pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle); + MonExitOnFailure(hr, "Failed to initiate wait"); + + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast(pMessage), 0)) + { + MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for regkey %ls", sczSubKey); + } + pMessage = NULL; + } + +LExit: + ReleaseStr(sczSubKey); + MonAddMessageDestroy(pMessage); + + return hr; +} + +extern "C" HRESULT DAPI MonRemoveDirectory( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in_z LPCWSTR wzDirectory, + __in BOOL fRecursive + ) +{ + HRESULT hr = S_OK; + MON_STRUCT *pm = static_cast(handle); + LPWSTR sczDirectory = NULL; + MON_REMOVE_MESSAGE *pMessage = NULL; + + hr = StrAllocString(&sczDirectory, wzDirectory, 0); + MonExitOnFailure(hr, "Failed to copy directory string"); + + hr = PathBackslashTerminate(&sczDirectory); + MonExitOnFailure(hr, "Failed to ensure directory ends in backslash"); + + pMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); + MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + + pMessage->type = MON_DIRECTORY; + pMessage->fRecursive = fRecursive; + + hr = StrAllocString(&pMessage->directory.sczDirectory, sczDirectory, 0); + MonExitOnFailure(hr, "Failed to allocate copy of directory string"); + + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pMessage), 0)) + { + MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory); + } + pMessage = NULL; + +LExit: + MonRemoveMessageDestroy(pMessage); + + return hr; +} + +extern "C" HRESULT DAPI MonRemoveRegKey( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fRecursive + ) +{ + HRESULT hr = S_OK; + MON_STRUCT *pm = static_cast(handle); + LPWSTR sczSubKey = NULL; + MON_REMOVE_MESSAGE *pMessage = NULL; + + hr = StrAllocString(&sczSubKey, wzSubKey, 0); + MonExitOnFailure(hr, "Failed to copy subkey string"); + + hr = PathBackslashTerminate(&sczSubKey); + MonExitOnFailure(hr, "Failed to ensure subkey path ends in backslash"); + + pMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); + MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message"); + + pMessage->type = MON_REGKEY; + pMessage->regkey.hkRoot = hkRoot; + pMessage->regkey.kbKeyBitness = kbKeyBitness; + pMessage->fRecursive = fRecursive; + + hr = StrAllocString(&pMessage->regkey.sczSubKey, sczSubKey, 0); + MonExitOnFailure(hr, "Failed to allocate copy of directory string"); + + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pMessage), 0)) + { + MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczSubKey); + } + pMessage = NULL; + +LExit: + ReleaseStr(sczSubKey); + MonRemoveMessageDestroy(pMessage); + + return hr; +} + +extern "C" void DAPI MonDestroy( + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + MON_STRUCT *pm = static_cast(handle); + + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0)) + { + er = ::GetLastError(); + if (ERROR_INVALID_THREAD_ID == er) + { + // It already halted, or doesn't exist for some other reason, so let's just ignore it and clean up + er = ERROR_SUCCESS; + } + MonExitOnWin32Error(er, hr, "Failed to send message to background thread to halt"); + } + + if (pm->hCoordinatorThread) + { + ::WaitForSingleObject(pm->hCoordinatorThread, INFINITE); + ::CloseHandle(pm->hCoordinatorThread); + } + +LExit: + return; +} + +static void MonRequestDestroy( + __in MON_REQUEST *pRequest + ) +{ + if (NULL != pRequest) + { + if (MON_REGKEY == pRequest->type) + { + ReleaseRegKey(pRequest->regkey.hkSubKey); + } + else if (MON_DIRECTORY == pRequest->type && pRequest->hNotify) + { + UnregisterDeviceNotification(pRequest->hNotify); + pRequest->hNotify = NULL; + } + ReleaseStr(pRequest->sczOriginalPathRequest); + ReleaseStrArray(pRequest->rgsczPathHierarchy, pRequest->cPathHierarchy); + } +} + +static void MonAddMessageDestroy( + __in_opt MON_ADD_MESSAGE *pMessage + ) +{ + if (pMessage) + { + MonRequestDestroy(&pMessage->request); + if (MON_DIRECTORY == pMessage->request.type && INVALID_HANDLE_VALUE != pMessage->handle) + { + ::FindCloseChangeNotification(pMessage->handle); + } + else if (MON_REGKEY == pMessage->request.type) + { + ReleaseHandle(pMessage->handle); + } + + ReleaseMem(pMessage); + } +} + +static void MonRemoveMessageDestroy( + __in_opt MON_REMOVE_MESSAGE *pMessage + ) +{ + if (pMessage) + { + switch (pMessage->type) + { + case MON_DIRECTORY: + ReleaseStr(pMessage->directory.sczDirectory); + break; + case MON_REGKEY: + ReleaseStr(pMessage->regkey.sczSubKey); + break; + default: + Assert(false); + } + + ReleaseMem(pMessage); + } +} + +static DWORD WINAPI CoordinatorThread( + __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + MSG msg = { }; + DWORD dwThreadIndex = DWORD_MAX; + DWORD dwRetries; + DWORD dwFailingNetworkWaits = 0; + MON_WAITER_CONTEXT *pWaiterContext = NULL; + MON_REMOVE_MESSAGE *pRemoveMessage = NULL; + MON_REMOVE_MESSAGE *pTempRemoveMessage = NULL; + MON_STRUCT *pm = reinterpret_cast(pvContext); + WSADATA wsaData = { }; + HANDLE hMonitor = NULL; + BOOL fRet = FALSE; + UINT_PTR uTimerSuccessfulNetworkRetry = 0; + UINT_PTR uTimerFailedNetworkRetry = 0; + + // Ensure the thread has a message queue + ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + pm->fCoordinatorThreadMessageQueueInitialized = TRUE; + + hr = CreateMonWindow(pm, &pm->hwnd); + MonExitOnFailure(hr, "Failed to create window for status update thread"); + + ::WSAStartup(MAKEWORD(2, 2), &wsaData); + + hr = WaitForNetworkChanges(&hMonitor, pm); + MonExitOnFailure(hr, "Failed to wait for network changes"); + + uTimerSuccessfulNetworkRetry = ::SetTimer(NULL, 1, MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS, NULL); + if (0 == uTimerSuccessfulNetworkRetry) + { + MonExitWithLastError(hr, "Failed to set timer for network successful retry"); + } + + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) + { + if (-1 == fRet) + { + hr = E_UNEXPECTED; + MonExitOnRootFailure(hr, "Unexpected return value from message pump."); + } + else + { + switch (msg.message) + { + case MON_MESSAGE_ADD: + dwThreadIndex = DWORD_MAX; + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + if (pm->rgWaiterThreads[i].cMonitorCount < MON_MAX_MONITORS_PER_THREAD) + { + dwThreadIndex = i; + break; + } + } + + if (dwThreadIndex < pm->cWaiterThreads) + { + pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext; + } + else + { + hr = MemEnsureArraySize(reinterpret_cast(&pm->rgWaiterThreads), pm->cWaiterThreads + 1, sizeof(MON_WAITER_INFO), MON_THREAD_GROWTH); + MonExitOnFailure(hr, "Failed to grow waiter thread array size"); + ++pm->cWaiterThreads; + + dwThreadIndex = pm->cWaiterThreads - 1; + pm->rgWaiterThreads[dwThreadIndex].pWaiterContext = reinterpret_cast(MemAlloc(sizeof(MON_WAITER_CONTEXT), TRUE)); + MonExitOnNull(pm->rgWaiterThreads[dwThreadIndex].pWaiterContext, hr, E_OUTOFMEMORY, "Failed to allocate waiter context struct"); + pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext; + pWaiterContext->dwCoordinatorThreadId = ::GetCurrentThreadId(); + pWaiterContext->vpfMonGeneral = pm->vpfMonGeneral; + pWaiterContext->vpfMonDirectory = pm->vpfMonDirectory; + pWaiterContext->vpfMonRegKey = pm->vpfMonRegKey; + pWaiterContext->pvContext = pm->pvContext; + + hr = MemEnsureArraySize(reinterpret_cast(&pWaiterContext->rgHandles), MON_MAX_MONITORS_PER_THREAD + 1, sizeof(HANDLE), 0); + MonExitOnFailure(hr, "Failed to allocate first handle"); + pWaiterContext->cHandles = 1; + + pWaiterContext->rgHandles[0] = ::CreateEventW(NULL, FALSE, FALSE, NULL); + MonExitOnNullWithLastError(pWaiterContext->rgHandles[0], hr, "Failed to create general event"); + + pWaiterContext->hWaiterThread = ::CreateThread(NULL, 0, WaiterThread, pWaiterContext, 0, &pWaiterContext->dwWaiterThreadId); + if (!pWaiterContext->hWaiterThread) + { + MonExitWithLastError(hr, "Failed to create waiter thread."); + } + + dwRetries = MON_THREAD_INIT_RETRIES; + while (!pWaiterContext->fWaiterThreadMessageQueueInitialized && 0 < dwRetries) + { + ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS); + --dwRetries; + } + + if (0 == dwRetries) + { + hr = E_UNEXPECTED; + MonExitOnFailure(hr, "Waiter thread apparently never initialized its message queue."); + } + } + + ++pm->rgWaiterThreads[dwThreadIndex].cMonitorCount; + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_ADD, msg.wParam, 0)) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming message"); + } + break; + + case MON_MESSAGE_REMOVE: + // Send remove to all waiter threads. They'll ignore it if they don't have that monitor. + // If they do have that monitor, they'll remove it from their list, and tell coordinator they have another + // empty slot via MON_MESSAGE_REMOVED message + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + pRemoveMessage = reinterpret_cast(msg.wParam); + + hr = DuplicateRemoveMessage(pRemoveMessage, &pTempRemoveMessage); + MonExitOnFailure(hr, "Failed to duplicate remove message"); + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_REMOVE, reinterpret_cast(pTempRemoveMessage), msg.lParam)) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to add monitor"); + } + pTempRemoveMessage = NULL; + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming remove message"); + } + } + MonRemoveMessageDestroy(pRemoveMessage); + pRemoveMessage = NULL; + break; + + case MON_MESSAGE_REMOVED: + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + if (pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId == static_cast(msg.wParam)) + { + Assert(pm->rgWaiterThreads[i].cMonitorCount > 0); + --pm->rgWaiterThreads[i].cMonitorCount; + if (0 == pm->rgWaiterThreads[i].cMonitorCount) + { + if (!::PostThreadMessageW(pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam)) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to stop"); + } + MemRemoveFromArray(reinterpret_cast(pm->rgWaiterThreads), i, 1, pm->cWaiterThreads, sizeof(MON_WAITER_INFO), TRUE); + --pm->cWaiterThreads; + --i; // reprocess this index in the for loop, which will now contain the item after the one we removed + } + } + } + break; + + case MON_MESSAGE_NETWORK_WAIT_FAILED: + if (0 == dwFailingNetworkWaits) + { + uTimerFailedNetworkRetry = ::SetTimer(NULL, uTimerSuccessfulNetworkRetry + 1, MON_THREAD_NETWORK_FAIL_RETRY_IN_MS, NULL); + if (0 == uTimerFailedNetworkRetry) + { + MonExitWithLastError(hr, "Failed to set timer for network fail retry"); + } + } + ++dwFailingNetworkWaits; + break; + + case MON_MESSAGE_NETWORK_WAIT_SUCCEEDED: + --dwFailingNetworkWaits; + if (0 == dwFailingNetworkWaits) + { + if (!::KillTimer(NULL, uTimerFailedNetworkRetry)) + { + MonExitWithLastError(hr, "Failed to kill timer for network fail retry"); + } + uTimerFailedNetworkRetry = 0; + } + break; + + case MON_MESSAGE_NETWORK_STATUS_UPDATE: + hr = WaitForNetworkChanges(&hMonitor, pm); + MonExitOnFailure(hr, "Failed to re-wait for network changes"); + + // Propagate any network status update messages to all waiter threads + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_NETWORK_STATUS_UPDATE, 0, 0)) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); + } + } + break; + + case WM_TIMER: + // Timer means some network wait is failing, and we need to retry every so often in case a remote server goes back up + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, msg.wParam == uTimerFailedNetworkRetry ? MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS : MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, 0, 0)) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message"); + } + } + break; + + case MON_MESSAGE_DRIVE_STATUS_UPDATE: + // If user requested to be notified of drive status updates, notify! + if (pm->vpfMonDriveStatus) + { + pm->vpfMonDriveStatus(static_cast(msg.wParam), static_cast(msg.lParam), pm->pvContext); + } + + // Propagate any drive status update messages to all waiter threads + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_STATUS_UPDATE, msg.wParam, msg.lParam)) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive status update"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive status update message"); + } + } + break; + + case MON_MESSAGE_STOP: + ExitFunction1(hr = static_cast(msg.wParam)); + + default: + // This thread owns a window, so this handles all the other random messages we get + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + break; + } + } + } + +LExit: + if (uTimerFailedNetworkRetry) + { + fRet = ::KillTimer(NULL, uTimerFailedNetworkRetry); + } + if (uTimerSuccessfulNetworkRetry) + { + fRet = ::KillTimer(NULL, uTimerSuccessfulNetworkRetry); + } + + if (pm->hwnd) + { + ::CloseWindow(pm->hwnd); + } + + // Tell all waiter threads to shutdown + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + if (NULL != pWaiterContext->rgHandles[0]) + { + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam)) + { + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to waiter thread to stop"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to set event to notify waiter thread of incoming message"); + } + } + } + + if (hMonitor != NULL) + { + ::WSALookupServiceEnd(hMonitor); + } + + // Now confirm they're actually shut down before returning + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + if (NULL != pWaiterContext->hWaiterThread) + { + ::WaitForSingleObject(pWaiterContext->hWaiterThread, INFINITE); + ::CloseHandle(pWaiterContext->hWaiterThread); + } + + // Waiter thread can't release these, because coordinator thread uses it to try communicating with waiter thread + ReleaseHandle(pWaiterContext->rgHandles[0]); + ReleaseMem(pWaiterContext->rgHandles); + + ReleaseMem(pWaiterContext); + } + + if (FAILED(hr)) + { + // If coordinator thread fails, notify general callback of an error + Assert(pm->vpfMonGeneral); + pm->vpfMonGeneral(hr, pm->pvContext); + } + MonRemoveMessageDestroy(pRemoveMessage); + MonRemoveMessageDestroy(pTempRemoveMessage); + + ::WSACleanup(); + + return hr; +} + +static HRESULT InitiateWait( + __inout MON_REQUEST *pRequest, + __inout HANDLE *pHandle + ) +{ + HRESULT hr = S_OK; + HRESULT hrTemp = S_OK; + DEV_BROADCAST_HANDLE dev = { }; + BOOL fRedo = FALSE; + BOOL fHandleFound; + DWORD er = ERROR_SUCCESS; + DWORD dwIndex = 0; + HKEY hk = NULL; + HANDLE hTemp = INVALID_HANDLE_VALUE; + + if (pRequest->hNotify) + { + UnregisterDeviceNotification(pRequest->hNotify); + pRequest->hNotify = NULL; + } + + do + { + fRedo = FALSE; + fHandleFound = FALSE; + + for (DWORD i = 0; i < pRequest->cPathHierarchy && !fHandleFound; ++i) + { + dwIndex = pRequest->cPathHierarchy - i - 1; + switch (pRequest->type) + { + case MON_DIRECTORY: + if (INVALID_HANDLE_VALUE != *pHandle) + { + ::FindCloseChangeNotification(*pHandle); + *pHandle = INVALID_HANDLE_VALUE; + } + + *pHandle = ::FindFirstChangeNotificationW(pRequest->rgsczPathHierarchy[dwIndex], GetRecursiveFlag(pRequest, dwIndex), FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SECURITY); + if (INVALID_HANDLE_VALUE == *pHandle) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || E_ACCESSDENIED == hr) + { + continue; + } + MonExitOnWin32Error(er, hr, "Failed to wait on path %ls", pRequest->rgsczPathHierarchy[dwIndex]); + } + else + { + fHandleFound = TRUE; + hr = S_OK; + } + break; + case MON_REGKEY: + ReleaseRegKey(pRequest->regkey.hkSubKey); + hr = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex], KEY_NOTIFY | GetRegKeyBitness(pRequest), &pRequest->regkey.hkSubKey); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + continue; + } + MonExitOnFailure(hr, "Failed to open regkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); + + er = ::RegNotifyChangeKeyValue(pRequest->regkey.hkSubKey, GetRecursiveFlag(pRequest, dwIndex), REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY, *pHandle, TRUE); + ReleaseRegKey(hk); + hr = HRESULT_FROM_WIN32(er); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || HRESULT_FROM_WIN32(ERROR_KEY_DELETED) == hr) + { + continue; + } + else + { + MonExitOnWin32Error(er, hr, "Failed to wait on subkey %ls", pRequest->rgsczPathHierarchy[dwIndex]); + + fHandleFound = TRUE; + } + + break; + default: + return E_INVALIDARG; + } + } + + pRequest->dwPathHierarchyIndex = dwIndex; + + // If we're monitoring a parent instead of the real path because the real path didn't exist, double-check the child hasn't been created since. + // If it has, restart the whole loop + if (dwIndex < pRequest->cPathHierarchy - 1) + { + switch (pRequest->type) + { + case MON_DIRECTORY: + hTemp = ::FindFirstChangeNotificationW(pRequest->rgsczPathHierarchy[dwIndex + 1], GetRecursiveFlag(pRequest, dwIndex + 1), FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SECURITY); + if (INVALID_HANDLE_VALUE != hTemp) + { + ::FindCloseChangeNotification(hTemp); + fRedo = TRUE; + } + break; + case MON_REGKEY: + hrTemp = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex + 1], KEY_NOTIFY | GetRegKeyBitness(pRequest), &hk); + ReleaseRegKey(hk); + fRedo = SUCCEEDED(hrTemp); + break; + default: + Assert(false); + } + } + } while (fRedo); + + MonExitOnFailure(hr, "Didn't get a successful wait after looping through all available options %ls", pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1]); + + if (MON_DIRECTORY == pRequest->type) + { + dev.dbch_size = sizeof(dev); + dev.dbch_devicetype = DBT_DEVTYP_HANDLE; + dev.dbch_handle = *pHandle; + // Ignore failure on this - some drives by design don't support it (like network paths), and the worst that can happen is a + // removable device will be left in use so user cannot gracefully remove + pRequest->hNotify = RegisterDeviceNotification(pRequest->hwnd, &dev, DEVICE_NOTIFY_WINDOW_HANDLE); + } + +LExit: + ReleaseRegKey(hk); + + return hr; +} + +static DWORD WINAPI WaiterThread( + __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + HRESULT hrTemp = S_OK; + DWORD dwRet = 0; + BOOL fAgain = FALSE; + BOOL fContinue = TRUE; + BOOL fNotify = FALSE; + BOOL fRet = FALSE; + MSG msg = { }; + MON_ADD_MESSAGE *pAddMessage = NULL; + MON_REMOVE_MESSAGE *pRemoveMessage = NULL; + MON_WAITER_CONTEXT *pWaiterContext = reinterpret_cast(pvContext); + DWORD dwRequestIndex; + DWORD dwNewRequestIndex; + // If we have one or more requests pending notification, this is the period we intend to wait for multiple objects (shortest amount of time to next potential notify) + DWORD dwWait = 0; + DWORD uCurrentTime = 0; + DWORD uLastTimeInMs = ::GetTickCount(); + DWORD uDeltaInMs = 0; + DWORD cRequestsPendingBeforeLoop = 0; + LPWSTR sczDirectory = NULL; + bool rgfProcessedIndex[MON_MAX_MONITORS_PER_THREAD + 1] = { }; + MON_INTERNAL_TEMPORARY_WAIT * pInternalWait = NULL; + + // Ensure the thread has a message queue + ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + pWaiterContext->fWaiterThreadMessageQueueInitialized = TRUE; + + do + { + dwRet = ::WaitForMultipleObjects(pWaiterContext->cHandles - pWaiterContext->cRequestsFailing, pWaiterContext->rgHandles, FALSE, pWaiterContext->cRequestsPending > 0 ? dwWait : INFINITE); + + uCurrentTime = ::GetTickCount(); + uDeltaInMs = uCurrentTime - uLastTimeInMs; + uLastTimeInMs = uCurrentTime; + + if (WAIT_OBJECT_0 == dwRet) + { + do + { + fRet = ::PeekMessage(&msg, reinterpret_cast(-1), 0, 0, PM_REMOVE); + fAgain = fRet; + if (fRet) + { + switch (msg.message) + { + case MON_MESSAGE_ADD: + pAddMessage = reinterpret_cast(msg.wParam); + + // Don't just blindly put it at the end of the array - it must be before any failing requests + // for WaitForMultipleObjects() to succeed + dwNewRequestIndex = pWaiterContext->cRequests - pWaiterContext->cRequestsFailing; + if (FAILED(pAddMessage->request.hrStatus)) + { + ++pWaiterContext->cRequestsFailing; + } + + hr = MemInsertIntoArray(reinterpret_cast(&pWaiterContext->rgHandles), dwNewRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), MON_ARRAY_GROWTH); + MonExitOnFailure(hr, "Failed to insert additional handle"); + ++pWaiterContext->cHandles; + + // Ugh - directory types start with INVALID_HANDLE_VALUE instead of NULL + if (MON_DIRECTORY == pAddMessage->request.type) + { + pWaiterContext->rgHandles[dwNewRequestIndex + 1] = INVALID_HANDLE_VALUE; + } + + hr = MemInsertIntoArray(reinterpret_cast(&pWaiterContext->rgRequests), dwNewRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), MON_ARRAY_GROWTH); + MonExitOnFailure(hr, "Failed to insert additional request struct"); + ++pWaiterContext->cRequests; + + pWaiterContext->rgRequests[dwNewRequestIndex] = pAddMessage->request; + pWaiterContext->rgHandles[dwNewRequestIndex + 1] = pAddMessage->handle; + + ReleaseNullMem(pAddMessage); + break; + + case MON_MESSAGE_REMOVE: + pRemoveMessage = reinterpret_cast(msg.wParam); + + // Find the request to remove + hr = FindRequestIndex(pWaiterContext, pRemoveMessage, &dwRequestIndex); + if (E_NOTFOUND == hr) + { + // Coordinator sends removes blindly to all waiter threads, so maybe this one wasn't intended for us + hr = S_OK; + } + else + { + MonExitOnFailure(hr, "Failed to find request index for remove message"); + + hr = RemoveRequest(pWaiterContext, dwRequestIndex); + MonExitOnFailure(hr, "Failed to remove request after request from coordinator thread."); + } + + MonRemoveMessageDestroy(pRemoveMessage); + pRemoveMessage = NULL; + break; + + case MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS: + if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, PM_NOREMOVE)) + { + // If there is another a pending retry failed wait message, skip this one + continue; + } + + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (rgfProcessedIndex[i]) + { + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it + continue; + } + + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && FAILED(pWaiterContext->rgRequests[i].hrStatus)) + { + // This is not a failure, just record this in the request's status + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + MonExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + if (dwNewRequestIndex != i) + { + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping + rgfProcessedIndex[dwNewRequestIndex] = true; + --i; + } + } + } + break; + + case MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS: + if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, PM_NOREMOVE)) + { + // If there is another a pending retry successful wait message, skip this one + continue; + } + + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (rgfProcessedIndex[i]) + { + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it + continue; + } + + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && SUCCEEDED(pWaiterContext->rgRequests[i].hrStatus)) + { + // This is not a failure, just record this in the request's status + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + MonExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + if (dwNewRequestIndex != i) + { + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping + rgfProcessedIndex[dwNewRequestIndex] = true; + --i; + } + } + } + break; + + case MON_MESSAGE_NETWORK_STATUS_UPDATE: + if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_STATUS_UPDATE, MON_MESSAGE_NETWORK_STATUS_UPDATE, PM_NOREMOVE)) + { + // If there is another a pending network status update message, skip this one + continue; + } + + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (rgfProcessedIndex[i]) + { + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it + continue; + } + + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork) + { + // Failures here get recorded in the request's status + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + MonExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + if (dwNewRequestIndex != i) + { + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping + rgfProcessedIndex[dwNewRequestIndex] = true; + --i; + } + } + } + break; + + case MON_MESSAGE_DRIVE_STATUS_UPDATE: + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex)); + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (rgfProcessedIndex[i]) + { + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it + continue; + } + + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].sczOriginalPathRequest[0] == static_cast(msg.wParam)) + { + // Failures here get recorded in the request's status + if (static_cast(msg.lParam)) + { + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1); + } + else + { + // If the message says the drive is disconnected, don't even try to wait, just mark it as gone + hrTemp = E_PATHNOTFOUND; + } + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + MonExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + if (dwNewRequestIndex != i) + { + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping + rgfProcessedIndex[dwNewRequestIndex] = true; + --i; + } + } + } + break; + + case MON_MESSAGE_DRIVE_QUERY_REMOVE: + pInternalWait = reinterpret_cast(msg.wParam); + // Only do any work if message is not yet out of date + // While it could become out of date while doing this processing, sending thread will check response to guard against this + if (pInternalWait->dwSendIteration == static_cast(msg.lParam)) + { + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgHandles[i + 1] == reinterpret_cast(pInternalWait->pvContext)) + { + // Release handles ASAP so the remove request will succeed + if (pWaiterContext->rgRequests[i].hNotify) + { + UnregisterDeviceNotification(pWaiterContext->rgRequests[i].hNotify); + pWaiterContext->rgRequests[i].hNotify = NULL; + } + ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]); + pWaiterContext->rgHandles[i + 1] = INVALID_HANDLE_VALUE; + + // Reply to unblock our reply to the remove request + pInternalWait->dwReceiveIteration = static_cast(msg.lParam); + if (!::SetEvent(pInternalWait->hWait)) + { + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to set event to notify coordinator thread that removable device handle was released, this could be due to wndproc no longer waiting for waiter thread's response"); + } + + // Drive is disconnecting, don't even try to wait, just mark it as gone + hrTemp = E_PATHNOTFOUND; + + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex); + MonExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + break; + } + } + } + break; + + case MON_MESSAGE_STOP: + // Stop requested, so abort the whole thread + Trace(REPORT_DEBUG, "Waiter thread was told to stop"); + fAgain = FALSE; + fContinue = FALSE; + ExitFunction1(hr = static_cast(msg.wParam)); + + default: + Assert(false); + break; + } + } + } while (fAgain); + } + else if (dwRet > WAIT_OBJECT_0 && dwRet - WAIT_OBJECT_0 < pWaiterContext->cHandles) + { + // OK a handle fired - only notify if it's the actual target, and not just some parent waiting for the target child to exist + dwRequestIndex = dwRet - WAIT_OBJECT_0 - 1; + fNotify = (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1); + + // Initiate re-waits before we notify callback, to ensure we don't miss a single update + hrTemp = InitiateWait(pWaiterContext->rgRequests + dwRequestIndex, pWaiterContext->rgHandles + dwRequestIndex + 1); + hr = UpdateWaitStatus(hrTemp, pWaiterContext, dwRequestIndex, &dwRequestIndex); + MonExitOnFailure(hr, "Failed to update wait status"); + hrTemp = S_OK; + + // If there were no errors and we were already waiting on the right target, or if we weren't yet but are able to now, it's a successful notify + if (SUCCEEDED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus) && (fNotify || (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1))) + { + Trace(REPORT_DEBUG, "Changes detected, waiting for silence period index %u", dwRequestIndex); + + if (0 < pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs) + { + pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs = 0; + pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = TRUE; + + if (!pWaiterContext->rgRequests[dwRequestIndex].fPendingFire) + { + pWaiterContext->rgRequests[dwRequestIndex].fPendingFire = TRUE; + ++pWaiterContext->cRequestsPending; + } + } + else + { + // If no silence period, notify immediately + Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex); + } + } + } + else if (WAIT_TIMEOUT != dwRet) + { + MonExitWithLastError(hr, "Failed to wait for multiple objects with return code %u", dwRet); + } + + // OK, now that we've checked all triggered handles (resetting silence period timers appropriately), check for any pending notifications that we can finally fire + // And set dwWait appropriately so we awaken at the right time to fire the next pending notification (in case no further writes occur during that time) + if (0 < pWaiterContext->cRequestsPending) + { + // Start at max value and find the lowest wait we can below that + dwWait = DWORD_MAX; + cRequestsPendingBeforeLoop = pWaiterContext->cRequestsPending; + + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (pWaiterContext->rgRequests[i].fPendingFire) + { + if (0 == cRequestsPendingBeforeLoop) + { + Assert(FALSE); + hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT); + MonExitOnFailure(hr, "Phantom pending fires were found!"); + } + --cRequestsPendingBeforeLoop; + + dwRequestIndex = i; + + if (pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd) + { + pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = FALSE; + } + else + { + pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs += uDeltaInMs; + } + + // silence period has elapsed without further notifications, so reset pending-related variables, and finally fire a notify! + if (pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs >= pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs) + { + Trace(REPORT_DEBUG, "Silence period surpassed, notifying %u ms late", pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs); + Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex); + } + else + { + // set dwWait to the shortest interval period so that if no changes occur, WaitForMultipleObjects + // wakes the thread back up when it's time to fire the next pending notification + if (dwWait > pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs) + { + dwWait = pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs; + } + } + } + } + + // Some post-loop list validation for sanity checking + if (0 < cRequestsPendingBeforeLoop) + { + Assert(FALSE); + hr = HRESULT_FROM_WIN32(PEERDIST_ERROR_MISSING_DATA); + MonExitOnFailure(hr, "Missing %u pending fires! Total pending fires: %u, wait: %u", cRequestsPendingBeforeLoop, pWaiterContext->cRequestsPending, dwWait); + } + if (0 < pWaiterContext->cRequestsPending && DWORD_MAX == dwWait) + { + Assert(FALSE); + hr = HRESULT_FROM_WIN32(ERROR_CANT_WAIT); + MonExitOnFailure(hr, "Pending fires exist (%u), but wait was infinite", cRequestsPendingBeforeLoop); + } + } + } while (fContinue); + + // Don't bother firing pending notifications. We were told to stop monitoring, so client doesn't care. + +LExit: + ReleaseStr(sczDirectory); + MonAddMessageDestroy(pAddMessage); + MonRemoveMessageDestroy(pRemoveMessage); + + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + MonRequestDestroy(pWaiterContext->rgRequests + i); + + switch (pWaiterContext->rgRequests[i].type) + { + case MON_DIRECTORY: + if (INVALID_HANDLE_VALUE != pWaiterContext->rgHandles[i + 1]) + { + ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]); + } + break; + case MON_REGKEY: + ReleaseHandle(pWaiterContext->rgHandles[i + 1]); + break; + default: + Assert(false); + } + } + + if (FAILED(hr)) + { + // If waiter thread fails, notify general callback of an error + Assert(pWaiterContext->vpfMonGeneral); + pWaiterContext->vpfMonGeneral(hr, pWaiterContext->pvContext); + + // And tell coordinator to shut all other waiters down + if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0)) + { + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to coordinator thread to stop (due to general failure)."); + } + } + + return hr; +} + +static void Notify( + __in HRESULT hr, + __in MON_WAITER_CONTEXT *pWaiterContext, + __in MON_REQUEST *pRequest + ) +{ + if (pRequest->fPendingFire) + { + --pWaiterContext->cRequestsPending; + } + + pRequest->fPendingFire = FALSE; + pRequest->fSkipDeltaAdd = FALSE; + pRequest->dwSilencePeriodInMs = 0; + + switch (pRequest->type) + { + case MON_DIRECTORY: + Assert(pWaiterContext->vpfMonDirectory); + pWaiterContext->vpfMonDirectory(hr, pRequest->sczOriginalPathRequest, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext); + break; + case MON_REGKEY: + Assert(pWaiterContext->vpfMonRegKey); + pWaiterContext->vpfMonRegKey(hr, pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1], pRequest->regkey.kbKeyBitness, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext); + break; + default: + Assert(false); + } +} + +static BOOL GetRecursiveFlag( + __in MON_REQUEST *pRequest, + __in DWORD dwIndex + ) +{ + if (pRequest->cPathHierarchy - 1 == dwIndex) + { + return pRequest->fRecursive; + } + else + { + return FALSE; + } +} + +static HRESULT FindRequestIndex( + __in MON_WAITER_CONTEXT *pWaiterContext, + __in MON_REMOVE_MESSAGE *pMessage, + __out DWORD *pdwIndex + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i) + { + if (pWaiterContext->rgRequests[i].type == pMessage->type) + { + switch (pWaiterContext->rgRequests[i].type) + { + case MON_DIRECTORY: + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pWaiterContext->rgRequests[i].rgsczPathHierarchy[pWaiterContext->rgRequests[i].cPathHierarchy - 1], -1, pMessage->directory.sczDirectory, -1) && pWaiterContext->rgRequests[i].fRecursive == pMessage->fRecursive) + { + *pdwIndex = i; + ExitFunction1(hr = S_OK); + } + break; + case MON_REGKEY: + if (reinterpret_cast(pMessage->regkey.hkRoot) == reinterpret_cast(pWaiterContext->rgRequests[i].regkey.hkRoot) && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pWaiterContext->rgRequests[i].rgsczPathHierarchy[pWaiterContext->rgRequests[i].cPathHierarchy - 1], -1, pMessage->regkey.sczSubKey, -1) && pWaiterContext->rgRequests[i].fRecursive == pMessage->fRecursive && pWaiterContext->rgRequests[i].regkey.kbKeyBitness == pMessage->regkey.kbKeyBitness) + { + *pdwIndex = i; + ExitFunction1(hr = S_OK); + } + break; + default: + Assert(false); + } + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +static HRESULT RemoveRequest( + __inout MON_WAITER_CONTEXT *pWaiterContext, + __in DWORD dwRequestIndex + ) +{ + HRESULT hr = S_OK; + + MonRequestDestroy(pWaiterContext->rgRequests + dwRequestIndex); + + switch (pWaiterContext->rgRequests[dwRequestIndex].type) + { + case MON_DIRECTORY: + if (pWaiterContext->rgHandles[dwRequestIndex + 1] != INVALID_HANDLE_VALUE) + { + ::FindCloseChangeNotification(pWaiterContext->rgHandles[dwRequestIndex + 1]); + } + break; + case MON_REGKEY: + ReleaseHandle(pWaiterContext->rgHandles[dwRequestIndex + 1]); + break; + default: + Assert(false); + } + + if (pWaiterContext->rgRequests[dwRequestIndex].fPendingFire) + { + --pWaiterContext->cRequestsPending; + } + + if (FAILED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus)) + { + --pWaiterContext->cRequestsFailing; + } + + MemRemoveFromArray(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), TRUE); + --pWaiterContext->cHandles; + MemRemoveFromArray(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), TRUE); + --pWaiterContext->cRequests; + + // Notify coordinator thread that a wait was removed + if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_REMOVED, static_cast(::GetCurrentThreadId()), 0)) + { + MonExitWithLastError(hr, "Failed to send message to coordinator thread to confirm directory was removed."); + } + +LExit: + return hr; +} + +static REGSAM GetRegKeyBitness( + __in MON_REQUEST *pRequest + ) +{ + if (REG_KEY_32BIT == pRequest->regkey.kbKeyBitness) + { + return KEY_WOW64_32KEY; + } + else if (REG_KEY_64BIT == pRequest->regkey.kbKeyBitness) + { + return KEY_WOW64_64KEY; + } + else + { + return 0; + } +} + +static HRESULT DuplicateRemoveMessage( + __in MON_REMOVE_MESSAGE *pMessage, + __out MON_REMOVE_MESSAGE **ppMessage + ) +{ + HRESULT hr = S_OK; + + *ppMessage = reinterpret_cast(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE)); + MonExitOnNull(*ppMessage, hr, E_OUTOFMEMORY, "Failed to allocate copy of remove message"); + + (*ppMessage)->type = pMessage->type; + (*ppMessage)->fRecursive = pMessage->fRecursive; + + switch (pMessage->type) + { + case MON_DIRECTORY: + hr = StrAllocString(&(*ppMessage)->directory.sczDirectory, pMessage->directory.sczDirectory, 0); + MonExitOnFailure(hr, "Failed to copy directory"); + break; + case MON_REGKEY: + (*ppMessage)->regkey.hkRoot = pMessage->regkey.hkRoot; + (*ppMessage)->regkey.kbKeyBitness = pMessage->regkey.kbKeyBitness; + hr = StrAllocString(&(*ppMessage)->regkey.sczSubKey, pMessage->regkey.sczSubKey, 0); + MonExitOnFailure(hr, "Failed to copy subkey"); + break; + default: + Assert(false); + break; + } + +LExit: + return hr; +} + +static LRESULT CALLBACK MonWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + HRESULT hr = S_OK; + DEV_BROADCAST_HDR *pHdr = NULL; + DEV_BROADCAST_HANDLE *pHandle = NULL; + DEV_BROADCAST_VOLUME *pVolume = NULL; + DWORD dwUnitMask = 0; + DWORD er = ERROR_SUCCESS; + WCHAR chDrive = L'\0'; + BOOL fArrival = FALSE; + BOOL fReturnTrue = FALSE; + CREATESTRUCT *pCreateStruct = NULL; + MON_WAITER_CONTEXT *pWaiterContext = NULL; + MON_STRUCT *pm = NULL; + + // keep track of the MON_STRUCT pointer that was passed in on init, associate it with the window + if (WM_CREATE == uMsg) + { + pCreateStruct = reinterpret_cast(lParam); + if (pCreateStruct) + { + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pCreateStruct->lpCreateParams)); + } + } + else if (WM_NCDESTROY == uMsg) + { + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + } + + // Note this message ONLY comes in through WndProc, it isn't visible from the GetMessage loop. + else if (WM_DEVICECHANGE == uMsg) + { + if (DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam) + { + fArrival = DBT_DEVICEARRIVAL == wParam; + + pHdr = reinterpret_cast(lParam); + if (DBT_DEVTYP_VOLUME == pHdr->dbch_devicetype) + { + pVolume = reinterpret_cast(lParam); + dwUnitMask = pVolume->dbcv_unitmask; + chDrive = L'a'; + while (0 < dwUnitMask) + { + if (dwUnitMask & 0x1) + { + // This drive had a status update, so send it out to all threads + if (!::PostThreadMessageW(::GetCurrentThreadId(), MON_MESSAGE_DRIVE_STATUS_UPDATE, static_cast(chDrive), static_cast(fArrival))) + { + MonExitWithLastError(hr, "Failed to send drive status update with drive %wc and arrival %ls", chDrive, fArrival ? L"TRUE" : L"FALSE"); + } + } + dwUnitMask >>= 1; + ++chDrive; + + if (chDrive == 'z') + { + hr = E_UNEXPECTED; + MonExitOnFailure(hr, "UnitMask showed drives beyond z:. Remaining UnitMask at this point: %u", dwUnitMask); + } + } + } + } + // We can only process device query remove messages if we have a MON_STRUCT pointer + else if (DBT_DEVICEQUERYREMOVE == wParam) + { + pm = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); + if (!pm) + { + hr = E_POINTER; + MonExitOnFailure(hr, "DBT_DEVICEQUERYREMOVE message received with no MON_STRUCT pointer, so message was ignored"); + } + + fReturnTrue = TRUE; + + pHdr = reinterpret_cast(lParam); + if (DBT_DEVTYP_HANDLE == pHdr->dbch_devicetype) + { + // We must wait for the actual wait handle to be released by waiter thread before telling windows to proceed with device removal, otherwise it could fail + // due to handles still being open, so use a MON_INTERNAL_TEMPORARY_WAIT struct to send and receive a reply from a waiter thread + pm->internalWait.hWait = ::CreateEventW(NULL, TRUE, FALSE, NULL); + MonExitOnNullWithLastError(pm->internalWait.hWait, hr, "Failed to create anonymous event for waiter to notify wndproc device can be removed"); + + pHandle = reinterpret_cast(lParam); + pm->internalWait.pvContext = pHandle->dbch_handle; + pm->internalWait.dwReceiveIteration = pm->internalWait.dwSendIteration - 1; + // This drive had a status update, so send it out to all threads + for (DWORD i = 0; i < pm->cWaiterThreads; ++i) + { + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext; + + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_QUERY_REMOVE, reinterpret_cast(&pm->internalWait), static_cast(pm->internalWait.dwSendIteration))) + { + MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive query remove"); + } + + if (!::SetEvent(pWaiterContext->rgHandles[0])) + { + MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive query remove message"); + } + } + + er = ::WaitForSingleObject(pm->internalWait.hWait, MON_THREAD_WAIT_REMOVE_DEVICE); + // Make sure any waiter thread processing really old messages can immediately know that we're no longer waiting for a response + if (WAIT_OBJECT_0 == er) + { + // If the response ID matches what we sent, we actually got a valid reply! + if (pm->internalWait.dwReceiveIteration != pm->internalWait.dwSendIteration) + { + TraceError(HRESULT_FROM_WIN32(er), "Waiter thread received wrong ID reply"); + } + } + else if (WAIT_TIMEOUT == er) + { + TraceError(HRESULT_FROM_WIN32(er), "No response from any waiter thread for query remove message"); + } + else + { + MonExitWithLastError(hr, "WaitForSingleObject failed with non-timeout reason while waiting for response from waiter thread"); + } + ++pm->internalWait.dwSendIteration; + } + } + } + +LExit: + if (pm) + { + ReleaseHandle(pm->internalWait.hWait); + } + + if (fReturnTrue) + { + return TRUE; + } + else + { + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + } +} + +static HRESULT CreateMonWindow( + __in MON_STRUCT *pm, + __out HWND *pHwnd + ) +{ + HRESULT hr = S_OK; + WNDCLASSW wc = { }; + + wc.lpfnWndProc = MonWndProc; + wc.hInstance = ::GetModuleHandleW(NULL); + wc.lpszClassName = MONUTIL_WINDOW_CLASS; + if (!::RegisterClassW(&wc)) + { + if (ERROR_CLASS_ALREADY_EXISTS != ::GetLastError()) + { + MonExitWithLastError(hr, "Failed to register MonUtil window class."); + } + } + + *pHwnd = ::CreateWindowExW(0, wc.lpszClassName, L"", 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, HWND_DESKTOP, NULL, wc.hInstance, pm); + MonExitOnNullWithLastError(*pHwnd, hr, "Failed to create window."); + + // Rumor has it that drive arrival / removal events can be lost in the rare event that some other application higher up in z-order is hanging if we don't make our window topmost + // SWP_NOACTIVATE is important so the currently active window doesn't lose focus + SetWindowPos(*pHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_DEFERERASE | SWP_NOACTIVATE); + +LExit: + return hr; +} + +static HRESULT WaitForNetworkChanges( + __inout HANDLE *phMonitor, + __in MON_STRUCT *pm + ) +{ + HRESULT hr = S_OK; + int nResult = 0; + DWORD dwBytesReturned = 0; + WSACOMPLETION wsaCompletion = { }; + WSAQUERYSET qsRestrictions = { }; + + qsRestrictions.dwSize = sizeof(WSAQUERYSET); + qsRestrictions.dwNameSpace = NS_NLA; + + if (NULL != *phMonitor) + { + ::WSALookupServiceEnd(*phMonitor); + *phMonitor = NULL; + } + + if (::WSALookupServiceBegin(&qsRestrictions, LUP_RETURN_ALL, phMonitor)) + { + hr = HRESULT_FROM_WIN32(::WSAGetLastError()); + MonExitOnFailure(hr, "WSALookupServiceBegin() failed"); + } + + wsaCompletion.Type = NSP_NOTIFY_HWND; + wsaCompletion.Parameters.WindowMessage.hWnd = pm->hwnd; + wsaCompletion.Parameters.WindowMessage.uMsg = MON_MESSAGE_NETWORK_STATUS_UPDATE; + nResult = ::WSANSPIoctl(*phMonitor, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &dwBytesReturned, &wsaCompletion); + if (SOCKET_ERROR != nResult || WSA_IO_PENDING != ::WSAGetLastError()) + { + hr = HRESULT_FROM_WIN32(::WSAGetLastError()); + if (SUCCEEDED(hr)) + { + hr = E_FAIL; + } + MonExitOnFailure(hr, "WSANSPIoctl() failed with return code %i, wsa last error %u", nResult, ::WSAGetLastError()); + } + +LExit: + return hr; +} + +static HRESULT UpdateWaitStatus( + __in HRESULT hrNewStatus, + __inout MON_WAITER_CONTEXT *pWaiterContext, + __in DWORD dwRequestIndex, + __out_opt DWORD *pdwNewRequestIndex + ) +{ + HRESULT hr = S_OK; + DWORD dwNewRequestIndex; + MON_REQUEST *pRequest = pWaiterContext->rgRequests + dwRequestIndex; + + if (NULL != pdwNewRequestIndex) + { + *pdwNewRequestIndex = dwRequestIndex; + } + + if (SUCCEEDED(pRequest->hrStatus) || SUCCEEDED(hrNewStatus)) + { + // If it's a network wait, notify as long as it's new status is successful because we *may* have lost some changes + // before the wait was re-initiated. Otherwise, only notify if there was an interesting status change + if (SUCCEEDED(pRequest->hrStatus) != SUCCEEDED(hrNewStatus) || (pRequest->fNetwork && SUCCEEDED(hrNewStatus))) + { + Notify(hrNewStatus, pWaiterContext, pRequest); + } + + if (SUCCEEDED(pRequest->hrStatus) && FAILED(hrNewStatus)) + { + // If it's a network wait, notify coordinator thread that a network wait is failing + if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_FAILED, 0, 0)) + { + MonExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait started to fail"); + } + + // Move the failing wait to the end of the list of waits and increment cRequestsFailing so WaitForMultipleObjects isn't passed an invalid handle + ++pWaiterContext->cRequestsFailing; + dwNewRequestIndex = pWaiterContext->cRequests - 1; + MemArraySwapItems(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles)); + MemArraySwapItems(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests)); + // Reset pRequest to the newly swapped item + pRequest = pWaiterContext->rgRequests + dwNewRequestIndex; + if (NULL != pdwNewRequestIndex) + { + *pdwNewRequestIndex = dwNewRequestIndex; + } + } + else if (FAILED(pRequest->hrStatus) && SUCCEEDED(hrNewStatus)) + { + Assert(pWaiterContext->cRequestsFailing > 0); + // If it's a network wait, notify coordinator thread that a network wait is succeeding again + if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_SUCCEEDED, 0, 0)) + { + MonExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait is succeeding again"); + } + + --pWaiterContext->cRequestsFailing; + dwNewRequestIndex = 0; + MemArraySwapItems(reinterpret_cast(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles)); + MemArraySwapItems(reinterpret_cast(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests)); + // Reset pRequest to the newly swapped item + pRequest = pWaiterContext->rgRequests + dwNewRequestIndex; + if (NULL != pdwNewRequestIndex) + { + *pdwNewRequestIndex = dwNewRequestIndex; + } + } + } + + pRequest->hrStatus = hrNewStatus; + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/osutil.cpp b/src/libs/dutil/WixToolset.DUtil/osutil.cpp new file mode 100644 index 00000000..880ec3ea --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/osutil.cpp @@ -0,0 +1,251 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define OsExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__) +#define OsExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__) +#define OsExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__) +#define OsExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__) +#define OsExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__) +#define OsExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_OSUTIL, e, x, s, __VA_ARGS__) +#define OsExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_OSUTIL, g, x, s, __VA_ARGS__) + +typedef NTSTATUS(NTAPI* PFN_RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation); + +OS_VERSION vOsVersion = OS_VERSION_UNKNOWN; +DWORD vdwOsServicePack = 0; +RTL_OSVERSIONINFOEXW vovix = { }; + +/******************************************************************** + OsGetVersion + +********************************************************************/ +extern "C" void DAPI OsGetVersion( + __out OS_VERSION* pVersion, + __out DWORD* pdwServicePack + ) +{ + OSVERSIONINFOEXW ovi = { }; + + if (OS_VERSION_UNKNOWN == vOsVersion) + { + ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + +#pragma warning (push) +#pragma warning(suppress: 4996) // deprecated +#pragma warning (push) +#pragma warning(suppress: 28159)// deprecated, use other function instead + ::GetVersionExW(reinterpret_cast(&ovi)); // only fails if version info size is set incorrectly. +#pragma warning (pop) +#pragma warning (pop) + + vdwOsServicePack = static_cast(ovi.wServicePackMajor) << 16 | ovi.wServicePackMinor; + if (4 == ovi.dwMajorVersion) + { + vOsVersion = OS_VERSION_WINNT; + } + else if (5 == ovi.dwMajorVersion) + { + if (0 == ovi.dwMinorVersion) + { + vOsVersion = OS_VERSION_WIN2000; + } + else if (1 == ovi.dwMinorVersion) + { + vOsVersion = OS_VERSION_WINXP; + } + else if (2 == ovi.dwMinorVersion) + { + vOsVersion = OS_VERSION_WIN2003; + } + else + { + vOsVersion = OS_VERSION_FUTURE; + } + } + else if (6 == ovi.dwMajorVersion) + { + if (0 == ovi.dwMinorVersion) + { + vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_VISTA : OS_VERSION_WIN2008; + } + else if (1 == ovi.dwMinorVersion) + { + vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_WIN7 : OS_VERSION_WIN2008_R2; + } + else + { + vOsVersion = OS_VERSION_FUTURE; + } + } + else + { + vOsVersion = OS_VERSION_FUTURE; + } + } + + *pVersion = vOsVersion; + *pdwServicePack = vdwOsServicePack; +} + +extern "C" HRESULT DAPI OsCouldRunPrivileged( + __out BOOL* pfPrivileged + ) +{ + HRESULT hr = S_OK; + BOOL fUacEnabled = FALSE; + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + PSID AdministratorsGroup = NULL; + + // Do a best effort check to see if UAC is enabled on this machine. + OsIsUacEnabled(&fUacEnabled); + + // If UAC is enabled then the process could run privileged by asking to elevate. + if (fUacEnabled) + { + *pfPrivileged = TRUE; + } + else // no UAC so only privilged if user is in administrators group. + { + *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); + if (*pfPrivileged) + { + if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged)) + { + *pfPrivileged = FALSE; + } + } + } + + ReleaseSid(AdministratorsGroup); + return hr; +} + +extern "C" HRESULT DAPI OsIsRunningPrivileged( + __out BOOL* pfPrivileged + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + HANDLE hToken = NULL; + TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeDefault; + DWORD dwSize = 0; + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + PSID AdministratorsGroup = NULL; + + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken)) + { + OsExitOnLastError(hr, "Failed to open process token."); + } + + if (::GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) + { + *pfPrivileged = (TokenElevationTypeFull == elevationType); + ExitFunction1(hr = S_OK); + } + + // If it's invalid argument, this means they don't support TokenElevationType, and we should fallback to another check + er = ::GetLastError(); + if (ERROR_INVALID_FUNCTION == er) + { + er = ERROR_SUCCESS; + } + OsExitOnWin32Error(er, hr, "Failed to get process token information."); + + // Fallback to this check for some OS's (like XP) + *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); + if (*pfPrivileged) + { + if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged)) + { + *pfPrivileged = FALSE; + } + } + +LExit: + ReleaseSid(AdministratorsGroup); + + if (hToken) + { + ::CloseHandle(hToken); + } + + return hr; +} + +extern "C" HRESULT DAPI OsIsUacEnabled( + __out BOOL* pfUacEnabled + ) +{ + HRESULT hr = S_OK; + HKEY hk = NULL; + DWORD dwUacEnabled = 0; + + *pfUacEnabled = FALSE; // assume UAC not enabled. + + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ, &hk); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + OsExitOnFailure(hr, "Failed to open system policy key to detect UAC."); + + hr = RegReadNumber(hk, L"EnableLUA", &dwUacEnabled); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + OsExitOnFailure(hr, "Failed to read registry value to detect UAC."); + + *pfUacEnabled = (0 != dwUacEnabled); + +LExit: + ReleaseRegKey(hk); + + return hr; +} + +HRESULT DAPI OsRtlGetVersion( + __inout RTL_OSVERSIONINFOEXW* pOvix + ) +{ + HRESULT hr = S_OK; + HMODULE hNtdll = NULL; + PFN_RTL_GET_VERSION pfnRtlGetVersion = NULL; + + if (vovix.dwOSVersionInfoSize) + { + ExitFunction(); + } + + vovix.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + + hr = LoadSystemLibrary(L"ntdll.dll", &hNtdll); + if (E_MODNOTFOUND == hr) + { + OsExitOnRootFailure(hr = E_NOTIMPL, "Failed to load ntdll.dll"); + } + OsExitOnFailure(hr, "Failed to load ntdll.dll."); + + pfnRtlGetVersion = reinterpret_cast(::GetProcAddress(hNtdll, "RtlGetVersion")); + OsExitOnNullWithLastError(pfnRtlGetVersion, hr, "Failed to locate RtlGetVersion."); + + hr = static_cast(pfnRtlGetVersion(&vovix)); + +LExit: + memcpy(pOvix, &vovix, sizeof(RTL_OSVERSIONINFOEXW)); + + if (hNtdll) + { + ::FreeLibrary(hNtdll); + } + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/packages.config b/src/libs/dutil/WixToolset.DUtil/packages.config new file mode 100644 index 00000000..5bbcd994 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp new file mode 100644 index 00000000..ff3a946d --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp @@ -0,0 +1,104 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) +#define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) +#define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) +#define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) +#define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__) +#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__) + + +DAPI_(HRESULT) PathCanonicalizePath( + __in_z LPCWSTR wzPath, + __deref_out_z LPWSTR* psczCanonicalized + ) +{ + HRESULT hr = S_OK; + int cch = MAX_PATH + 1; + + hr = StrAlloc(psczCanonicalized, cch); + PathExitOnFailure(hr, "Failed to allocate string for the canonicalized path."); + + if (::PathCanonicalizeW(*psczCanonicalized, wzPath)) + { + hr = S_OK; + } + else + { + ExitFunctionWithLastError(hr); + } + +LExit: + return hr; +} + +DAPI_(HRESULT) PathDirectoryContainsPath( + __in_z LPCWSTR wzDirectory, + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + LPWSTR sczDirectory = NULL; + LPWSTR sczOriginalPath = NULL; + LPWSTR sczOriginalDirectory = NULL; + + hr = PathCanonicalizePath(wzPath, &sczOriginalPath); + PathExitOnFailure(hr, "Failed to canonicalize the path."); + + hr = PathCanonicalizePath(wzDirectory, &sczOriginalDirectory); + PathExitOnFailure(hr, "Failed to canonicalize the directory."); + + if (!sczOriginalPath || !*sczOriginalPath) + { + ExitFunction1(hr = S_FALSE); + } + if (!sczOriginalDirectory || !*sczOriginalDirectory) + { + ExitFunction1(hr = S_FALSE); + } + + sczPath = sczOriginalPath; + sczDirectory = sczOriginalDirectory; + + for (; *sczDirectory;) + { + if (!*sczPath) + { + ExitFunction1(hr = S_FALSE); + } + + if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczDirectory, 1, sczPath, 1)) + { + ExitFunction1(hr = S_FALSE); + } + + ++sczDirectory; + ++sczPath; + } + + --sczDirectory; + if (('\\' == *sczDirectory && *sczPath) || '\\' == *sczPath) + { + hr = S_OK; + } + else + { + hr = S_FALSE; + } + +LExit: + ReleaseStr(sczOriginalPath); + ReleaseStr(sczOriginalDirectory); + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp new file mode 100644 index 00000000..7c3cfe06 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp @@ -0,0 +1,1083 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) +#define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) +#define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) +#define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) +#define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) +#define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__) +#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__) + +#define PATH_GOOD_ENOUGH 64 + + +DAPI_(HRESULT) PathCommandLineAppend( + __deref_inout_z LPWSTR* psczCommandLine, + __in_z LPCWSTR wzArgument + ) +{ + HRESULT hr = S_OK; + LPWSTR sczQuotedArg = NULL; + BOOL fRequiresQuoting = FALSE; + DWORD dwMaxEscapedSize = 0; + + // Loop through the argument determining if it needs to be quoted and what the maximum + // size would be if there are escape characters required. + for (LPCWSTR pwz = wzArgument; *pwz; ++pwz) + { + // Arguments with whitespace need quoting. + if (L' ' == *pwz || L'\t' == *pwz || L'\n' == *pwz || L'\v' == *pwz) + { + fRequiresQuoting = TRUE; + } + else if (L'"' == *pwz) // quotes need quoting and sometimes escaping. + { + fRequiresQuoting = TRUE; + ++dwMaxEscapedSize; + } + else if (L'\\' == *pwz) // some backslashes need escaping, so we'll count them all to make sure there is room. + { + ++dwMaxEscapedSize; + } + + ++dwMaxEscapedSize; + } + + // If we found anything in the argument that requires our argument to be quoted + if (fRequiresQuoting) + { + hr = StrAlloc(&sczQuotedArg, dwMaxEscapedSize + 3); // plus three for the start and end quote plus null terminator. + PathExitOnFailure(hr, "Failed to allocate argument to be quoted."); + + LPCWSTR pwz = wzArgument; + LPWSTR pwzQuoted = sczQuotedArg; + + *pwzQuoted = L'"'; + ++pwzQuoted; + while (*pwz) + { + DWORD dwBackslashes = 0; + while (L'\\' == *pwz) + { + ++dwBackslashes; + ++pwz; + } + + // Escape all backslashes at the end of the string. + if (!*pwz) + { + dwBackslashes *= 2; + } + else if (L'"' == *pwz) // escape all backslashes before the quote and escape the quote itself. + { + dwBackslashes = dwBackslashes * 2 + 1; + } + // the backslashes don't have to be escaped. + + // Add the appropriate number of backslashes + for (DWORD i = 0; i < dwBackslashes; ++i) + { + *pwzQuoted = L'\\'; + ++pwzQuoted; + } + + // If there is a character, add it after all the escaped backslashes + if (*pwz) + { + *pwzQuoted = *pwz; + ++pwz; + ++pwzQuoted; + } + } + + *pwzQuoted = L'"'; + ++pwzQuoted; + *pwzQuoted = L'\0'; // ensure the arg is null terminated. + } + + // If there is already data in the command line, append a space before appending the + // argument. + if (*psczCommandLine && **psczCommandLine) + { + hr = StrAllocConcat(psczCommandLine, L" ", 0); + PathExitOnFailure(hr, "Failed to append space to command line with existing data."); + } + + hr = StrAllocConcat(psczCommandLine, sczQuotedArg ? sczQuotedArg : wzArgument, 0); + PathExitOnFailure(hr, "Failed to copy command line argument."); + +LExit: + ReleaseStr(sczQuotedArg); + + return hr; +} + + +DAPI_(LPWSTR) PathFile( + __in_z LPCWSTR wzPath + ) +{ + if (!wzPath) + { + return NULL; + } + + LPWSTR wzFile = const_cast(wzPath); + for (LPWSTR wz = wzFile; *wz; ++wz) + { + // valid delineators + // \ => Windows path + // / => unix and URL path + // : => relative path from mapped root + if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) + { + wzFile = wz + 1; + } + } + + return wzFile; +} + + +DAPI_(LPCWSTR) PathExtension( + __in_z LPCWSTR wzPath + ) +{ + if (!wzPath) + { + return NULL; + } + + // Find the last dot in the last thing that could be a file. + LPCWSTR wzExtension = NULL; + for (LPCWSTR wz = wzPath; *wz; ++wz) + { + if (L'\\' == *wz || L'/' == *wz || L':' == *wz) + { + wzExtension = NULL; + } + else if (L'.' == *wz) + { + wzExtension = wz; + } + } + + return wzExtension; +} + + +DAPI_(HRESULT) PathGetDirectory( + __in_z LPCWSTR wzPath, + __out_z LPWSTR *psczDirectory + ) +{ + HRESULT hr = S_OK; + size_t cchDirectory = SIZE_T_MAX; + + for (LPCWSTR wz = wzPath; *wz; ++wz) + { + // valid delineators: + // \ => Windows path + // / => unix and URL path + // : => relative path from mapped root + if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) + { + cchDirectory = static_cast(wz - wzPath) + 1; + } + } + + if (SIZE_T_MAX == cchDirectory) + { + // we were given just a file name, so there's no directory available + return S_FALSE; + } + + if (wzPath[0] == L'\"') + { + ++wzPath; + --cchDirectory; + } + + hr = StrAllocString(psczDirectory, wzPath, cchDirectory); + PathExitOnFailure(hr, "Failed to copy directory."); + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathGetParentPath( + __in_z LPCWSTR wzPath, + __out_z LPWSTR *psczParent + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzParent = NULL; + + for (LPCWSTR wz = wzPath; *wz; ++wz) + { + if (wz[1] && (L'\\' == *wz || L'/' == *wz)) + { + wzParent = wz; + } + } + + if (wzParent) + { + size_t cchPath = static_cast(wzParent - wzPath) + 1; + + hr = StrAllocString(psczParent, wzPath, cchPath); + PathExitOnFailure(hr, "Failed to copy directory."); + } + else + { + ReleaseNullStr(psczParent); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathExpand( + __out LPWSTR *psczFullPath, + __in_z LPCWSTR wzRelativePath, + __in DWORD dwResolveFlags + ) +{ + Assert(wzRelativePath && *wzRelativePath); + + HRESULT hr = S_OK; + DWORD cch = 0; + LPWSTR sczExpandedPath = NULL; + DWORD cchExpandedPath = 0; + SIZE_T cbSize = 0; + + LPWSTR sczFullPath = NULL; + + // + // First, expand any environment variables. + // + if (dwResolveFlags & PATH_EXPAND_ENVIRONMENT) + { + cchExpandedPath = PATH_GOOD_ENOUGH; + + hr = StrAlloc(&sczExpandedPath, cchExpandedPath); + PathExitOnFailure(hr, "Failed to allocate space for expanded path."); + + cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); + if (0 == cch) + { + PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + } + else if (cchExpandedPath < cch) + { + cchExpandedPath = cch; + hr = StrAlloc(&sczExpandedPath, cchExpandedPath); + PathExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); + + cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); + if (0 == cch) + { + PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); + } + else if (cchExpandedPath < cch) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + PathExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); + } + } + + if (MAX_PATH < cch) + { + hr = PathPrefix(&sczExpandedPath); // ignore invald arg from path prefix because this may not be a complete path yet + if (E_INVALIDARG == hr) + { + hr = S_OK; + } + PathExitOnFailure(hr, "Failed to prefix long path after expanding environment variables."); + + hr = StrMaxLength(sczExpandedPath, &cbSize); + PathExitOnFailure(hr, "Failed to get max length of expanded path."); + + cchExpandedPath = (DWORD)min(DWORD_MAX, cbSize); + } + } + + // + // Second, get the full path. + // + if (dwResolveFlags & PATH_EXPAND_FULLPATH) + { + LPWSTR wzFileName = NULL; + LPCWSTR wzPath = sczExpandedPath ? sczExpandedPath : wzRelativePath; + DWORD cchFullPath = max(PATH_GOOD_ENOUGH, cchExpandedPath); + + hr = StrAlloc(&sczFullPath, cchFullPath); + PathExitOnFailure(hr, "Failed to allocate space for full path."); + + cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); + if (0 == cch) + { + PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); + } + else if (cchFullPath < cch) + { + cchFullPath = cch < MAX_PATH ? cch : cch + 7; // ensure space for "\\?\UNC" prefix if needed + hr = StrAlloc(&sczFullPath, cchFullPath); + PathExitOnFailure(hr, "Failed to re-allocate more space for full path."); + + cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); + if (0 == cch) + { + PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); + } + else if (cchFullPath < cch) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + PathExitOnRootFailure(hr, "Failed to allocate buffer for full path."); + } + } + + if (MAX_PATH < cch) + { + hr = PathPrefix(&sczFullPath); + PathExitOnFailure(hr, "Failed to prefix long path after expanding."); + } + } + else + { + sczFullPath = sczExpandedPath; + sczExpandedPath = NULL; + } + + hr = StrAllocString(psczFullPath, sczFullPath? sczFullPath : wzRelativePath, 0); + PathExitOnFailure(hr, "Failed to copy relative path into full path."); + +LExit: + ReleaseStr(sczFullPath); + ReleaseStr(sczExpandedPath); + + return hr; +} + + +DAPI_(HRESULT) PathPrefix( + __inout LPWSTR *psczFullPath + ) +{ + Assert(psczFullPath && *psczFullPath); + + HRESULT hr = S_OK; + LPWSTR wzFullPath = *psczFullPath; + SIZE_T cbFullPath = 0; + + if (((L'a' <= wzFullPath[0] && L'z' >= wzFullPath[0]) || + (L'A' <= wzFullPath[0] && L'Z' >= wzFullPath[0])) && + L':' == wzFullPath[1] && + L'\\' == wzFullPath[2]) // normal path + { + hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); + PathExitOnFailure(hr, "Failed to add prefix to file path."); + } + else if (L'\\' == wzFullPath[0] && L'\\' == wzFullPath[1]) // UNC + { + // ensure that we're not already prefixed + if (!(L'?' == wzFullPath[2] && L'\\' == wzFullPath[3])) + { + hr = StrSize(*psczFullPath, &cbFullPath); + PathExitOnFailure(hr, "Failed to get size of full path."); + + memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR)); + + hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); + PathExitOnFailure(hr, "Failed to add prefix to UNC path."); + } + } + else + { + hr = E_INVALIDARG; + PathExitOnFailure(hr, "Invalid path provided to prefix: %ls.", wzFullPath); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathFixedBackslashTerminate( + __inout_ecount_z(cchPath) LPWSTR wzPath, + __in SIZE_T cchPath + ) +{ + HRESULT hr = S_OK; + size_t cchLength = 0; + + hr = ::StringCchLengthW(wzPath, cchPath, &cchLength); + PathExitOnFailure(hr, "Failed to get length of path."); + + if (cchLength >= cchPath) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + else if (L'\\' != wzPath[cchLength - 1]) + { + wzPath[cchLength] = L'\\'; + wzPath[cchLength + 1] = L'\0'; + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathBackslashTerminate( + __inout LPWSTR* psczPath + ) +{ + Assert(psczPath && *psczPath); + + HRESULT hr = S_OK; + SIZE_T cchPath = 0; + size_t cchLength = 0; + + hr = StrMaxLength(*psczPath, &cchPath); + PathExitOnFailure(hr, "Failed to get size of path string."); + + hr = ::StringCchLengthW(*psczPath, cchPath, &cchLength); + PathExitOnFailure(hr, "Failed to get length of path."); + + if (L'\\' != (*psczPath)[cchLength - 1]) + { + hr = StrAllocConcat(psczPath, L"\\", 1); + PathExitOnFailure(hr, "Failed to concat backslash onto string."); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathForCurrentProcess( + __inout LPWSTR *psczFullPath, + __in_opt HMODULE hModule + ) +{ + HRESULT hr = S_OK; + DWORD cch = MAX_PATH; + + do + { + hr = StrAlloc(psczFullPath, cch); + PathExitOnFailure(hr, "Failed to allocate string for module path."); + + DWORD cchRequired = ::GetModuleFileNameW(hModule, *psczFullPath, cch); + if (0 == cchRequired) + { + PathExitWithLastError(hr, "Failed to get path for executing process."); + } + else if (cchRequired == cch) + { + cch = cchRequired + 1; + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + else + { + hr = S_OK; + } + } while (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr); + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathRelativeToModule( + __inout LPWSTR *psczFullPath, + __in_opt LPCWSTR wzFileName, + __in_opt HMODULE hModule + ) +{ + HRESULT hr = PathForCurrentProcess(psczFullPath, hModule); + PathExitOnFailure(hr, "Failed to get current module path."); + + hr = PathGetDirectory(*psczFullPath, psczFullPath); + PathExitOnFailure(hr, "Failed to get current module directory."); + + if (wzFileName) + { + hr = PathConcat(*psczFullPath, wzFileName, psczFullPath); + PathExitOnFailure(hr, "Failed to append filename."); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathCreateTempFile( + __in_opt LPCWSTR wzDirectory, + __in_opt __format_string LPCWSTR wzFileNameTemplate, + __in DWORD dwUniqueCount, + __in DWORD dwFileAttributes, + __out_opt LPWSTR* psczTempFile, + __out_opt HANDLE* phTempFile + ) +{ + AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); + + HRESULT hr = S_OK; + + LPWSTR sczTempPath = NULL; + DWORD cchTempPath = MAX_PATH; + + HANDLE hTempFile = INVALID_HANDLE_VALUE; + LPWSTR scz = NULL; + LPWSTR sczTempFile = NULL; + + if (wzDirectory && *wzDirectory) + { + hr = StrAllocString(&sczTempPath, wzDirectory, 0); + PathExitOnFailure(hr, "Failed to copy temp path."); + } + else + { + hr = StrAlloc(&sczTempPath, cchTempPath); + PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); + + if (!::GetTempPathW(cchTempPath, sczTempPath)) + { + PathExitWithLastError(hr, "Failed to get temp path."); + } + } + + if (wzFileNameTemplate && *wzFileNameTemplate) + { + for (DWORD i = 1; i <= dwUniqueCount && INVALID_HANDLE_VALUE == hTempFile; ++i) + { + hr = StrAllocFormatted(&scz, wzFileNameTemplate, i); + PathExitOnFailure(hr, "Failed to allocate memory for file template."); + + hr = StrAllocFormatted(&sczTempFile, L"%s%s", sczTempPath, scz); + PathExitOnFailure(hr, "Failed to allocate temp file name."); + + hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, dwFileAttributes, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + // if the file already exists, just try again + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) + { + hr = S_OK; + } + PathExitOnFailure(hr, "Failed to create file: %ls", sczTempFile); + } + } + } + + // If we were not able to or we did not try to create a temp file, ask + // the system to provide us a temp file using its built-in mechanism. + if (INVALID_HANDLE_VALUE == hTempFile) + { + hr = StrAlloc(&sczTempFile, MAX_PATH); + PathExitOnFailure(hr, "Failed to allocate memory for the temp path"); + + if (!::GetTempFileNameW(sczTempPath, L"TMP", 0, sczTempFile)) + { + PathExitWithLastError(hr, "Failed to create new temp file name."); + } + + hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, dwFileAttributes, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + PathExitWithLastError(hr, "Failed to open new temp file: %ls", sczTempFile); + } + } + + // If the caller wanted the temp file name or handle, return them here. + if (psczTempFile) + { + hr = StrAllocString(psczTempFile, sczTempFile, 0); + PathExitOnFailure(hr, "Failed to copy temp file string."); + } + + if (phTempFile) + { + *phTempFile = hTempFile; + hTempFile = INVALID_HANDLE_VALUE; + } + +LExit: + if (INVALID_HANDLE_VALUE != hTempFile) + { + ::CloseHandle(hTempFile); + } + + ReleaseStr(scz); + ReleaseStr(sczTempFile); + ReleaseStr(sczTempPath); + + return hr; +} + + +DAPI_(HRESULT) PathCreateTimeBasedTempFile( + __in_z_opt LPCWSTR wzDirectory, + __in_z LPCWSTR wzPrefix, + __in_z_opt LPCWSTR wzPostfix, + __in_z LPCWSTR wzExtension, + __deref_opt_out_z LPWSTR* psczTempFile, + __out_opt HANDLE* phTempFile + ) +{ + HRESULT hr = S_OK; + BOOL fRetry = FALSE; + WCHAR wzTempPath[MAX_PATH] = { }; + LPWSTR sczPrefix = NULL; + LPWSTR sczPrefixFolder = NULL; + SYSTEMTIME time = { }; + + LPWSTR sczTempPath = NULL; + HANDLE hTempFile = INVALID_HANDLE_VALUE; + DWORD dwAttempts = 0; + + if (wzDirectory && *wzDirectory) + { + hr = PathConcat(wzDirectory, wzPrefix, &sczPrefix); + PathExitOnFailure(hr, "Failed to combine directory and log prefix."); + } + else + { + if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) + { + PathExitWithLastError(hr, "Failed to get temp folder."); + } + + hr = PathConcat(wzTempPath, wzPrefix, &sczPrefix); + PathExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix."); + } + + hr = PathGetDirectory(sczPrefix, &sczPrefixFolder); + if (S_OK == hr) + { + hr = DirEnsureExists(sczPrefixFolder, NULL); + PathExitOnFailure(hr, "Failed to ensure temp file path exists: %ls", sczPrefixFolder); + } + + if (!wzPostfix) + { + wzPostfix = L""; + } + + do + { + fRetry = FALSE; + ++dwAttempts; + + ::GetLocalTime(&time); + + // Log format: pre YYYY MM dd hh mm ss post ext + hr = StrAllocFormatted(&sczTempPath, L"%ls_%04u%02u%02u%02u%02u%02u%ls%ls%ls", sczPrefix, time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, wzPostfix, L'.' == *wzExtension ? L"" : L".", wzExtension); + PathExitOnFailure(hr, "failed to allocate memory for the temp path"); + + hTempFile = ::CreateFileW(sczTempPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hTempFile) + { + // If the file already exists, just try again. + DWORD er = ::GetLastError(); + if (ERROR_FILE_EXISTS == er || ERROR_ACCESS_DENIED == er) + { + ::Sleep(100); + + if (10 > dwAttempts) + { + er = ERROR_SUCCESS; + fRetry = TRUE; + } + } + + hr = HRESULT_FROM_WIN32(er); + PathExitOnFailureDebugTrace(hr, "Failed to create temp file: %ls", sczTempPath); + } + } while (fRetry); + + if (psczTempFile) + { + hr = StrAllocString(psczTempFile, sczTempPath, 0); + PathExitOnFailure(hr, "Failed to copy temp path to return."); + } + + if (phTempFile) + { + *phTempFile = hTempFile; + hTempFile = INVALID_HANDLE_VALUE; + } + +LExit: + ReleaseFile(hTempFile); + ReleaseStr(sczTempPath); + ReleaseStr(sczPrefixFolder); + ReleaseStr(sczPrefix); + + return hr; +} + + +DAPI_(HRESULT) PathCreateTempDirectory( + __in_opt LPCWSTR wzDirectory, + __in __format_string LPCWSTR wzDirectoryNameTemplate, + __in DWORD dwUniqueCount, + __out LPWSTR* psczTempDirectory + ) +{ + AssertSz(wzDirectoryNameTemplate && *wzDirectoryNameTemplate, "DirectoryNameTemplate must be specified."); + AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); + + HRESULT hr = S_OK; + + LPWSTR sczTempPath = NULL; + DWORD cchTempPath = MAX_PATH; + + LPWSTR scz = NULL; + + if (wzDirectory && *wzDirectory) + { + hr = StrAllocString(&sczTempPath, wzDirectory, 0); + PathExitOnFailure(hr, "Failed to copy temp path."); + + hr = PathBackslashTerminate(&sczTempPath); + PathExitOnFailure(hr, "Failed to ensure path ends in backslash: %ls", wzDirectory); + } + else + { + hr = StrAlloc(&sczTempPath, cchTempPath); + PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); + + if (!::GetTempPathW(cchTempPath, sczTempPath)) + { + PathExitWithLastError(hr, "Failed to get temp path."); + } + } + + for (DWORD i = 1; i <= dwUniqueCount; ++i) + { + hr = StrAllocFormatted(&scz, wzDirectoryNameTemplate, i); + PathExitOnFailure(hr, "Failed to allocate memory for directory name template."); + + hr = StrAllocFormatted(psczTempDirectory, L"%s%s", sczTempPath, scz); + PathExitOnFailure(hr, "Failed to allocate temp directory name."); + + if (!::CreateDirectoryW(*psczTempDirectory, NULL)) + { + DWORD er = ::GetLastError(); + if (ERROR_ALREADY_EXISTS == er) + { + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); + continue; + } + else if (ERROR_PATH_NOT_FOUND == er) + { + hr = DirEnsureExists(*psczTempDirectory, NULL); + break; + } + else + { + hr = HRESULT_FROM_WIN32(er); + break; + } + } + else + { + hr = S_OK; + break; + } + } + PathExitOnFailure(hr, "Failed to create temp directory."); + + hr = PathBackslashTerminate(psczTempDirectory); + PathExitOnFailure(hr, "Failed to ensure temp directory is backslash terminated."); + +LExit: + ReleaseStr(scz); + ReleaseStr(sczTempPath); + + return hr; +} + + +DAPI_(HRESULT) PathGetKnownFolder( + __in int csidl, + __out LPWSTR* psczKnownFolder + ) +{ + HRESULT hr = S_OK; + + hr = StrAlloc(psczKnownFolder, MAX_PATH); + PathExitOnFailure(hr, "Failed to allocate memory for known folder."); + + hr = ::SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, *psczKnownFolder); + PathExitOnFailure(hr, "Failed to get known folder path."); + + hr = PathBackslashTerminate(psczKnownFolder); + PathExitOnFailure(hr, "Failed to ensure known folder path is backslash terminated."); + +LExit: + return hr; +} + + +DAPI_(BOOL) PathIsAbsolute( + __in_z LPCWSTR wzPath + ) +{ + return wzPath && wzPath[0] && wzPath[1] && (wzPath[0] == L'\\') || (wzPath[1] == L':'); +} + + +DAPI_(HRESULT) PathConcat( + __in_opt LPCWSTR wzPath1, + __in_opt LPCWSTR wzPath2, + __deref_out_z LPWSTR* psczCombined + ) +{ + return PathConcatCch(wzPath1, 0, wzPath2, 0, psczCombined); +} + + +DAPI_(HRESULT) PathConcatCch( + __in_opt LPCWSTR wzPath1, + __in SIZE_T cchPath1, + __in_opt LPCWSTR wzPath2, + __in SIZE_T cchPath2, + __deref_out_z LPWSTR* psczCombined + ) +{ + HRESULT hr = S_OK; + + if (!wzPath2 || !*wzPath2) + { + hr = StrAllocString(psczCombined, wzPath1, cchPath1); + PathExitOnFailure(hr, "Failed to copy just path1 to output."); + } + else if (!wzPath1 || !*wzPath1 || PathIsAbsolute(wzPath2)) + { + hr = StrAllocString(psczCombined, wzPath2, cchPath2); + PathExitOnFailure(hr, "Failed to copy just path2 to output."); + } + else + { + hr = StrAllocString(psczCombined, wzPath1, cchPath1); + PathExitOnFailure(hr, "Failed to copy path1 to output."); + + hr = PathBackslashTerminate(psczCombined); + PathExitOnFailure(hr, "Failed to backslashify."); + + hr = StrAllocConcat(psczCombined, wzPath2, cchPath2); + PathExitOnFailure(hr, "Failed to append path2 to output."); + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) PathEnsureQuoted( + __inout LPWSTR* ppszPath, + __in BOOL fDirectory + ) +{ + Assert(ppszPath && *ppszPath); + + HRESULT hr = S_OK; + size_t cchPath = 0; + + hr = ::StringCchLengthW(*ppszPath, STRSAFE_MAX_CCH, &cchPath); + PathExitOnFailure(hr, "Failed to get the length of the path."); + + // Handle simple special cases. + if (0 == cchPath || (1 == cchPath && L'"' == (*ppszPath)[0])) + { + hr = StrAllocString(ppszPath, L"\"\"", 2); + PathExitOnFailure(hr, "Failed to allocate a quoted empty string."); + + ExitFunction(); + } + + if (L'"' != (*ppszPath)[0]) + { + hr = StrAllocPrefix(ppszPath, L"\"", 1); + PathExitOnFailure(hr, "Failed to allocate an opening quote."); + + // Add a char for the opening quote. + ++cchPath; + } + + if (L'"' != (*ppszPath)[cchPath - 1]) + { + hr = StrAllocConcat(ppszPath, L"\"", 1); + PathExitOnFailure(hr, "Failed to allocate a closing quote."); + + // Add a char for the closing quote. + ++cchPath; + } + + if (fDirectory) + { + if (L'\\' != (*ppszPath)[cchPath - 2]) + { + // Change the last char to a backslash and re-append the closing quote. + (*ppszPath)[cchPath - 1] = L'\\'; + + hr = StrAllocConcat(ppszPath, L"\"", 1); + PathExitOnFailure(hr, "Failed to allocate another closing quote after the backslash."); + } + } + +LExit: + + return hr; +} + + +DAPI_(HRESULT) PathCompare( + __in_z LPCWSTR wzPath1, + __in_z LPCWSTR wzPath2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath1 = NULL; + LPWSTR sczPath2 = NULL; + + hr = PathExpand(&sczPath1, wzPath1, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + PathExitOnFailure(hr, "Failed to expand path1."); + + hr = PathExpand(&sczPath2, wzPath2, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + PathExitOnFailure(hr, "Failed to expand path2."); + + *pnResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczPath1, -1, sczPath2, -1); + +LExit: + ReleaseStr(sczPath2); + ReleaseStr(sczPath1); + + return hr; +} + + +DAPI_(HRESULT) PathCompress( + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + HANDLE hPath = INVALID_HANDLE_VALUE; + + hPath = ::CreateFileW(wzPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (INVALID_HANDLE_VALUE == hPath) + { + PathExitWithLastError(hr, "Failed to open path %ls for compression.", wzPath); + } + + DWORD dwBytesReturned = 0; + USHORT usCompressionFormat = COMPRESSION_FORMAT_DEFAULT; + if (0 == ::DeviceIoControl(hPath, FSCTL_SET_COMPRESSION, &usCompressionFormat, sizeof(usCompressionFormat), NULL, 0, &dwBytesReturned, NULL)) + { + // ignore compression attempts on file systems that don't support it + DWORD er = ::GetLastError(); + if (ERROR_INVALID_FUNCTION != er) + { + PathExitOnWin32Error(er, hr, "Failed to set compression state for path %ls.", wzPath); + } + } + +LExit: + ReleaseFile(hPath); + + return hr; +} + +DAPI_(HRESULT) PathGetHierarchyArray( + __in_z LPCWSTR wzPath, + __deref_inout_ecount_opt(*pcPathArray) LPWSTR **prgsczPathArray, + __inout LPUINT pcPathArray + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPathCopy = NULL; + LPWSTR sczNewPathCopy = NULL; + DWORD cArraySpacesNeeded = 0; + size_t cchPath = 0; + + hr = ::StringCchLengthW(wzPath, STRSAFE_MAX_LENGTH, &cchPath); + PathExitOnRootFailure(hr, "Failed to get string length of path: %ls", wzPath); + + if (!cchPath) + { + ExitFunction1(hr = E_INVALIDARG); + } + + for (size_t i = 0; i < cchPath; ++i) + { + if (wzPath[i] == L'\\') + { + ++cArraySpacesNeeded; + } + } + + if (wzPath[cchPath - 1] != L'\\') + { + ++cArraySpacesNeeded; + } + + // If it's a UNC path, cut off the first three paths, 2 because it starts with a double backslash, and another because the first ("\\servername\") isn't a path. + if (wzPath[0] == L'\\' && wzPath[1] == L'\\') + { + cArraySpacesNeeded -= 3; + } + + Assert(cArraySpacesNeeded >= 1); + + hr = MemEnsureArraySize(reinterpret_cast(prgsczPathArray), cArraySpacesNeeded, sizeof(LPWSTR), 0); + PathExitOnFailure(hr, "Failed to allocate array of size %u for parent directories", cArraySpacesNeeded); + *pcPathArray = cArraySpacesNeeded; + + hr = StrAllocString(&sczPathCopy, wzPath, 0); + PathExitOnFailure(hr, "Failed to allocate copy of original path"); + + for (DWORD i = 0; i < cArraySpacesNeeded; ++i) + { + hr = StrAllocString((*prgsczPathArray) + cArraySpacesNeeded - 1 - i, sczPathCopy, 0); + PathExitOnFailure(hr, "Failed to copy path"); + + DWORD cchPathCopy = lstrlenW(sczPathCopy); + + // If it ends in a backslash, it's a directory path, so cut off everything the last backslash before we get the directory portion of the path + if (wzPath[cchPathCopy - 1] == L'\\') + { + sczPathCopy[cchPathCopy - 1] = L'\0'; + } + + hr = PathGetDirectory(sczPathCopy, &sczNewPathCopy); + PathExitOnFailure(hr, "Failed to get directory portion of path"); + + ReleaseStr(sczPathCopy); + sczPathCopy = sczNewPathCopy; + sczNewPathCopy = NULL; + } + + hr = S_OK; + +LExit: + ReleaseStr(sczPathCopy); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/perfutil.cpp b/src/libs/dutil/WixToolset.DUtil/perfutil.cpp new file mode 100644 index 00000000..bc138d34 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/perfutil.cpp @@ -0,0 +1,82 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define PerfExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__) +#define PerfExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PERFUTIL, p, x, e, s, __VA_ARGS__) +#define PerfExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, p, x, s, __VA_ARGS__) +#define PerfExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PERFUTIL, p, x, e, s, __VA_ARGS__) +#define PerfExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, p, x, s, __VA_ARGS__) +#define PerfExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PERFUTIL, e, x, s, __VA_ARGS__) +#define PerfExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PERFUTIL, g, x, s, __VA_ARGS__) + +static BOOL vfHighPerformanceCounter = TRUE; // assume the system has a high performance counter +static double vdFrequency = 1; + + +/******************************************************************** + PerfInitialize - initializes internal static variables + +********************************************************************/ +extern "C" void DAPI PerfInitialize( + ) +{ + LARGE_INTEGER liFrequency = { }; + + // + // check for high perf counter + // + if (!::QueryPerformanceFrequency(&liFrequency)) + { + vfHighPerformanceCounter = FALSE; + vdFrequency = 1000; // ticks are measured in milliseconds + } + else + vdFrequency = static_cast(liFrequency.QuadPart); +} + + +/******************************************************************** + PerfClickTime - resets the clicker, or returns elapsed time since last call + + NOTE: if pliElapsed is NULL, resets the elapsed time + if pliElapsed is not NULL, returns perf number since last call to PerfClickTime() +********************************************************************/ +extern "C" void DAPI PerfClickTime( + __out_opt LARGE_INTEGER* pliElapsed + ) +{ + static LARGE_INTEGER liStart = { }; + LARGE_INTEGER* pli = pliElapsed; + + if (!pli) // if elapsed time time was not requested, reset the start time + pli = &liStart; + + if (vfHighPerformanceCounter) + ::QueryPerformanceCounter(pli); + else + pli->QuadPart = ::GetTickCount(); + + if (pliElapsed) + pliElapsed->QuadPart -= liStart.QuadPart; +} + + +/******************************************************************** + PerfConvertToSeconds - converts perf number to seconds + +********************************************************************/ +extern "C" double DAPI PerfConvertToSeconds( + __in const LARGE_INTEGER* pli + ) +{ + Assert(0 < vdFrequency); + return pli->QuadPart / vdFrequency; +} diff --git a/src/libs/dutil/WixToolset.DUtil/polcutil.cpp b/src/libs/dutil/WixToolset.DUtil/polcutil.cpp new file mode 100644 index 00000000..1fdfa18c --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/polcutil.cpp @@ -0,0 +1,126 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define PolcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__) +#define PolcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_POLCUTIL, p, x, e, s, __VA_ARGS__) +#define PolcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, p, x, s, __VA_ARGS__) +#define PolcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_POLCUTIL, p, x, e, s, __VA_ARGS__) +#define PolcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, p, x, s, __VA_ARGS__) +#define PolcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_POLCUTIL, e, x, s, __VA_ARGS__) +#define PolcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_POLCUTIL, g, x, s, __VA_ARGS__) + +const LPCWSTR REGISTRY_POLICIES_KEY = L"SOFTWARE\\Policies\\"; + +static HRESULT OpenPolicyKey( + __in_z LPCWSTR wzPolicyPath, + __out HKEY* phk + ); + + +extern "C" HRESULT DAPI PolcReadNumber( + __in_z LPCWSTR wzPolicyPath, + __in_z LPCWSTR wzPolicyName, + __in DWORD dwDefault, + __out DWORD* pdw + ) +{ + HRESULT hr = S_OK; + HKEY hk = NULL; + + hr = OpenPolicyKey(wzPolicyPath, &hk); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + ExitFunction1(hr = S_FALSE); + } + PolcExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); + + hr = RegReadNumber(hk, wzPolicyName, pdw); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + ExitFunction1(hr = S_FALSE); + } + PolcExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); + +LExit: + ReleaseRegKey(hk); + + if (S_FALSE == hr || FAILED(hr)) + { + *pdw = dwDefault; + } + + return hr; +} + +extern "C" HRESULT DAPI PolcReadString( + __in_z LPCWSTR wzPolicyPath, + __in_z LPCWSTR wzPolicyName, + __in_z_opt LPCWSTR wzDefault, + __deref_out_z LPWSTR* pscz + ) +{ + HRESULT hr = S_OK; + HKEY hk = NULL; + + hr = OpenPolicyKey(wzPolicyPath, &hk); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + ExitFunction1(hr = S_FALSE); + } + PolcExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath); + + hr = RegReadString(hk, wzPolicyName, pscz); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + ExitFunction1(hr = S_FALSE); + } + PolcExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName); + +LExit: + ReleaseRegKey(hk); + + if (S_FALSE == hr || FAILED(hr)) + { + if (NULL == wzDefault) + { + ReleaseNullStr(*pscz); + } + else + { + hr = StrAllocString(pscz, wzDefault, 0); + } + } + + return hr; +} + + +// internal functions + +static HRESULT OpenPolicyKey( + __in_z LPCWSTR wzPolicyPath, + __out HKEY* phk + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + + hr = PathConcat(REGISTRY_POLICIES_KEY, wzPolicyPath, &sczPath); + PolcExitOnFailure(hr, "Failed to combine logging path with root path."); + + hr = RegOpen(HKEY_LOCAL_MACHINE, sczPath, KEY_READ, phk); + PolcExitOnFailure(hr, "Failed to open policy registry key."); + +LExit: + ReleaseStr(sczPath); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/precomp.h b/src/libs/dutil/WixToolset.DUtil/precomp.h new file mode 100644 index 00000000..f8f3b944 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/precomp.h @@ -0,0 +1,98 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif + +#ifndef _WIN32_MSI +#define _WIN32_MSI 200 +#endif + +#define JET_VERSION 0x0501 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dutilsources.h" +#include "dutil.h" +#include "verutil.h" +#include "aclutil.h" +#include "atomutil.h" +#include "buffutil.h" +#include "butil.h" +#include "cabcutil.h" +#include "cabutil.h" +#include "conutil.h" +#include "cryputil.h" +#include "eseutil.h" +#include "dirutil.h" +#include "dlutil.h" +#include "dpiutil.h" +#include "fileutil.h" +#include "guidutil.h" +#include "gdiputil.h" +#include "dictutil.h" +#include "deputil.h" // NOTE: This must come after dictutil.h since it uses it. +#include "inetutil.h" +#include "iniutil.h" +#include "jsonutil.h" +#include "locutil.h" +#include "logutil.h" +#include "memutil.h" // NOTE: almost everying is inlined so there is a small .cpp file +//#include "metautil.h" - see metautil.cpp why this *must* be commented out +#include "monutil.h" +#include "osutil.h" +#include "pathutil.h" +#include "perfutil.h" +#include "polcutil.h" +#include "procutil.h" +#include "regutil.h" +#include "resrutil.h" +#include "reswutil.h" +#include "rmutil.h" +#include "rssutil.h" +#include "apuputil.h" // NOTE: this must come after atomutil.h and rssutil.h since it uses them. +#include "shelutil.h" +//#include "sqlutil.h" - see sqlutil.cpp why this *must* be commented out +#include "srputil.h" +#include "strutil.h" +#include "timeutil.h" +#include "timeutil.h" +#include "thmutil.h" +#include "uncutil.h" +#include "uriutil.h" +#include "userutil.h" +#include "wiutil.h" +#include "wuautil.h" +#include // This header is needed for msxml2.h to compile correctly +#include // This file is needed to include xmlutil.h +#include "xmlutil.h" + diff --git a/src/libs/dutil/WixToolset.DUtil/proc2utl.cpp b/src/libs/dutil/WixToolset.DUtil/proc2utl.cpp new file mode 100644 index 00000000..a59d2ffc --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/proc2utl.cpp @@ -0,0 +1,83 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) +#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) + +/******************************************************************** + ProcFindAllIdsFromExeName() - returns an array of process ids that are running specified executable. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcFindAllIdsFromExeName( + __in_z LPCWSTR wzExeName, + __out DWORD** ppdwProcessIds, + __out DWORD* pcProcessIds + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HANDLE hSnap = INVALID_HANDLE_VALUE; + BOOL fContinue = FALSE; + PROCESSENTRY32W peData = { sizeof(peData) }; + + hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (INVALID_HANDLE_VALUE == hSnap) + { + ProcExitWithLastError(hr, "Failed to create snapshot of processes on system"); + } + + fContinue = ::Process32FirstW(hSnap, &peData); + + while (fContinue) + { + if (0 == lstrcmpiW((LPCWSTR)&(peData.szExeFile), wzExeName)) + { + if (!*ppdwProcessIds) + { + *ppdwProcessIds = static_cast(MemAlloc(sizeof(DWORD), TRUE)); + ProcExitOnNull(ppdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for returned process IDs."); + } + else + { + DWORD* pdwReAllocReturnedPids = NULL; + pdwReAllocReturnedPids = static_cast(MemReAlloc(*ppdwProcessIds, sizeof(DWORD) * ((*pcProcessIds) + 1), TRUE)); + ProcExitOnNull(pdwReAllocReturnedPids, hr, E_OUTOFMEMORY, "Failed to re-allocate array for returned process IDs."); + + *ppdwProcessIds = pdwReAllocReturnedPids; + } + + (*ppdwProcessIds)[*pcProcessIds] = peData.th32ProcessID; + ++(*pcProcessIds); + } + + fContinue = ::Process32NextW(hSnap, &peData); + } + + er = ::GetLastError(); + if (ERROR_NO_MORE_FILES == er) + { + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + +LExit: + ReleaseFile(hSnap); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/proc3utl.cpp b/src/libs/dutil/WixToolset.DUtil/proc3utl.cpp new file mode 100644 index 00000000..6d3cbc67 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/proc3utl.cpp @@ -0,0 +1,129 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) +#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) + +static HRESULT GetActiveSessionUserToken( + __out HANDLE *phToken + ); + + +/******************************************************************** + ProcExecuteAsInteractiveUser() - runs process as currently logged in + user. +*******************************************************************/ +extern "C" HRESULT DAPI ProcExecuteAsInteractiveUser( + __in_z LPCWSTR wzExecutablePath, + __in_z LPCWSTR wzCommandLine, + __out HANDLE *phProcess + ) +{ + HRESULT hr = S_OK; + HANDLE hToken = NULL; + LPVOID pEnvironment = NULL; + LPWSTR sczFullCommandLine = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + + hr = GetActiveSessionUserToken(&hToken); + ProcExitOnFailure(hr, "Failed to get active session user token."); + + if (!::CreateEnvironmentBlock(&pEnvironment, hToken, FALSE)) + { + ProcExitWithLastError(hr, "Failed to create environment block for UI process."); + } + + hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine); + ProcExitOnFailure(hr, "Failed to allocate full command-line."); + + si.cb = sizeof(si); + if (!::CreateProcessAsUserW(hToken, wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, pEnvironment, NULL, &si, &pi)) + { + ProcExitWithLastError(hr, "Failed to create UI process: %ls", sczFullCommandLine); + } + + *phProcess = pi.hProcess; + pi.hProcess = NULL; + +LExit: + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + ReleaseStr(sczFullCommandLine); + + if (pEnvironment) + { + ::DestroyEnvironmentBlock(pEnvironment); + } + + ReleaseHandle(hToken); + + return hr; +} + + +static HRESULT GetActiveSessionUserToken( + __out HANDLE *phToken + ) +{ + HRESULT hr = S_OK; + PWTS_SESSION_INFO pSessionInfo = NULL; + DWORD cSessions = 0; + DWORD dwSessionId = 0; + BOOL fSessionFound = FALSE; + HANDLE hToken = NULL; + + // Loop through the sessions looking for the active one. + if (!::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &cSessions)) + { + ProcExitWithLastError(hr, "Failed to enumerate sessions."); + } + + for (DWORD i = 0; i < cSessions; ++i) + { + if (WTSActive == pSessionInfo[i].State) + { + dwSessionId = pSessionInfo[i].SessionId; + fSessionFound = TRUE; + + break; + } + } + + if (!fSessionFound) + { + ExitFunction1(hr = E_NOTFOUND); + } + + // Get the user token from the active session. + if (!::WTSQueryUserToken(dwSessionId, &hToken)) + { + ProcExitWithLastError(hr, "Failed to get active session user token."); + } + + *phToken = hToken; + hToken = NULL; + +LExit: + ReleaseHandle(hToken); + + if (pSessionInfo) + { + ::WTSFreeMemory(pSessionInfo); + } + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/procutil.cpp b/src/libs/dutil/WixToolset.DUtil/procutil.cpp new file mode 100644 index 00000000..6bfe5017 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/procutil.cpp @@ -0,0 +1,522 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__) +#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__) +#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) +#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) +#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) + + +// private functions +static HRESULT CreatePipes( + __out HANDLE *phOutRead, + __out HANDLE *phOutWrite, + __out HANDLE *phErrWrite, + __out HANDLE *phInRead, + __out HANDLE *phInWrite + ); + +static BOOL CALLBACK CloseWindowEnumCallback( + __in HWND hWnd, + __in LPARAM lParam + ); + + +extern "C" HRESULT DAPI ProcElevated( + __in HANDLE hProcess, + __out BOOL* pfElevated + ) +{ + HRESULT hr = S_OK; + HANDLE hToken = NULL; + TOKEN_ELEVATION tokenElevated = { }; + DWORD cbToken = 0; + + if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) + { + ProcExitWithLastError(hr, "Failed to open process token."); + } + + if (::GetTokenInformation(hToken, TokenElevation, &tokenElevated, sizeof(TOKEN_ELEVATION), &cbToken)) + { + *pfElevated = (0 != tokenElevated.TokenIsElevated); + } + else + { + DWORD er = ::GetLastError(); + hr = HRESULT_FROM_WIN32(er); + + // If it's invalid argument, this means the OS doesn't support TokenElevation, so we're not elevated. + if (E_INVALIDARG == hr) + { + *pfElevated = FALSE; + hr = S_OK; + } + else + { + ProcExitOnRootFailure(hr, "Failed to get elevation token from process."); + } + } + +LExit: + ReleaseHandle(hToken); + + return hr; +} + +extern "C" HRESULT DAPI ProcWow64( + __in HANDLE hProcess, + __out BOOL* pfWow64 + ) +{ + HRESULT hr = S_OK; + BOOL fIsWow64 = FALSE; + + typedef BOOL(WINAPI* LPFN_ISWOW64PROCESS2)(HANDLE, USHORT *, USHORT *); + LPFN_ISWOW64PROCESS2 pfnIsWow64Process2 = (LPFN_ISWOW64PROCESS2)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process2"); + + if (pfnIsWow64Process2) + { + USHORT pProcessMachine = IMAGE_FILE_MACHINE_UNKNOWN; + if (!pfnIsWow64Process2(hProcess, &pProcessMachine, nullptr)) + { + ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process2."); + } + + if (pProcessMachine != IMAGE_FILE_MACHINE_UNKNOWN) + { + fIsWow64 = TRUE; + } + } + else + { + typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); + LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process"); + + if (pfnIsWow64Process) + { + if (!pfnIsWow64Process(hProcess, &fIsWow64)) + { + ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process."); + } + } + } + + *pfWow64 = fIsWow64; + +LExit: + return hr; +} + +extern "C" HRESULT DAPI ProcDisableWowFileSystemRedirection( + __in PROC_FILESYSTEMREDIRECTION* pfsr + ) +{ + AssertSz(!pfsr->fDisabled, "File system redirection was already disabled."); + HRESULT hr = S_OK; + + typedef BOOL (WINAPI *LPFN_Wow64DisableWow64FsRedirection)(PVOID *); + LPFN_Wow64DisableWow64FsRedirection pfnWow64DisableWow64FsRedirection = (LPFN_Wow64DisableWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64DisableWow64FsRedirection"); + + if (!pfnWow64DisableWow64FsRedirection) + { + ExitFunction1(hr = E_NOTIMPL); + } + + if (!pfnWow64DisableWow64FsRedirection(&pfsr->pvRevertState)) + { + ProcExitWithLastError(hr, "Failed to disable file system redirection."); + } + + pfsr->fDisabled = TRUE; + +LExit: + return hr; +} + +extern "C" HRESULT DAPI ProcRevertWowFileSystemRedirection( + __in PROC_FILESYSTEMREDIRECTION* pfsr + ) +{ + HRESULT hr = S_OK; + + if (pfsr->fDisabled) + { + typedef BOOL (WINAPI *LPFN_Wow64RevertWow64FsRedirection)(PVOID); + LPFN_Wow64RevertWow64FsRedirection pfnWow64RevertWow64FsRedirection = (LPFN_Wow64RevertWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64RevertWow64FsRedirection"); + + if (!pfnWow64RevertWow64FsRedirection(pfsr->pvRevertState)) + { + ProcExitWithLastError(hr, "Failed to revert file system redirection."); + } + + pfsr->fDisabled = FALSE; + pfsr->pvRevertState = NULL; + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI ProcExec( + __in_z LPCWSTR wzExecutablePath, + __in_z_opt LPCWSTR wzCommandLine, + __in int nCmdShow, + __out HANDLE *phProcess + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFullCommandLine = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + + hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine ? wzCommandLine : L""); + ProcExitOnFailure(hr, "Failed to allocate full command-line."); + + si.cb = sizeof(si); + si.wShowWindow = static_cast(nCmdShow); + if (!::CreateProcessW(wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi)) + { + ProcExitWithLastError(hr, "Failed to create process: %ls", sczFullCommandLine); + } + + *phProcess = pi.hProcess; + pi.hProcess = NULL; + +LExit: + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + ReleaseStr(sczFullCommandLine); + + return hr; +} + + +/******************************************************************** + ProcExecute() - executes a command-line. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcExecute( + __in_z LPWSTR wzCommand, + __out HANDLE *phProcess, + __out_opt HANDLE *phChildStdIn, + __out_opt HANDLE *phChildStdOutErr + ) +{ + HRESULT hr = S_OK; + + PROCESS_INFORMATION pi = { }; + STARTUPINFOW si = { }; + + HANDLE hOutRead = INVALID_HANDLE_VALUE; + HANDLE hOutWrite = INVALID_HANDLE_VALUE; + HANDLE hErrWrite = INVALID_HANDLE_VALUE; + HANDLE hInRead = INVALID_HANDLE_VALUE; + HANDLE hInWrite = INVALID_HANDLE_VALUE; + + // Create redirect pipes. + hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite); + ProcExitOnFailure(hr, "failed to create output pipes"); + + // Set up startup structure. + si.cb = sizeof(STARTUPINFOW); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = hInRead; + si.hStdOutput = hOutWrite; + si.hStdError = hErrWrite; + +#pragma prefast(push) +#pragma prefast(disable:25028) + if (::CreateProcessW(NULL, + wzCommand, // command line + NULL, // security info + NULL, // thread info + TRUE, // inherit handles + ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags + NULL, // environment + NULL, // cur dir + &si, + &pi)) +#pragma prefast(pop) + { + // Close child process output/input handles so child doesn't hang + // while waiting for input from parent process. + ::CloseHandle(hOutWrite); + hOutWrite = INVALID_HANDLE_VALUE; + + ::CloseHandle(hErrWrite); + hErrWrite = INVALID_HANDLE_VALUE; + + ::CloseHandle(hInRead); + hInRead = INVALID_HANDLE_VALUE; + } + else + { + ProcExitWithLastError(hr, "Process failed to execute."); + } + + *phProcess = pi.hProcess; + pi.hProcess = 0; + + if (phChildStdIn) + { + *phChildStdIn = hInWrite; + hInWrite = INVALID_HANDLE_VALUE; + } + + if (phChildStdOutErr) + { + *phChildStdOutErr = hOutRead; + hOutRead = INVALID_HANDLE_VALUE; + } + +LExit: + if (pi.hThread) + { + ::CloseHandle(pi.hThread); + } + + if (pi.hProcess) + { + ::CloseHandle(pi.hProcess); + } + + ReleaseFileHandle(hOutRead); + ReleaseFileHandle(hOutWrite); + ReleaseFileHandle(hErrWrite); + ReleaseFileHandle(hInRead); + ReleaseFileHandle(hInWrite); + + return hr; +} + + +/******************************************************************** + ProcWaitForCompletion() - waits for process to complete and gets return code. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcWaitForCompletion( + __in HANDLE hProcess, + __in DWORD dwTimeout, + __out DWORD *pReturnCode + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + // Wait for everything to finish + er = ::WaitForSingleObject(hProcess, dwTimeout); + if (WAIT_FAILED == er) + { + ProcExitWithLastError(hr, "Failed to wait for process to complete."); + } + else if (WAIT_TIMEOUT == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + + if (!::GetExitCodeProcess(hProcess, &er)) + { + ProcExitWithLastError(hr, "Failed to get process return code."); + } + + *pReturnCode = er; + +LExit: + return hr; +} + +/******************************************************************** + ProcWaitForIds() - waits for multiple processes to complete. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcWaitForIds( + __in_ecount(cProcessIds) const DWORD rgdwProcessIds[], + __in DWORD cProcessIds, + __in DWORD dwMilliseconds + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HANDLE hProcess = NULL; + HANDLE * rghProcesses = NULL; + DWORD cProcesses = 0; + + rghProcesses = static_cast(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE)); + ProcExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles."); + + for (DWORD i = 0; i < cProcessIds; ++i) + { + hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, rgdwProcessIds[i]); + if (hProcess != NULL) + { + rghProcesses[cProcesses++] = hProcess; + } + } + + er = ::WaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds); + if (WAIT_FAILED == er) + { + ProcExitWithLastError(hr, "Failed to wait for process to complete."); + } + else if (WAIT_TIMEOUT == er) + { + ProcExitOnWin32Error(er, hr, "Timed out while waiting for process to complete."); + } + +LExit: + if (rghProcesses) + { + for (DWORD i = 0; i < cProcesses; ++i) + { + if (NULL != rghProcesses[i]) + { + ::CloseHandle(rghProcesses[i]); + } + } + + MemFree(rghProcesses); + } + + return hr; +} + +/******************************************************************** + ProcCloseIds() - sends WM_CLOSE messages to all process ids. + +*******************************************************************/ +extern "C" HRESULT DAPI ProcCloseIds( + __in_ecount(cProcessIds) const DWORD* pdwProcessIds, + __in DWORD cProcessIds + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cProcessIds; ++i) + { + if (!::EnumWindows(&CloseWindowEnumCallback, pdwProcessIds[i])) + { + ProcExitWithLastError(hr, "Failed to enumerate windows."); + } + } + +LExit: + return hr; +} + + +static HRESULT CreatePipes( + __out HANDLE *phOutRead, + __out HANDLE *phOutWrite, + __out HANDLE *phErrWrite, + __out HANDLE *phInRead, + __out HANDLE *phInWrite + ) +{ + HRESULT hr = S_OK; + SECURITY_ATTRIBUTES sa; + HANDLE hOutTemp = INVALID_HANDLE_VALUE; + HANDLE hInTemp = INVALID_HANDLE_VALUE; + + HANDLE hOutRead = INVALID_HANDLE_VALUE; + HANDLE hOutWrite = INVALID_HANDLE_VALUE; + HANDLE hErrWrite = INVALID_HANDLE_VALUE; + HANDLE hInRead = INVALID_HANDLE_VALUE; + HANDLE hInWrite = INVALID_HANDLE_VALUE; + + // Fill out security structure so we can inherit handles + ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + // Create pipes + if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0)) + { + ProcExitWithLastError(hr, "failed to create output pipe"); + } + + if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0)) + { + ProcExitWithLastError(hr, "failed to create input pipe"); + } + + // Duplicate output pipe so standard error and standard output write to the same pipe. + if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) + { + ProcExitWithLastError(hr, "failed to duplicate write handle"); + } + + // We need to create new "output read" and "input write" handles that are non inheritable. Otherwise CreateProcess will creates handles in + // the child process that can't be closed. + if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + ProcExitWithLastError(hr, "failed to duplicate output pipe"); + } + + if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + ProcExitWithLastError(hr, "failed to duplicate input pipe"); + } + + // now that everything has succeeded, assign to the outputs + *phOutRead = hOutRead; + hOutRead = INVALID_HANDLE_VALUE; + + *phOutWrite = hOutWrite; + hOutWrite = INVALID_HANDLE_VALUE; + + *phErrWrite = hErrWrite; + hErrWrite = INVALID_HANDLE_VALUE; + + *phInRead = hInRead; + hInRead = INVALID_HANDLE_VALUE; + + *phInWrite = hInWrite; + hInWrite = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hOutRead); + ReleaseFileHandle(hOutWrite); + ReleaseFileHandle(hErrWrite); + ReleaseFileHandle(hInRead); + ReleaseFileHandle(hInWrite); + ReleaseFileHandle(hOutTemp); + ReleaseFileHandle(hInTemp); + + return hr; +} + + +/******************************************************************** + CloseWindowEnumCallback() - outputs trace and log info + +*******************************************************************/ +static BOOL CALLBACK CloseWindowEnumCallback( + __in HWND hWnd, + __in LPARAM lParam + ) +{ + DWORD dwPid = static_cast(lParam); + DWORD dwProcessId = 0; + + ::GetWindowThreadProcessId(hWnd, &dwProcessId); + if (dwPid == dwProcessId) + { + ::SendMessageW(hWnd, WM_CLOSE, 0, 0); + } + + return TRUE; +} diff --git a/src/libs/dutil/WixToolset.DUtil/regutil.cpp b/src/libs/dutil/WixToolset.DUtil/regutil.cpp new file mode 100644 index 00000000..cb617932 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/regutil.cpp @@ -0,0 +1,1035 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define RegExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) +#define RegExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__) +#define RegExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__) +#define RegExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__) +#define RegExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__) +#define RegExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REGUTIL, e, x, s, __VA_ARGS__) +#define RegExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REGUTIL, g, x, s, __VA_ARGS__) + +static PFN_REGCREATEKEYEXW vpfnRegCreateKeyExW = ::RegCreateKeyExW; +static PFN_REGOPENKEYEXW vpfnRegOpenKeyExW = ::RegOpenKeyExW; +static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExW = NULL; +static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExWFromLibrary = NULL; +static PFN_REGDELETEKEYW vpfnRegDeleteKeyW = ::RegDeleteKeyW; +static PFN_REGENUMKEYEXW vpfnRegEnumKeyExW = ::RegEnumKeyExW; +static PFN_REGENUMVALUEW vpfnRegEnumValueW = ::RegEnumValueW; +static PFN_REGQUERYINFOKEYW vpfnRegQueryInfoKeyW = ::RegQueryInfoKeyW; +static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW; +static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW; +static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW; + +static HMODULE vhAdvApi32Dll = NULL; +static BOOL vfRegInitialized = FALSE; + +static HRESULT WriteStringToRegistry( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue, + __in DWORD dwType +); + +/******************************************************************** + RegInitialize - initializes regutil + +*********************************************************************/ +extern "C" HRESULT DAPI RegInitialize( + ) +{ + HRESULT hr = S_OK; + + hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); + RegExitOnFailure(hr, "Failed to load AdvApi32.dll"); + + // ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW + vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW")); + + if (NULL == vpfnRegDeleteKeyExW) + { + vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary; + } + + vfRegInitialized = TRUE; + +LExit: + return hr; +} + + +/******************************************************************** + RegUninitialize - uninitializes regutil + +*********************************************************************/ +extern "C" void DAPI RegUninitialize( + ) +{ + if (vhAdvApi32Dll) + { + ::FreeLibrary(vhAdvApi32Dll); + vhAdvApi32Dll = NULL; + vpfnRegDeleteKeyExWFromLibrary = NULL; + vpfnRegDeleteKeyExW = NULL; + } + + vfRegInitialized = FALSE; +} + + +/******************************************************************** + RegFunctionOverride - overrides the registry functions. Typically used + for unit testing. + +*********************************************************************/ +extern "C" void DAPI RegFunctionOverride( + __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW, + __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW, + __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW, + __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW, + __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW, + __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, + __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, + __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, + __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW + ) +{ + vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW; + vpfnRegOpenKeyExW = pfnRegOpenKeyExW ? pfnRegOpenKeyExW : ::RegOpenKeyExW; + vpfnRegDeleteKeyExW = pfnRegDeleteKeyExW ? pfnRegDeleteKeyExW : vpfnRegDeleteKeyExWFromLibrary; + vpfnRegEnumKeyExW = pfnRegEnumKeyExW ? pfnRegEnumKeyExW : ::RegEnumKeyExW; + vpfnRegEnumValueW = pfnRegEnumValueW ? pfnRegEnumValueW : ::RegEnumValueW; + vpfnRegQueryInfoKeyW = pfnRegQueryInfoKeyW ? pfnRegQueryInfoKeyW : ::RegQueryInfoKeyW; + vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW; + vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW; + vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW; +} + + +/******************************************************************** + RegCreate - creates a registry key. + +*********************************************************************/ +extern "C" HRESULT DAPI RegCreate( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __out HKEY* phk + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, dwAccess, NULL, phk, NULL); + RegExitOnWin32Error(er, hr, "Failed to create registry key."); + +LExit: + return hr; +} + + +/******************************************************************** + RegCreate - creates a registry key with extra options. + +*********************************************************************/ +HRESULT DAPI RegCreateEx( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __in BOOL fVolatile, + __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes, + __out HKEY* phk, + __out_opt BOOL* pfCreated + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwDisposition; + + er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, fVolatile ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE, dwAccess, pSecurityAttributes, phk, &dwDisposition); + RegExitOnWin32Error(er, hr, "Failed to create registry key."); + + if (pfCreated) + { + *pfCreated = (REG_CREATED_NEW_KEY == dwDisposition); + } + +LExit: + return hr; +} + + +/******************************************************************** + RegOpen - opens a registry key. + +*********************************************************************/ +extern "C" HRESULT DAPI RegOpen( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in DWORD dwAccess, + __out HKEY* phk + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegOpenKeyExW(hkRoot, wzSubKey, 0, dwAccess, phk); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to open registry key."); + +LExit: + return hr; +} + + +/******************************************************************** + RegDelete - deletes a registry key (and optionally it's whole tree). + +*********************************************************************/ +extern "C" HRESULT DAPI RegDelete( + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in BOOL fDeleteTree + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR pszEnumeratedSubKey = NULL; + LPWSTR pszRecursiveSubKey = NULL; + HKEY hkKey = NULL; + REGSAM samDesired = 0; + + if (!vfRegInitialized && REG_KEY_DEFAULT != kbKeyBitness) + { + hr = E_INVALIDARG; + RegExitOnFailure(hr, "RegInitialize must be called first in order to RegDelete() a key with non-default bit attributes!"); + } + + switch (kbKeyBitness) + { + case REG_KEY_32BIT: + samDesired = KEY_WOW64_32KEY; + break; + case REG_KEY_64BIT: + samDesired = KEY_WOW64_64KEY; + break; + case REG_KEY_DEFAULT: + // Nothing to do + break; + } + + if (fDeleteTree) + { + hr = RegOpen(hkRoot, wzSubKey, KEY_READ | samDesired, &hkKey); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + RegExitOnFailure(hr, "Failed to open this key for enumerating subkeys: %ls", wzSubKey); + + // Yes, keep enumerating the 0th item, because we're deleting it every time + while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, 0, &pszEnumeratedSubKey))) + { + RegExitOnFailure(hr, "Failed to enumerate key 0"); + + hr = PathConcat(wzSubKey, pszEnumeratedSubKey, &pszRecursiveSubKey); + RegExitOnFailure(hr, "Failed to concatenate paths while recursively deleting subkeys. Path1: %ls, Path2: %ls", wzSubKey, pszEnumeratedSubKey); + + hr = RegDelete(hkRoot, pszRecursiveSubKey, kbKeyBitness, fDeleteTree); + RegExitOnFailure(hr, "Failed to recursively delete subkey: %ls", pszRecursiveSubKey); + } + + hr = S_OK; + } + + if (NULL != vpfnRegDeleteKeyExW) + { + er = vpfnRegDeleteKeyExW(hkRoot, wzSubKey, samDesired, 0); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to delete registry key (ex)."); + } + else + { + er = vpfnRegDeleteKeyW(hkRoot, wzSubKey); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to delete registry key."); + } + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(pszEnumeratedSubKey); + ReleaseStr(pszRecursiveSubKey); + + return hr; +} + + +/******************************************************************** + RegKeyEnum - enumerates child registry keys. + +*********************************************************************/ +extern "C" HRESULT DAPI RegKeyEnum( + __in HKEY hk, + __in DWORD dwIndex, + __deref_out_z LPWSTR* psczKey + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + SIZE_T cb = 0; + DWORD cch = 0; + + if (psczKey && *psczKey) + { + hr = StrMaxLength(*psczKey, &cb); + RegExitOnFailure(hr, "Failed to determine length of string."); + + cch = (DWORD)min(DWORD_MAX, cb); + } + + if (2 > cch) + { + cch = 2; + + hr = StrAlloc(psczKey, cch); + RegExitOnFailure(hr, "Failed to allocate string to minimum size."); + } + + er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); + if (ERROR_MORE_DATA == er) + { + er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, &cch, NULL, NULL, NULL, NULL, NULL, NULL); + RegExitOnWin32Error(er, hr, "Failed to get max size of subkey name under registry key."); + + ++cch; // add one because RegQueryInfoKeyW() returns the length of the subkeys without the null terminator. + hr = StrAlloc(psczKey, cch); + RegExitOnFailure(hr, "Failed to allocate string bigger for enum registry key."); + + er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); + } + else if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = E_NOMOREITEMS); + } + RegExitOnWin32Error(er, hr, "Failed to enum registry key."); + + // Always ensure the registry key name is null terminated. +#pragma prefast(push) +#pragma prefast(disable:26018) + (*psczKey)[cch] = L'\0'; // note that cch will always be one less than the size of the buffer because that's how RegEnumKeyExW() works. +#pragma prefast(pop) + +LExit: + return hr; +} + + +/******************************************************************** + RegValueEnum - enumerates registry values. + +*********************************************************************/ +HRESULT DAPI RegValueEnum( + __in HKEY hk, + __in DWORD dwIndex, + __deref_out_z LPWSTR* psczName, + __out_opt DWORD *pdwType + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD cbValueName = 0; + + er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cbValueName, NULL, NULL, NULL); + RegExitOnWin32Error(er, hr, "Failed to get max size of value name under registry key."); + + // Add one for null terminator + ++cbValueName; + + hr = StrAlloc(psczName, cbValueName); + RegExitOnFailure(hr, "Failed to allocate array for registry value name"); + + er = vpfnRegEnumValueW(hk, dwIndex, *psczName, &cbValueName, NULL, pdwType, NULL, NULL); + if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = E_NOMOREITEMS); + } + RegExitOnWin32Error(er, hr, "Failed to enumerate registry value"); + +LExit: + return hr; +} + +/******************************************************************** + RegGetType - reads a registry key value type. + *********************************************************************/ +HRESULT DAPI RegGetType( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD *pdwType + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegQueryValueExW(hk, wzName, NULL, pdwType, NULL, NULL); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to read registry value."); +LExit: + + return hr; +} + +/******************************************************************** + RegReadBinary - reads a registry key binary value. + NOTE: caller is responsible for freeing *ppbBuffer +*********************************************************************/ +HRESULT DAPI RegReadBinary( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, + __out SIZE_T *pcbBuffer + ) +{ + HRESULT hr = S_OK; + LPBYTE pbBuffer = NULL; + DWORD er = ERROR_SUCCESS; + DWORD cb = 0; + DWORD dwType = 0; + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, &cb); + RegExitOnWin32Error(er, hr, "Failed to get size of registry value."); + + // Zero-length binary values can exist + if (0 < cb) + { + pbBuffer = static_cast(MemAlloc(cb, FALSE)); + RegExitOnNull(pbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for binary registry value."); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, pbBuffer, &cb); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to read registry value."); + } + + if (REG_BINARY == dwType) + { + *ppbBuffer = pbBuffer; + pbBuffer = NULL; + *pcbBuffer = cb; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + RegExitOnRootFailure(hr, "Error reading binary registry value due to unexpected data type: %u", dwType); + } + +LExit: + ReleaseMem(pbBuffer); + + return hr; +} + + +/******************************************************************** + RegReadString - reads a registry key value as a string. + +*********************************************************************/ +extern "C" HRESULT DAPI RegReadString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + SIZE_T cbValue = 0; + DWORD cch = 0; + DWORD cb = 0; + DWORD dwType = 0; + LPWSTR sczExpand = NULL; + + if (psczValue && *psczValue) + { + hr = StrMaxLength(*psczValue, &cbValue); + RegExitOnFailure(hr, "Failed to determine length of string."); + + cch = (DWORD)min(DWORD_MAX, cbValue); + } + + if (2 > cch) + { + cch = 2; + + hr = StrAlloc(psczValue, cch); + RegExitOnFailure(hr, "Failed to allocate string to minimum size."); + } + + cb = sizeof(WCHAR) * (cch - 1); // subtract one to ensure there will be a space at the end of the string for the null terminator. + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*psczValue), &cb); + if (ERROR_MORE_DATA == er) + { + cch = cb / sizeof(WCHAR) + 1; // add one to ensure there will be space at the end for the null terminator + hr = StrAlloc(psczValue, cch); + RegExitOnFailure(hr, "Failed to allocate string bigger for registry value."); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*psczValue), &cb); + } + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to read registry key."); + + if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) + { + // Always ensure the registry value is null terminated. + (*psczValue)[cch - 1] = L'\0'; + + if (REG_EXPAND_SZ == dwType) + { + hr = StrAllocString(&sczExpand, *psczValue, 0); + RegExitOnFailure(hr, "Failed to copy registry value to expand."); + + hr = PathExpand(psczValue, sczExpand, PATH_EXPAND_ENVIRONMENT); + RegExitOnFailure(hr, "Failed to expand registry value: %ls", *psczValue); + } + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + RegExitOnRootFailure(hr, "Error reading string registry value due to unexpected data type: %u", dwType); + } + +LExit: + ReleaseStr(sczExpand); + + return hr; +} + + +/******************************************************************** + RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array. + +*********************************************************************/ +HRESULT DAPI RegReadStringArray( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings, + __out DWORD *pcStrings + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwNullCharacters = 0; + DWORD dwType = 0; + DWORD cb = 0; + DWORD cch = 0; + LPCWSTR wzSource = NULL; + LPWSTR sczValue = NULL; + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(sczValue), &cb); + if (0 < cb) + { + cch = cb / sizeof(WCHAR); + hr = StrAlloc(&sczValue, cch); + RegExitOnFailure(hr, "Failed to allocate string for registry value."); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(sczValue), &cb); + } + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to read registry key."); + + if (cb / sizeof(WCHAR) != cch) + { + hr = E_UNEXPECTED; + RegExitOnFailure(hr, "The size of registry value %ls unexpected changed between 2 reads", wzName); + } + + if (REG_MULTI_SZ != dwType) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + RegExitOnRootFailure(hr, "Tried to read string array, but registry value %ls is of an incorrect type", wzName); + } + + // Value exists, but is empty, so no strings to return. + if (2 > cch) + { + *prgsczStrings = NULL; + *pcStrings = 0; + ExitFunction1(hr = S_OK); + } + + // The docs specifically say if the value was written without double-null-termination, it'll get read back without it too. + if (L'\0' != sczValue[cch-1] || L'\0' != sczValue[cch-2]) + { + hr = E_INVALIDARG; + RegExitOnFailure(hr, "Tried to read string array, but registry value %ls is invalid (isn't double-null-terminated)", wzName); + } + + cch = cb / sizeof(WCHAR); + for (DWORD i = 0; i < cch; ++i) + { + if (L'\0' == sczValue[i]) + { + ++dwNullCharacters; + } + } + + // There's one string for every null character encountered (except the extra 1 at the end of the string) + *pcStrings = dwNullCharacters - 1; + hr = MemEnsureArraySize(reinterpret_cast(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0); + RegExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value"); + +#pragma prefast(push) +#pragma prefast(disable:26010) + wzSource = sczValue; + for (DWORD i = 0; i < *pcStrings; ++i) + { + hr = StrAllocString(&(*prgsczStrings)[i], wzSource, 0); + RegExitOnFailure(hr, "Failed to allocate copy of string"); + + // Skip past this string + wzSource += lstrlenW(wzSource) + 1; + } +#pragma prefast(pop) + +LExit: + ReleaseStr(sczValue); + + return hr; +} + + +/******************************************************************** + RegReadVersion - reads a registry key value as a version. + +*********************************************************************/ +extern "C" HRESULT DAPI RegReadVersion( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD64* pdw64Version + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwType = 0; + DWORD cb = 0; + LPWSTR sczVersion = NULL; + + cb = sizeof(DWORD64); + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(*pdw64Version), &cb); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) + { + hr = RegReadString(hk, wzName, &sczVersion); + RegExitOnFailure(hr, "Failed to read registry version as string."); + + hr = FileVersionFromStringEx(sczVersion, 0, pdw64Version); + RegExitOnFailure(hr, "Failed to convert registry string to version."); + } + else if (REG_QWORD == dwType) + { + RegExitOnWin32Error(er, hr, "Failed to read registry key."); + } + else // unexpected data type + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); + } + +LExit: + ReleaseStr(sczVersion); + + return hr; +} + + +/******************************************************************** + RegReadNumber - reads a DWORD registry key value as a number. + +*********************************************************************/ +extern "C" HRESULT DAPI RegReadNumber( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD* pdwValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwType = 0; + DWORD cb = sizeof(DWORD); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(pdwValue), &cb); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to query registry key value."); + + if (REG_DWORD != dwType) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); + } + +LExit: + return hr; +} + + +/******************************************************************** + RegReadQword - reads a QWORD registry key value as a number. + +*********************************************************************/ +extern "C" HRESULT DAPI RegReadQword( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out DWORD64* pqwValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwType = 0; + DWORD cb = sizeof(DWORD64); + + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(pqwValue), &cb); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + RegExitOnWin32Error(er, hr, "Failed to query registry key value."); + + if (REG_QWORD != dwType) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); + } + +LExit: + return hr; +} + + +/******************************************************************** + RegWriteBinary - writes a registry key value as a binary. + +*********************************************************************/ +HRESULT DAPI RegWriteBinary( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_bcount(cbBuffer) const BYTE *pbBuffer, + __in DWORD cbBuffer + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegSetValueExW(hk, wzName, 0, REG_BINARY, pbBuffer, cbBuffer); + RegExitOnWin32Error(er, hr, "Failed to write binary registry value with name: %ls", wzName); + +LExit: + return hr; +} + + +/******************************************************************** +RegWriteExpandString - writes a registry key value as an expand string. + +Note: if wzValue is NULL the value will be removed. +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteExpandString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue +) +{ + return WriteStringToRegistry(hk, wzName, wzValue, REG_EXPAND_SZ); +} + + +/******************************************************************** + RegWriteString - writes a registry key value as a string. + + Note: if wzValue is NULL the value will be removed. +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteString( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue + ) +{ + return WriteStringToRegistry(hk, wzName, wzValue, REG_SZ); +} + + +/******************************************************************** + RegWriteStringFormatted - writes a registry key value as a formatted string. + +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteStringFormatted( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in __format_string LPCWSTR szFormat, + ... + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + va_list args; + + va_start(args, szFormat); + hr = StrAllocFormattedArgs(&sczValue, szFormat, args); + va_end(args); + RegExitOnFailure(hr, "Failed to allocate %ls value.", wzName); + + hr = WriteStringToRegistry(hk, wzName, sczValue, REG_SZ); + +LExit: + ReleaseStr(sczValue); + + return hr; +} + + +/******************************************************************** + RegWriteStringArray - writes an array of strings as a REG_MULTI_SZ value + +*********************************************************************/ +HRESULT DAPI RegWriteStringArray( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_ecount(cValues) LPWSTR *rgwzValues, + __in DWORD cValues + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR wzCopyDestination = NULL; + LPCWSTR wzWriteValue = NULL; + LPWSTR sczWriteValue = NULL; + DWORD dwTotalStringSize = 0; + DWORD cbTotalStringSize = 0; + DWORD dwTemp = 0; + + if (0 == cValues) + { + wzWriteValue = L"\0"; + } + else + { + // Add space for the null terminator + dwTotalStringSize = 1; + + for (DWORD i = 0; i < cValues; ++i) + { + dwTemp = dwTotalStringSize; + hr = ::DWordAdd(dwTemp, 1 + lstrlenW(rgwzValues[i]), &dwTotalStringSize); + RegExitOnFailure(hr, "DWORD Overflow while adding length of string to write REG_MULTI_SZ"); + } + + hr = StrAlloc(&sczWriteValue, dwTotalStringSize); + RegExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ"); + + wzCopyDestination = sczWriteValue; + dwTemp = dwTotalStringSize; + for (DWORD i = 0; i < cValues; ++i) + { + hr = ::StringCchCopyW(wzCopyDestination, dwTotalStringSize, rgwzValues[i]); + RegExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]); + + dwTemp -= lstrlenW(rgwzValues[i]) + 1; + wzCopyDestination += lstrlenW(rgwzValues[i]) + 1; + } + + wzWriteValue = sczWriteValue; + } + + hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize); + RegExitOnFailure(hr, "Failed to get total string size in bytes"); + + er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast(wzWriteValue), cbTotalStringSize); + RegExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue); + +LExit: + ReleaseStr(sczWriteValue); + + return hr; +} + +/******************************************************************** + RegWriteNumber - writes a registry key value as a number. + +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteNumber( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in DWORD dwValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegSetValueExW(hk, wzName, 0, REG_DWORD, reinterpret_cast(&dwValue), sizeof(dwValue)); + RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); + +LExit: + return hr; +} + +/******************************************************************** + RegWriteQword - writes a registry key value as a Qword. + +*********************************************************************/ +extern "C" HRESULT DAPI RegWriteQword( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in DWORD64 qwValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegSetValueExW(hk, wzName, 0, REG_QWORD, reinterpret_cast(&qwValue), sizeof(qwValue)); + RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); + +LExit: + return hr; +} + +/******************************************************************** + RegQueryKey - queries the key for the number of subkeys and values. + +*********************************************************************/ +extern "C" HRESULT DAPI RegQueryKey( + __in HKEY hk, + __out_opt DWORD* pcSubKeys, + __out_opt DWORD* pcValues + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, pcSubKeys, NULL, NULL, pcValues, NULL, NULL, NULL, NULL); + RegExitOnWin32Error(er, hr, "Failed to get the number of subkeys and values under registry key."); + +LExit: + return hr; +} + +/******************************************************************** +RegKeyReadNumber - reads a DWORD registry key value as a number from +a specified subkey. + +*********************************************************************/ +extern "C" HRESULT DAPI RegKeyReadNumber( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit, + __out DWORD* pdwValue + ) +{ + HRESULT hr = S_OK; + HKEY hkKey = NULL; + + hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey); + RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey); + + hr = RegReadNumber(hkKey, wzName, pdwValue); + RegExitOnFailure(hr, "Failed to read value: %ls/@%ls", wzSubKey, wzName); + +LExit: + ReleaseRegKey(hkKey); + + return hr; +} + +/******************************************************************** +RegValueExists - determines whether a named value exists in a +specified subkey. + +*********************************************************************/ +extern "C" BOOL DAPI RegValueExists( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit + ) +{ + HRESULT hr = S_OK; + HKEY hkKey = NULL; + DWORD dwType = 0; + + hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey); + RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey); + + hr = RegGetType(hkKey, wzName, &dwType); + RegExitOnFailure(hr, "Failed to read value type: %ls/@%ls", wzSubKey, wzName); + +LExit: + ReleaseRegKey(hkKey); + + return SUCCEEDED(hr); +} + +static HRESULT WriteStringToRegistry( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __in_z_opt LPCWSTR wzValue, + __in DWORD dwType + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + size_t cbValue = 0; + + if (wzValue) + { + hr = ::StringCbLengthW(wzValue, STRSAFE_MAX_CCH * sizeof(TCHAR), &cbValue); + RegExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); + + er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast(wzValue), static_cast(cbValue)); + RegExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); + } + else + { + er = vpfnRegDeleteValueW(hk, wzName); + if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) + { + er = ERROR_SUCCESS; + } + RegExitOnWin32Error(er, hr, "Failed to delete registry value: %ls", wzName); + } + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/resrutil.cpp b/src/libs/dutil/WixToolset.DUtil/resrutil.cpp new file mode 100644 index 00000000..a6a7ee23 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/resrutil.cpp @@ -0,0 +1,266 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ResrExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__) +#define ResrExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RESRUTIL, p, x, e, s, __VA_ARGS__) +#define ResrExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, p, x, s, __VA_ARGS__) +#define ResrExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RESRUTIL, p, x, e, s, __VA_ARGS__) +#define ResrExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, p, x, s, __VA_ARGS__) +#define ResrExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RESRUTIL, e, x, s, __VA_ARGS__) +#define ResrExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RESRUTIL, g, x, s, __VA_ARGS__) + +#define RES_STRINGS_PER_BLOCK 16 + + +BOOL CALLBACK EnumLangIdProc( + __in_opt HMODULE hModule, + __in_z LPCSTR lpType, + __in_z LPCSTR lpName, + __in WORD wLanguage, + __in LONG_PTR lParam + ); + +/******************************************************************** +ResGetStringLangId - get the language id for a string in the string table. + +********************************************************************/ +extern "C" HRESULT DAPI ResGetStringLangId( + __in_opt LPCWSTR wzPath, + __in UINT uID, + __out WORD *pwLangId + ) +{ + Assert(pwLangId); + + HRESULT hr = S_OK; + HINSTANCE hModule = NULL; + DWORD dwBlockId = (uID / RES_STRINGS_PER_BLOCK) + 1; + WORD wFoundLangId = 0; + + if (wzPath && *wzPath) + { + hModule = LoadLibraryExW(wzPath, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); + ResrExitOnNullWithLastError(hModule, hr, "Failed to open resource file: %ls", wzPath); + } + +#pragma prefast(push) +#pragma prefast(disable:25068) + if (!::EnumResourceLanguagesA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), static_cast(EnumLangIdProc), reinterpret_cast(&wFoundLangId))) +#pragma prefast(pop) + { + ResrExitWithLastError(hr, "Failed to find string language identifier."); + } + + *pwLangId = wFoundLangId; + +LExit: + if (hModule) + { + ::FreeLibrary(hModule); + } + + return hr; +} + + +/******************************************************************** +ResReadString + +NOTE: ppwzString should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI ResReadString( + __in HINSTANCE hinst, + __in UINT uID, + __deref_out_z LPWSTR* ppwzString + ) +{ + Assert(hinst && ppwzString); + + HRESULT hr = S_OK; + DWORD cch = 64; // first guess + DWORD cchReturned = 0; + + do + { + hr = StrAlloc(ppwzString, cch); + ResrExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); + + cchReturned = ::LoadStringW(hinst, uID, *ppwzString, cch); + if (0 == cchReturned) + { + ResrExitWithLastError(hr, "Failed to load string resource id: %d", uID); + } + + // if the returned string count is one character too small, it's likely we have + // more data to read + if (cchReturned + 1 == cch) + { + cch *= 2; + hr = S_FALSE; + } + } while (S_FALSE == hr); + ResrExitOnFailure(hr, "Failed to load string resource id: %d", uID); + +LExit: + return hr; +} + + +/******************************************************************** + ResReadStringAnsi + + NOTE: ppszString should be freed with StrFree() +********************************************************************/ +extern "C" HRESULT DAPI ResReadStringAnsi( + __in HINSTANCE hinst, + __in UINT uID, + __deref_out_z LPSTR* ppszString + ) +{ + Assert(hinst && ppszString); + + HRESULT hr = S_OK; + DWORD cch = 64; // first guess + DWORD cchReturned = 0; + + do + { + hr = StrAnsiAlloc(ppszString, cch); + ResrExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID); + +#pragma prefast(push) +#pragma prefast(disable:25068) + cchReturned = ::LoadStringA(hinst, uID, *ppszString, cch); +#pragma prefast(pop) + if (0 == cchReturned) + { + ResrExitWithLastError(hr, "Failed to load string resource id: %d", uID); + } + + // if the returned string count is one character too small, it's likely we have + // more data to read + if (cchReturned + 1 == cch) + { + cch *= 2; + hr = S_FALSE; + } + } while (S_FALSE == hr); + ResrExitOnFailure(hr, "failed to load string resource id: %d", uID); + +LExit: + return hr; +} + + +/******************************************************************** +ResReadData - returns a pointer to the specified resource data + +NOTE: there is no "free" function for this call +********************************************************************/ +extern "C" HRESULT DAPI ResReadData( + __in_opt HINSTANCE hinst, + __in_z LPCSTR szDataName, + __deref_out_bcount(*pcb) PVOID *ppv, + __out DWORD *pcb + ) +{ + Assert(szDataName); + Assert(ppv); + + HRESULT hr = S_OK; + HRSRC hRsrc = NULL; + HGLOBAL hData = NULL; + DWORD cbData = 0; + +#pragma prefast(push) +#pragma prefast(disable:25068) + hRsrc = ::FindResourceExA(hinst, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); +#pragma prefast(pop) + ResrExitOnNullWithLastError(hRsrc, hr, "Failed to find resource."); + + hData = ::LoadResource(hinst, hRsrc); + ResrExitOnNullWithLastError(hData, hr, "Failed to load resource."); + + cbData = ::SizeofResource(hinst, hRsrc); + if (!cbData) + { + ResrExitWithLastError(hr, "Failed to get size of resource."); + } + + *ppv = ::LockResource(hData); + ResrExitOnNullWithLastError(*ppv, hr, "Failed to lock data resource."); + *pcb = cbData; + +LExit: + return hr; +} + + +/******************************************************************** +ResExportDataToFile - extracts the resource data to the specified target file + +********************************************************************/ +extern "C" HRESULT DAPI ResExportDataToFile( + __in_z LPCSTR szDataName, + __in_z LPCWSTR wzTargetFile, + __in DWORD dwCreationDisposition + ) +{ + HRESULT hr = S_OK; + PVOID pData = NULL; + DWORD cbData = 0; + DWORD cbWritten = 0; + HANDLE hFile = INVALID_HANDLE_VALUE; + BOOL bCreatedFile = FALSE; + + hr = ResReadData(NULL, szDataName, &pData, &cbData); + ResrExitOnFailure(hr, "Failed to GetData from %s.", szDataName); + + hFile = ::CreateFileW(wzTargetFile, GENERIC_WRITE, 0, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ResrExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzTargetFile); + } + bCreatedFile = TRUE; + + if (!::WriteFile(hFile, pData, cbData, &cbWritten, NULL)) + { + ResrExitWithLastError(hr, "Failed to ::WriteFile for %ls.", wzTargetFile); + } + +LExit: + ReleaseFile(hFile); + + if (FAILED(hr)) + { + if (bCreatedFile) + { + ::DeleteFileW(wzTargetFile); + } + } + + return hr; +} + + +BOOL CALLBACK EnumLangIdProc( + __in_opt HMODULE /* hModule */, + __in_z LPCSTR /* lpType */, + __in_z LPCSTR /* lpName */, + __in WORD wLanguage, + __in LONG_PTR lParam + ) +{ + WORD *pwLangId = reinterpret_cast(lParam); + + *pwLangId = wLanguage; + return TRUE; +} diff --git a/src/libs/dutil/WixToolset.DUtil/reswutil.cpp b/src/libs/dutil/WixToolset.DUtil/reswutil.cpp new file mode 100644 index 00000000..e78de84a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/reswutil.cpp @@ -0,0 +1,386 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ReswExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) +#define ReswExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__) +#define ReswExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__) +#define ReswExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__) +#define ReswExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__) +#define ReswExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RESWUTIL, e, x, s, __VA_ARGS__) +#define ReswExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RESWUTIL, g, x, s, __VA_ARGS__) + +#define RES_STRINGS_PER_BLOCK 16 + +// Internal data structure format for a string block in a resource table. +// Note: Strings are always stored as UNICODED. +typedef struct _RES_STRING_BLOCK +{ + DWORD dwBlockId; + WORD wLangId; + LPWSTR rgwz[RES_STRINGS_PER_BLOCK]; +} RES_STRING_BLOCK; + + +// private functions +static HRESULT StringBlockInitialize( + __in_opt HINSTANCE hModule, + __in DWORD dwBlockId, + __in WORD wLangId, + __in RES_STRING_BLOCK* pStrBlock + ); +static void StringBlockUnitialize( + __in RES_STRING_BLOCK* pStrBlock + ); +static HRESULT StringBlockChangeString( + __in RES_STRING_BLOCK* pStrBlock, + __in DWORD dwStringId, + __in_z LPCWSTR szData + ); +static HRESULT StringBlockConvertToResourceData( + __in const RES_STRING_BLOCK* pStrBlock, + __deref_out_bcount(*pcbData) LPVOID* ppvData, + __out DWORD* pcbData + ); +static HRESULT StringBlockConvertFromResourceData( + __in RES_STRING_BLOCK* pStrBlock, + __in_bcount(cbData) LPCVOID pvData, + __in SIZE_T cbData + ); + + +/******************************************************************** +ResWriteString - sets the string into to the specified file's resource name + +********************************************************************/ +extern "C" HRESULT DAPI ResWriteString( + __in_z LPCWSTR wzResourceFile, + __in DWORD dwDataId, + __in_z LPCWSTR wzData, + __in WORD wLangId + ) +{ + Assert(wzResourceFile); + Assert(wzData); + + HRESULT hr = S_OK; + HINSTANCE hModule = NULL; + HANDLE hUpdate = NULL; + RES_STRING_BLOCK StrBlock = { }; + LPVOID pvData = NULL; + DWORD cbData = 0; + + DWORD dwBlockId = (dwDataId / RES_STRINGS_PER_BLOCK) + 1; + DWORD dwStringId = (dwDataId % RES_STRINGS_PER_BLOCK); + + hModule = LoadLibraryExW(wzResourceFile, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); + ReswExitOnNullWithLastError(hModule, hr, "Failed to load library: %ls", wzResourceFile); + + hr = StringBlockInitialize(hModule, dwBlockId, wLangId, &StrBlock); + ReswExitOnFailure(hr, "Failed to get string block to update."); + + hr = StringBlockChangeString(&StrBlock, dwStringId, wzData); + ReswExitOnFailure(hr, "Failed to update string block string."); + + hr = StringBlockConvertToResourceData(&StrBlock, &pvData, &cbData); + ReswExitOnFailure(hr, "Failed to convert string block to resource data."); + + ::FreeLibrary(hModule); + hModule = NULL; + + hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); + ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); + + if (!::UpdateResourceA(hUpdate, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId, pvData, cbData)) + { + ReswExitWithLastError(hr, "Failed to ::UpdateResourceA."); + } + + if (!::EndUpdateResource(hUpdate, FALSE)) + { + ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); + } + + hUpdate = NULL; + +LExit: + ReleaseMem(pvData); + + StringBlockUnitialize(&StrBlock); + + if (hUpdate) + { + ::EndUpdateResource(hUpdate, TRUE); + } + + if (hModule) + { + ::FreeLibrary(hModule); + } + + return hr; +} + + +/******************************************************************** +ResWriteData - sets the data into to the specified file's resource name + +********************************************************************/ +extern "C" HRESULT DAPI ResWriteData( + __in_z LPCWSTR wzResourceFile, + __in_z LPCSTR szDataName, + __in PVOID pData, + __in DWORD cbData + ) +{ + Assert(wzResourceFile); + Assert(szDataName); + Assert(pData); + Assert(cbData); + + HRESULT hr = S_OK; + HANDLE hUpdate = NULL; + + hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); + ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); + + if (!::UpdateResourceA(hUpdate, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), pData, cbData)) + { + ReswExitWithLastError(hr, "Failed to ::UpdateResourceA."); + } + + if (!::EndUpdateResource(hUpdate, FALSE)) + { + ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); + } + + hUpdate = NULL; + +LExit: + if (hUpdate) + { + ::EndUpdateResource(hUpdate, TRUE); + } + + return hr; +} + + +/******************************************************************** +ResImportDataFromFile - reads a file and sets the data into to the specified file's resource name + +********************************************************************/ +extern "C" HRESULT DAPI ResImportDataFromFile( + __in_z LPCWSTR wzTargetFile, + __in_z LPCWSTR wzSourceFile, + __in_z LPCSTR szDataName + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD cbFile = 0; + HANDLE hMap = NULL; + PVOID pv = NULL; + + hFile = ::CreateFileW(wzSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ReswExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzSourceFile); + } + + cbFile = ::GetFileSize(hFile, NULL); + if (!cbFile) + { + ReswExitWithLastError(hr, "Failed to GetFileSize for %ls.", wzSourceFile); + } + + hMap = ::CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + ReswExitOnNullWithLastError(hMap, hr, "Failed to CreateFileMapping for %ls.", wzSourceFile); + + pv = ::MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, cbFile); + ReswExitOnNullWithLastError(pv, hr, "Failed to MapViewOfFile for %ls.", wzSourceFile); + + hr = ResWriteData(wzTargetFile, szDataName, pv, cbFile); + ReswExitOnFailure(hr, "Failed to ResSetData %s on file %ls.", szDataName, wzTargetFile); + +LExit: + if (pv) + { + ::UnmapViewOfFile(pv); + } + + if (hMap) + { + ::CloseHandle(hMap); + } + + ReleaseFile(hFile); + + return hr; +} + + +static HRESULT StringBlockInitialize( + __in_opt HINSTANCE hModule, + __in DWORD dwBlockId, + __in WORD wLangId, + __in RES_STRING_BLOCK* pStrBlock + ) +{ + HRESULT hr = S_OK; + HRSRC hRsrc = NULL; + HGLOBAL hData = NULL; + LPCVOID pvData = NULL; // does not need to be freed + DWORD cbData = 0; + + hRsrc = ::FindResourceExA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId); + ReswExitOnNullWithLastError(hRsrc, hr, "Failed to ::FindResourceExW."); + + hData = ::LoadResource(hModule, hRsrc); + ReswExitOnNullWithLastError(hData, hr, "Failed to ::LoadResource."); + + cbData = ::SizeofResource(hModule, hRsrc); + if (!cbData) + { + ReswExitWithLastError(hr, "Failed to ::SizeofResource."); + } + + pvData = ::LockResource(hData); + ReswExitOnNullWithLastError(pvData, hr, "Failed to lock data resource."); + + pStrBlock->dwBlockId = dwBlockId; + pStrBlock->wLangId = wLangId; + + hr = StringBlockConvertFromResourceData(pStrBlock, pvData, cbData); + ReswExitOnFailure(hr, "Failed to convert string block from resource data."); + +LExit: + return hr; +} + + +static void StringBlockUnitialize( + __in RES_STRING_BLOCK* pStrBlock + ) +{ + if (pStrBlock) + { + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) + { + ReleaseNullMem(pStrBlock->rgwz[i]); + } + } +} + + +static HRESULT StringBlockChangeString( + __in RES_STRING_BLOCK* pStrBlock, + __in DWORD dwStringId, + __in_z LPCWSTR szData + ) +{ + HRESULT hr = S_OK; + LPWSTR pwzData = NULL; + size_t cchData = 0; + + hr = ::StringCchLengthW(szData, STRSAFE_MAX_LENGTH, &cchData); + ReswExitOnRootFailure(hr, "Failed to get block string length."); + + pwzData = static_cast(MemAlloc((cchData + 1) * sizeof(WCHAR), TRUE)); + ReswExitOnNull(pwzData, hr, E_OUTOFMEMORY, "Failed to allocate new block string."); + + hr = ::StringCchCopyW(pwzData, cchData + 1, szData); + ReswExitOnRootFailure(hr, "Failed to copy new block string."); + + ReleaseNullMem(pStrBlock->rgwz[dwStringId]); + + pStrBlock->rgwz[dwStringId] = pwzData; + pwzData = NULL; + +LExit: + ReleaseMem(pwzData); + + return hr; +} + + +static HRESULT StringBlockConvertToResourceData( + __in const RES_STRING_BLOCK* pStrBlock, + __deref_out_bcount(*pcbData) LPVOID* ppvData, + __out DWORD* pcbData + ) +{ + HRESULT hr = S_OK; + DWORD cbData = 0; + LPVOID pvData = NULL; + WCHAR* pwz = NULL; + + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) + { + cbData += (lstrlenW(pStrBlock->rgwz[i]) + 1); + } + cbData *= sizeof(WCHAR); + + pvData = MemAlloc(cbData, TRUE); + ReswExitOnNull(pvData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to convert string block."); + + pwz = static_cast(pvData); + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) + { + DWORD cch = lstrlenW(pStrBlock->rgwz[i]); + + *pwz = static_cast(cch); + ++pwz; + + for (DWORD j = 0; j < cch; ++j) + { + *pwz = pStrBlock->rgwz[i][j]; + ++pwz; + } + } + + *pcbData = cbData; + *ppvData = pvData; + pvData = NULL; + +LExit: + ReleaseMem(pvData); + + return hr; +} + + +static HRESULT StringBlockConvertFromResourceData( + __in RES_STRING_BLOCK* pStrBlock, + __in_bcount(cbData) LPCVOID pvData, + __in SIZE_T cbData + ) +{ + UNREFERENCED_PARAMETER(cbData); + HRESULT hr = S_OK; + LPCWSTR pwzParse = static_cast(pvData); + + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) + { + DWORD cchParse = static_cast(*pwzParse); + ++pwzParse; + + pStrBlock->rgwz[i] = static_cast(MemAlloc((cchParse + 1) * sizeof(WCHAR), TRUE)); + ReswExitOnNull(pStrBlock->rgwz[i], hr, E_OUTOFMEMORY, "Failed to populate pStrBlock."); + + hr = ::StringCchCopyNExW(pStrBlock->rgwz[i], cchParse + 1, pwzParse, cchParse, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + ReswExitOnFailure(hr, "Failed to copy parsed resource data into string block."); + + pwzParse += cchParse; + } + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/rexutil.cpp b/src/libs/dutil/WixToolset.DUtil/rexutil.cpp new file mode 100644 index 00000000..155ca714 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/rexutil.cpp @@ -0,0 +1,601 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#include "rexutil.h" + + +// Exit macros +#define RexExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__) +#define RexExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__) +#define RexExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__) +#define RexExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__) +#define RexExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__) +#define RexExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REXUTIL, e, x, s, __VA_ARGS__) +#define RexExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REXUTIL, g, x, s, __VA_ARGS__) + +// +// static globals +// +static HMODULE vhCabinetDll = NULL; +static HFDI vhfdi = NULL; +static ERF verf; + +static FAKE_FILE vrgffFileTable[FILETABLESIZE]; +static DWORD vcbRes; +static LPCBYTE vpbRes; +static CHAR vszResource[MAX_PATH]; +static REX_CALLBACK_WRITE vpfnWrite = NULL; + +static HRESULT vhrLastError = S_OK; + +// +// structs +// +struct REX_CALLBACK_STRUCT +{ + BOOL fStopExtracting; // flag set when no more files are needed + LPCWSTR pwzExtract; // file to extract ("*" means extract all) + LPCWSTR pwzExtractDir; // directory to extract files to + LPCWSTR pwzExtractName; // name of file (pwzExtract can't be "*") + + // possible user data + REX_CALLBACK_PROGRESS pfnProgress; + LPVOID pvContext; +}; + +// +// prototypes +// +static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize); +static __callback void DIAMONDAPI RexFree(LPVOID pvData); +static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode); +static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb); +static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb); +static __callback int FAR DIAMONDAPI RexClose(INT_PTR hf); +static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype); +static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify); + + +/******************************************************************** + RexInitialize - initializes internal static variables + +*******************************************************************/ +extern "C" HRESULT RexInitialize() +{ + Assert(!vhfdi); + + HRESULT hr = S_OK; + + vhfdi = ::FDICreate(RexAlloc, RexFree, RexOpen, RexRead, RexWrite, RexClose, RexSeek, cpuUNKNOWN, &verf); + if (!vhfdi) + { + hr = E_FAIL; + RexExitOnFailure(hr, "failed to initialize cabinet.dll"); // TODO: put verf info in trace message here + } + + ::ZeroMemory(vrgffFileTable, sizeof(vrgffFileTable)); + +LExit: + if (FAILED(hr)) + { + ::FDIDestroy(vhfdi); + vhfdi = NULL; + } + + return hr; +} + + +/******************************************************************** + RexUninitialize - initializes internal static variables + +*******************************************************************/ +extern "C" void RexUninitialize() +{ + if (vhfdi) + { + ::FDIDestroy(vhfdi); + vhfdi = NULL; + } +} + + +/******************************************************************** + RexExtract - extracts one or all files from a resource cabinet + + NOTE: wzExtractId can be a single file id or "*" to extract all files + wzExttractDir must be normalized (end in a "\") + wzExtractName is ignored if wzExtractId is "*" +*******************************************************************/ +extern "C" HRESULT RexExtract( + __in_z LPCSTR szResource, + __in_z LPCWSTR wzExtractId, + __in_z LPCWSTR wzExtractDir, + __in_z LPCWSTR wzExtractName, + __in REX_CALLBACK_PROGRESS pfnProgress, + __in REX_CALLBACK_WRITE pfnWrite, + __in LPVOID pvContext + ) +{ + Assert(vhfdi); + HRESULT hr = S_OK; + BOOL fResult; + + HRSRC hResInfo = NULL; + HANDLE hRes = NULL; + + REX_CALLBACK_STRUCT rcs; + + // remember the write callback + vpfnWrite = pfnWrite; + + // + // load the cabinet resource + // + hResInfo = ::FindResourceExA(NULL, RT_RCDATA, szResource, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); + RexExitOnNullWithLastError(hResInfo, hr, "Failed to find resource."); + //hResInfo = ::FindResourceW(NULL, wzResource, /*RT_RCDATA*/MAKEINTRESOURCEW(10)); + //ExitOnNullWithLastError(hResInfo, hr, "failed to load resource info"); + + hRes = ::LoadResource(NULL, hResInfo); + RexExitOnNullWithLastError(hRes, hr, "failed to load resource"); + + vcbRes = ::SizeofResource(NULL, hResInfo); + vpbRes = (const BYTE*)::LockResource(hRes); + + // TODO: Call FDIIsCabinet to confirm resource is a cabinet before trying to extract from it + + // + // convert the resource name to multi-byte + // + //if (!::WideCharToMultiByte(CP_ACP, 0, wzResource, -1, vszResource, countof(vszResource), NULL, NULL)) + //{ + // RexExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource); + //} + + hr = ::StringCchCopyA(vszResource, countof(vszResource), szResource); + RexExitOnFailure(hr, "Failed to copy resource name to global."); + + // + // iterate through files in cabinet extracting them to the callback function + // + rcs.fStopExtracting = FALSE; + rcs.pwzExtract = wzExtractId; + rcs.pwzExtractDir = wzExtractDir; + rcs.pwzExtractName = wzExtractName; + rcs.pfnProgress = pfnProgress; + rcs.pvContext = pvContext; + + fResult = ::FDICopy(vhfdi, vszResource, "", 0, RexCallback, NULL, static_cast(&rcs)); + if (!fResult && !rcs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure + { + hr = vhrLastError; // TODO: put verf info in trace message here + } + +LExit: + return hr; +} + + +/**************************************************************************** + default extract routines + +****************************************************************************/ +static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize) +{ + return MemAlloc(dwSize, FALSE); +} + + +static __callback void DIAMONDAPI RexFree(LPVOID pvData) +{ + MemFree(pvData); +} + + +static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + int i = 0; + + // if FDI asks for some unusual mode (__in low memory situation it could ask for a scratch file) fail + if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) + { + hr = E_OUTOFMEMORY; + RexExitOnFailure(hr, "FDI asked for to create a scratch file, which is unusual"); + } + + // find an empty spot in the fake file table + for (i = 0; i < FILETABLESIZE; ++i) + { + if (!vrgffFileTable[i].fUsed) + { + break; + } + } + + // we should never run out of space in the fake file table + if (FILETABLESIZE <= i) + { + hr = E_OUTOFMEMORY; + RexExitOnFailure(hr, "File table exceeded"); + } + + if (0 == lstrcmpA(vszResource, pszFile)) + { + vrgffFileTable[i].fUsed = TRUE; + vrgffFileTable[i].fftType = MEMORY_FILE; + vrgffFileTable[i].mfFile.vpStart = static_cast(vpbRes); + vrgffFileTable[i].mfFile.uiCurrent = 0; + vrgffFileTable[i].mfFile.uiLength = vcbRes; + } + else // it's a real file + { + hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + RexExitWithLastError(hr, "failed to open file: %s", pszFile); + } + + vrgffFileTable[i].fUsed = TRUE; + vrgffFileTable[i].fftType = NORMAL_FILE; + vrgffFileTable[i].hFile = hFile; + } + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : i; +} + + +static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb) +{ + Assert(vrgffFileTable[hf].fUsed); + + HRESULT hr = S_OK; + DWORD cbRead = 0; + DWORD cbAvailable = 0; + + if (MEMORY_FILE == vrgffFileTable[hf].fftType) + { + // ensure that we don't read past the length of the resource + cbAvailable = vrgffFileTable[hf].mfFile.uiLength - vrgffFileTable[hf].mfFile.uiCurrent; + cbRead = cb < cbAvailable? cb : cbAvailable; + + memcpy(pv, static_cast(vrgffFileTable[hf].mfFile.vpStart + vrgffFileTable[hf].mfFile.uiCurrent), cbRead); + + vrgffFileTable[hf].mfFile.uiCurrent += cbRead; + } + else // NORMAL_FILE + { + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); + + if (!::ReadFile(vrgffFileTable[hf].hFile, pv, cb, &cbRead, NULL)) + { + RexExitWithLastError(hr, "failed to read during cabinet extraction"); + } + } + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : cbRead; +} + + +static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb) +{ + Assert(vrgffFileTable[hf].fUsed); + Assert(vrgffFileTable[hf].fftType == NORMAL_FILE); // we should never be writing to a memory file + + HRESULT hr = S_OK; + DWORD cbWrite = 0; + + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); + if (!::WriteFile(reinterpret_cast(vrgffFileTable[hf].hFile), pv, cb, &cbWrite, NULL)) + { + RexExitWithLastError(hr, "failed to write during cabinet extraction"); + } + + // call the writer callback if defined + if (vpfnWrite) + { + vpfnWrite(cb); + } + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : cbWrite; +} + + +static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype) +{ + Assert(vrgffFileTable[hf].fUsed); + + HRESULT hr = S_OK; + DWORD dwMoveMethod; + LONG lMove = 0; + + switch (seektype) + { + case 0: // SEEK_SET + dwMoveMethod = FILE_BEGIN; + break; + case 1: /// SEEK_CUR + dwMoveMethod = FILE_CURRENT; + break; + case 2: // SEEK_END + dwMoveMethod = FILE_END; + break; + default : + dwMoveMethod = 0; + hr = E_UNEXPECTED; + RexExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); + } + + if (MEMORY_FILE == vrgffFileTable[hf].fftType) + { + if (FILE_BEGIN == dwMoveMethod) + { + vrgffFileTable[hf].mfFile.uiCurrent = dist; + } + else if (FILE_CURRENT == dwMoveMethod) + { + vrgffFileTable[hf].mfFile.uiCurrent += dist; + } + else // FILE_END + { + vrgffFileTable[hf].mfFile.uiCurrent = vrgffFileTable[hf].mfFile.uiLength + dist; + } + + lMove = vrgffFileTable[hf].mfFile.uiCurrent; + } + else // NORMAL_FILE + { + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); + + // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. + // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) + lMove = ::SetFilePointer(vrgffFileTable[hf].hFile, dist, NULL, dwMoveMethod); + if (0xFFFFFFFF == lMove) + { + RexExitWithLastError(hr, "failed to move file pointer %d bytes", dist); + } + } + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : lMove; +} + + +__callback int FAR DIAMONDAPI RexClose(INT_PTR hf) +{ + Assert(vrgffFileTable[hf].fUsed); + + HRESULT hr = S_OK; + + if (MEMORY_FILE == vrgffFileTable[hf].fftType) + { + vrgffFileTable[hf].mfFile.vpStart = NULL; + vrgffFileTable[hf].mfFile.uiCurrent = 0; + vrgffFileTable[hf].mfFile.uiLength = 0; + } + else + { + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); + + if (!::CloseHandle(vrgffFileTable[hf].hFile)) + { + RexExitWithLastError(hr, "failed to close file during cabinet extraction"); + } + + vrgffFileTable[hf].hFile = INVALID_HANDLE_VALUE; + } + + vrgffFileTable[hf].fUsed = FALSE; + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return FAILED(hr) ? -1 : 0; +} + + +static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify) +{ + Assert(pFDINotify->pv); + + HRESULT hr = S_OK; + int ipResult = 0; // result to return on success + HANDLE hFile = INVALID_HANDLE_VALUE; + + REX_CALLBACK_STRUCT* prcs = static_cast(pFDINotify->pv); + LPCSTR sz; + WCHAR wz[MAX_PATH]; + FILETIME ft; + int i = 0; + + switch (iNotification) + { + case fdintCOPY_FILE: // beGIN extracting a resource from cabinet + Assert(pFDINotify->psz1); + + if (prcs->fStopExtracting) + { + ExitFunction1(hr = S_FALSE); // no more extracting + } + + // convert params to useful variables + sz = static_cast(pFDINotify->psz1); + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) + { + RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + } + + if (prcs->pfnProgress) + { + hr = prcs->pfnProgress(TRUE, wz, prcs->pvContext); + if (S_OK != hr) + { + ExitFunction(); + } + } + + if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, wz)) + { + // get the created date for the resource in the cabinet + if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft)) + { + RexExitWithLastError(hr, "failed to get time for resource: %ls", wz); + } + + WCHAR wzPath[MAX_PATH]; + + hr = ::StringCchCopyW(wzPath, countof(wzPath), prcs->pwzExtractDir); + RexExitOnFailure(hr, "failed to copy extract directory: %ls for file: %ls", prcs->pwzExtractDir, wz); + + if (L'*' == *prcs->pwzExtract) + { + hr = ::StringCchCatW(wzPath, countof(wzPath), wz); + RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); + } + else + { + Assert(*prcs->pwzExtractName); + + hr = ::StringCchCatW(wzPath, countof(wzPath), prcs->pwzExtractName); + RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName); + } + + // Quickly chop off the file name part of the path to ensure the path exists + // then put the file name back on the path (by putting the first character + // back over the null terminator). + LPWSTR wzFile = PathFile(wzPath); + WCHAR wzFileFirstChar = *wzFile; + *wzFile = L'\0'; + + hr = DirEnsureExists(wzPath, NULL); + RexExitOnFailure(hr, "failed to ensure directory: %ls", wzPath); + + hr = S_OK; + + *wzFile = wzFileFirstChar; + + // find an empty spot in the fake file table + for (i = 0; i < FILETABLESIZE; ++i) + { + if (!vrgffFileTable[i].fUsed) + { + break; + } + } + + // we should never run out of space in the fake file table + if (FILETABLESIZE <= i) + { + hr = E_OUTOFMEMORY; + RexExitOnFailure(hr, "File table exceeded"); + } + + // open the file + hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + RexExitWithLastError(hr, "failed to open file: %ls", wzPath); + } + + vrgffFileTable[i].fUsed = TRUE; + vrgffFileTable[i].fftType = NORMAL_FILE; + vrgffFileTable[i].hFile = hFile; + + ipResult = i; + + ::SetFileTime(vrgffFileTable[i].hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails) + + if (::SetFilePointer(vrgffFileTable[i].hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) + { + if (::SetEndOfFile(vrgffFileTable[i].hFile)) + { + ::SetFilePointer(vrgffFileTable[i].hFile, 0, NULL, FILE_BEGIN); // reset the file pointer + } + } + } + else // resource wasn't requested, skip it + { + hr = S_OK; + ipResult = 0; + } + + break; + case fdintCLOSE_FILE_INFO: // resource extraction complete + Assert(pFDINotify->hf && pFDINotify->psz1); + + // convert params to useful variables + sz = static_cast(pFDINotify->psz1); + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) + { + RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); + } + + RexClose(pFDINotify->hf); + + if (prcs->pfnProgress) + { + hr = prcs->pfnProgress(FALSE, wz, prcs->pvContext); + } + + if (S_OK == hr && L'*' == *prcs->pwzExtract) // if everything is okay and we're extracting all files, keep going + { + ipResult = TRUE; + } + else // something went wrong or we only needed to extract one file + { + hr = S_OK; + ipResult = FALSE; + prcs->fStopExtracting = TRUE; + } + + break; + case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through + case fdintNEXT_CABINET: __fallthrough; + case fdintENUMERATE: __fallthrough; + case fdintCABINET_INFO: + break; + default: + AssertSz(FALSE, "RexCallback() - unknown FDI notification command"); + }; + +LExit: + if (FAILED(hr)) + { + vhrLastError = hr; + } + + return (S_OK == hr) ? ipResult : -1; +} diff --git a/src/libs/dutil/WixToolset.DUtil/rmutil.cpp b/src/libs/dutil/WixToolset.DUtil/rmutil.cpp new file mode 100644 index 00000000..95c8c8a4 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/rmutil.cpp @@ -0,0 +1,488 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#include + + +// Exit macros +#define RmExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__) +#define RmExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RMUTIL, p, x, e, s, __VA_ARGS__) +#define RmExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RMUTIL, p, x, s, __VA_ARGS__) +#define RmExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RMUTIL, p, x, e, s, __VA_ARGS__) +#define RmExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RMUTIL, p, x, s, __VA_ARGS__) +#define RmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RMUTIL, e, x, s, __VA_ARGS__) +#define RmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RMUTIL, g, x, s, __VA_ARGS__) + +#define ARRAY_GROWTH_SIZE 5 + +typedef DWORD (WINAPI *PFNRMJOINSESSION)( + __out DWORD *pSessionHandle, + __in_z const WCHAR strSessionKey[] + ); + +typedef DWORD (WINAPI *PFNRMENDSESSION)( + __in DWORD dwSessionHandle + ); + +typedef DWORD (WINAPI *PFNRMREGISTERRESOURCES)( + __in DWORD dwSessionHandle, + __in UINT nFiles, + __in_z_opt LPWSTR *rgsFilenames, + __in UINT nApplications, + __in_opt RM_UNIQUE_PROCESS *rgApplications, + __in UINT nServices, + __in_z_opt LPWSTR *rgsServiceNames + ); + +typedef struct _RMU_SESSION +{ + CRITICAL_SECTION cs; + DWORD dwSessionHandle; + BOOL fStartedSessionHandle; + BOOL fInitialized; + + UINT cFilenames; + LPWSTR *rgsczFilenames; + + UINT cApplications; + RM_UNIQUE_PROCESS *rgApplications; + + UINT cServiceNames; + LPWSTR *rgsczServiceNames; + +} RMU_SESSION; + +static volatile LONG vcRmuInitialized = 0; +static HMODULE vhModule = NULL; +static PFNRMJOINSESSION vpfnRmJoinSession = NULL; +static PFNRMENDSESSION vpfnRmEndSession = NULL; +static PFNRMREGISTERRESOURCES vpfnRmRegisterResources = NULL; + +static HRESULT RmuInitialize(); +static void RmuUninitialize(); + +static HRESULT RmuApplicationArrayAlloc( + __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications, + __inout LPUINT pcApplications, + __in DWORD dwProcessId, + __in FILETIME ProcessStartTime + ); + +static HRESULT RmuApplicationArrayFree( + __in RM_UNIQUE_PROCESS *rgApplications + ); + +#define ReleaseNullApplicationArray(rg, c) { if (rg) { RmuApplicationArrayFree(rg); c = 0; rg = NULL; } } + +/******************************************************************** +RmuJoinSession - Joins an existing Restart Manager session. + +********************************************************************/ +extern "C" HRESULT DAPI RmuJoinSession( + __out PRMU_SESSION *ppSession, + __in_z LPCWSTR wzSessionKey + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + PRMU_SESSION pSession = NULL; + + *ppSession = NULL; + + pSession = static_cast(MemAlloc(sizeof(RMU_SESSION), TRUE)); + RmExitOnNull(pSession, hr, E_OUTOFMEMORY, "Failed to allocate the RMU_SESSION structure."); + + hr = RmuInitialize(); + RmExitOnFailure(hr, "Failed to initialize Restart Manager."); + + er = vpfnRmJoinSession(&pSession->dwSessionHandle, wzSessionKey); + RmExitOnWin32Error(er, hr, "Failed to join Restart Manager session %ls.", wzSessionKey); + + ::InitializeCriticalSection(&pSession->cs); + pSession->fInitialized = TRUE; + + *ppSession = pSession; + +LExit: + if (FAILED(hr)) + { + ReleaseNullMem(pSession); + } + + return hr; +} + +/******************************************************************** +RmuAddFile - Adds the file path to the Restart Manager session. + +You should call this multiple times as necessary before calling +RmuRegisterResources. + +********************************************************************/ +extern "C" HRESULT DAPI RmuAddFile( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + + ::EnterCriticalSection(&pSession->cs); + + // Create or grow the jagged array. + hr = StrArrayAllocString(&pSession->rgsczFilenames, &pSession->cFilenames, wzPath, 0); + RmExitOnFailure(hr, "Failed to add the filename to the array."); + +LExit: + ::LeaveCriticalSection(&pSession->cs); + return hr; +} + +/******************************************************************** +RmuAddProcessById - Adds the process ID to the Restart Manager sesion. + +You should call this multiple times as necessary before calling +RmuRegisterResources. + +********************************************************************/ +extern "C" HRESULT DAPI RmuAddProcessById( + __in PRMU_SESSION pSession, + __in DWORD dwProcessId + ) +{ + HRESULT hr = S_OK; + HANDLE hProcess = NULL; + FILETIME CreationTime = {}; + FILETIME ExitTime = {}; + FILETIME KernelTime = {}; + FILETIME UserTime = {}; + BOOL fLocked = FALSE; + + HANDLE hToken = NULL; + TOKEN_PRIVILEGES priv = { 0 }; + TOKEN_PRIVILEGES* pPrevPriv = NULL; + DWORD cbPrevPriv = 0; + DWORD er = ERROR_SUCCESS; + BOOL fAdjustedPrivileges = FALSE; + BOOL fElevated = FALSE; + ProcElevated(::GetCurrentProcess(), &fElevated); + + // Must be elevated to adjust process privileges + if (fElevated) { + // Adding SeDebugPrivilege in the event that the process targeted by ::OpenProcess() is in a another user context. + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) + { + RmExitWithLastError(hr, "Failed to get process token."); + } + + priv.PrivilegeCount = 1; + priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (!::LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &priv.Privileges[0].Luid)) + { + RmExitWithLastError(hr, "Failed to get debug privilege LUID."); + } + + cbPrevPriv = sizeof(TOKEN_PRIVILEGES); + pPrevPriv = static_cast(MemAlloc(cbPrevPriv, TRUE)); + RmExitOnNull(pPrevPriv, hr, E_OUTOFMEMORY, "Failed to allocate memory for empty previous privileges."); + + if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) + { + LPVOID pv = MemReAlloc(pPrevPriv, cbPrevPriv, TRUE); + RmExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for previous privileges."); + pPrevPriv = static_cast(pv); + + if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) + { + RmExitWithLastError(hr, "Failed to get debug privilege LUID."); + } + } + + fAdjustedPrivileges = TRUE; + } + + hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId); + if (hProcess) + { + if (!::GetProcessTimes(hProcess, &CreationTime, &ExitTime, &KernelTime, &UserTime)) + { + RmExitWithLastError(hr, "Failed to get the process times for process ID %d.", dwProcessId); + } + + ::EnterCriticalSection(&pSession->cs); + fLocked = TRUE; + hr = RmuApplicationArrayAlloc(&pSession->rgApplications, &pSession->cApplications, dwProcessId, CreationTime); + RmExitOnFailure(hr, "Failed to add the application to the array."); + } + else + { + er = ::GetLastError(); + if (ERROR_ACCESS_DENIED == er) + { + // OpenProcess will fail when not elevated and the target process is in another user context. Let the caller log and continue. + hr = E_NOTFOUND; + } + else + { + RmExitOnWin32Error(er, hr, "Failed to open the process ID %d.", dwProcessId); + } + } + +LExit: + if (hProcess) + { + ::CloseHandle(hProcess); + } + + if (fAdjustedPrivileges) + { + ::AdjustTokenPrivileges(hToken, FALSE, pPrevPriv, 0, NULL, NULL); + } + + ReleaseMem(pPrevPriv); + ReleaseHandle(hToken); + + if (fLocked) + { + ::LeaveCriticalSection(&pSession->cs); + } + + return hr; +} + +/******************************************************************** +RmuAddProcessesByName - Adds all processes by the given process name + to the Restart Manager Session. + +You should call this multiple times as necessary before calling +RmuRegisterResources. + +********************************************************************/ +extern "C" HRESULT DAPI RmuAddProcessesByName( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzProcessName + ) +{ + HRESULT hr = S_OK; + DWORD *pdwProcessIds = NULL; + DWORD cProcessIds = 0; + BOOL fNotFound = FALSE; + + hr = ProcFindAllIdsFromExeName(wzProcessName, &pdwProcessIds, &cProcessIds); + RmExitOnFailure(hr, "Failed to enumerate all the processes by name %ls.", wzProcessName); + + for (DWORD i = 0; i < cProcessIds; ++i) + { + hr = RmuAddProcessById(pSession, pdwProcessIds[i]); + if (E_NOTFOUND == hr) + { + // RmuAddProcessById returns E_NOTFOUND when this setup is not elevated and OpenProcess returned access denied (target process running under another user account). + fNotFound = TRUE; + } + else + { + RmExitOnFailure(hr, "Failed to add process %ls (%d) to the Restart Manager session.", wzProcessName, pdwProcessIds[i]); + } + } + + // If one or more calls to RmuAddProcessById returned E_NOTFOUND, then return E_NOTFOUND even if other calls succeeded, so that caller can log the issue. + if (fNotFound) + { + hr = E_NOTFOUND; + } + +LExit: + ReleaseMem(pdwProcessIds); + + return hr; +} + +/******************************************************************** +RmuAddService - Adds the service name to the Restart Manager session. + +You should call this multiple times as necessary before calling +RmuRegisterResources. + +********************************************************************/ +extern "C" HRESULT DAPI RmuAddService( + __in PRMU_SESSION pSession, + __in_z LPCWSTR wzServiceName + ) +{ + HRESULT hr = S_OK; + + ::EnterCriticalSection(&pSession->cs); + + hr = StrArrayAllocString(&pSession->rgsczServiceNames, &pSession->cServiceNames, wzServiceName, 0); + RmExitOnFailure(hr, "Failed to add the service name to the array."); + +LExit: + ::LeaveCriticalSection(&pSession->cs); + return hr; +} + +/******************************************************************** +RmuRegisterResources - Registers resources for the Restart Manager. + +This should be called rarely because it is expensive to run. Call +functions like RmuAddFile for multiple resources then commit them +as a batch of updates to RmuRegisterResources. + +Duplicate resources appear to be handled by Restart Manager. +Only one WM_QUERYENDSESSION is being sent for each top-level window. + +********************************************************************/ +extern "C" HRESULT DAPI RmuRegisterResources( + __in PRMU_SESSION pSession + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized."); + + ::EnterCriticalSection(&pSession->cs); + + er = vpfnRmRegisterResources( + pSession->dwSessionHandle, + pSession->cFilenames, + pSession->rgsczFilenames, + pSession->cApplications, + pSession->rgApplications, + pSession->cServiceNames, + pSession->rgsczServiceNames + ); + RmExitOnWin32Error(er, hr, "Failed to register the resources with the Restart Manager session."); + + // Empty the arrays if registered in case additional resources are added later. + ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); + ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications); + ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames); + +LExit: + ::LeaveCriticalSection(&pSession->cs); + return hr; +} + +/******************************************************************** +RmuEndSession - Ends the session. + +If the session was joined by RmuJoinSession, any remaining resources +are registered before the session is ended (left). + +********************************************************************/ +extern "C" HRESULT DAPI RmuEndSession( + __in PRMU_SESSION pSession + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized."); + + // Make sure all resources are registered if we joined the session. + if (!pSession->fStartedSessionHandle) + { + hr = RmuRegisterResources(pSession); + RmExitOnFailure(hr, "Failed to register remaining resources."); + } + + er = vpfnRmEndSession(pSession->dwSessionHandle); + RmExitOnWin32Error(er, hr, "Failed to end the Restart Manager session."); + +LExit: + if (pSession->fInitialized) + { + ::DeleteCriticalSection(&pSession->cs); + } + + ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); + ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications); + ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames); + ReleaseNullMem(pSession); + + RmuUninitialize(); + + return hr; +} + +static HRESULT RmuInitialize() +{ + HRESULT hr = S_OK; + HMODULE hModule = NULL; + + LONG iRef = ::InterlockedIncrement(&vcRmuInitialized); + if (1 == iRef && !vhModule) + { + hr = LoadSystemLibrary(L"rstrtmgr.dll", &hModule); + RmExitOnFailure(hr, "Failed to load the rstrtmgr.dll module."); + + vpfnRmJoinSession = reinterpret_cast(::GetProcAddress(hModule, "RmJoinSession")); + RmExitOnNullWithLastError(vpfnRmJoinSession, hr, "Failed to get the RmJoinSession procedure from rstrtmgr.dll."); + + vpfnRmRegisterResources = reinterpret_cast(::GetProcAddress(hModule, "RmRegisterResources")); + RmExitOnNullWithLastError(vpfnRmRegisterResources, hr, "Failed to get the RmRegisterResources procedure from rstrtmgr.dll."); + + vpfnRmEndSession = reinterpret_cast(::GetProcAddress(hModule, "RmEndSession")); + RmExitOnNullWithLastError(vpfnRmEndSession, hr, "Failed to get the RmEndSession procedure from rstrtmgr.dll."); + + vhModule = hModule; + } + +LExit: + return hr; +} + +static void RmuUninitialize() +{ + LONG iRef = ::InterlockedDecrement(&vcRmuInitialized); + if (0 == iRef && vhModule) + { + vpfnRmJoinSession = NULL; + vpfnRmEndSession = NULL; + vpfnRmRegisterResources = NULL; + + ::FreeLibrary(vhModule); + vhModule = NULL; + } +} + +static HRESULT RmuApplicationArrayAlloc( + __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications, + __inout LPUINT pcApplications, + __in DWORD dwProcessId, + __in FILETIME ProcessStartTime + ) +{ + HRESULT hr = S_OK; + RM_UNIQUE_PROCESS *pApplication = NULL; + + hr = MemEnsureArraySize(reinterpret_cast(prgApplications), *pcApplications + 1, sizeof(RM_UNIQUE_PROCESS), ARRAY_GROWTH_SIZE); + RmExitOnFailure(hr, "Failed to allocate memory for the application array."); + + pApplication = static_cast(&(*prgApplications)[*pcApplications]); + pApplication->dwProcessId = dwProcessId; + pApplication->ProcessStartTime = ProcessStartTime; + + ++(*pcApplications); + +LExit: + return hr; +} + +static HRESULT RmuApplicationArrayFree( + __in RM_UNIQUE_PROCESS *rgApplications + ) +{ + HRESULT hr = S_OK; + + hr = MemFree(rgApplications); + RmExitOnFailure(hr, "Failed to free memory for the application array."); + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/rssutil.cpp b/src/libs/dutil/WixToolset.DUtil/rssutil.cpp new file mode 100644 index 00000000..8f994dfc --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/rssutil.cpp @@ -0,0 +1,647 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define RssExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__) +#define RssExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__) +#define RssExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__) +#define RssExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__) +#define RssExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__) +#define RssExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RSSUTIL, e, x, s, __VA_ARGS__) +#define RssExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RSSUTIL, g, x, s, __VA_ARGS__) + +static HRESULT ParseRssDocument( + __in IXMLDOMDocument *pixd, + __out RSS_CHANNEL **ppChannel + ); +static HRESULT ParseRssChannel( + __in IXMLDOMNode *pixnChannel, + __out RSS_CHANNEL **ppChannel + ); +static HRESULT ParseRssItem( + __in IXMLDOMNode *pixnItem, + __in DWORD cItem, + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel + ); +static HRESULT ParseRssUnknownElement( + __in IXMLDOMNode *pNode, + __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement + ); +static HRESULT ParseRssUnknownAttribute( + __in IXMLDOMNode *pNode, + __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute + ); +static void FreeRssUnknownElementList( + __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement + ); +static void FreeRssUnknownAttributeList( + __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute + ); + + +/******************************************************************** + RssInitialize - Initialize RSS utilities. + +*********************************************************************/ +extern "C" HRESULT DAPI RssInitialize() +{ + return XmlInitialize(); +} + + +/******************************************************************** + RssUninitialize - Uninitialize RSS utilities. + +*********************************************************************/ +extern "C" void DAPI RssUninitialize() +{ + XmlUninitialize(); +} + + +/******************************************************************** + RssParseFromString - parses out an RSS channel from a string. + +*********************************************************************/ +extern "C" HRESULT DAPI RssParseFromString( + __in_z LPCWSTR wzRssString, + __out RSS_CHANNEL **ppChannel + ) +{ + Assert(wzRssString); + Assert(ppChannel); + + HRESULT hr = S_OK; + RSS_CHANNEL *pNewChannel = NULL; + IXMLDOMDocument *pixdRss = NULL; + + hr = XmlLoadDocument(wzRssString, &pixdRss); + RssExitOnFailure(hr, "Failed to load RSS string as XML document."); + + hr = ParseRssDocument(pixdRss, &pNewChannel); + RssExitOnFailure(hr, "Failed to parse RSS document."); + + *ppChannel = pNewChannel; + pNewChannel = NULL; + +LExit: + ReleaseObject(pixdRss); + + ReleaseRssChannel(pNewChannel); + + return hr; +} + + +/******************************************************************** + RssParseFromFile - parses out an RSS channel from a file path. + +*********************************************************************/ +extern "C" HRESULT DAPI RssParseFromFile( + __in_z LPCWSTR wzRssFile, + __out RSS_CHANNEL **ppChannel + ) +{ + Assert(wzRssFile); + Assert(ppChannel); + + HRESULT hr = S_OK; + RSS_CHANNEL *pNewChannel = NULL; + IXMLDOMDocument *pixdRss = NULL; + + hr = XmlLoadDocumentFromFile(wzRssFile, &pixdRss); + RssExitOnFailure(hr, "Failed to load RSS string as XML document."); + + hr = ParseRssDocument(pixdRss, &pNewChannel); + RssExitOnFailure(hr, "Failed to parse RSS document."); + + *ppChannel = pNewChannel; + pNewChannel = NULL; + +LExit: + ReleaseObject(pixdRss); + + ReleaseRssChannel(pNewChannel); + + return hr; +} + + +/******************************************************************** + RssFreeChannel - parses out an RSS channel from a string. + +*********************************************************************/ +extern "C" void DAPI RssFreeChannel( + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel + ) +{ + if (pChannel) + { + for (DWORD i = 0; i < pChannel->cItems; ++i) + { + ReleaseStr(pChannel->rgItems[i].wzTitle); + ReleaseStr(pChannel->rgItems[i].wzLink); + ReleaseStr(pChannel->rgItems[i].wzDescription); + ReleaseStr(pChannel->rgItems[i].wzGuid); + ReleaseStr(pChannel->rgItems[i].wzEnclosureUrl); + ReleaseStr(pChannel->rgItems[i].wzEnclosureType); + + FreeRssUnknownElementList(pChannel->rgItems[i].pUnknownElements); + } + + ReleaseStr(pChannel->wzTitle); + ReleaseStr(pChannel->wzLink); + ReleaseStr(pChannel->wzDescription); + FreeRssUnknownElementList(pChannel->pUnknownElements); + + MemFree(pChannel); + } +} + + +/******************************************************************** + ParseRssDocument - parses out an RSS channel from a loaded XML DOM document. + +*********************************************************************/ +static HRESULT ParseRssDocument( + __in IXMLDOMDocument *pixd, + __out RSS_CHANNEL **ppChannel + ) +{ + Assert(pixd); + Assert(ppChannel); + + HRESULT hr = S_OK; + IXMLDOMElement *pRssElement = NULL; + IXMLDOMNodeList *pChannelNodes = NULL; + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + + RSS_CHANNEL *pNewChannel = NULL; + + // + // Get the document element and start processing channels. + // + hr = pixd ->get_documentElement(&pRssElement); + RssExitOnFailure(hr, "failed get_documentElement in ParseRssDocument"); + + hr = pRssElement->get_childNodes(&pChannelNodes); + RssExitOnFailure(hr, "Failed to get child nodes of Rss Document element."); + + while (S_OK == (hr = XmlNextElement(pChannelNodes, &pNode, &bstrNodeName))) + { + if (0 == lstrcmpW(bstrNodeName, L"channel")) + { + hr = ParseRssChannel(pNode, &pNewChannel); + RssExitOnFailure(hr, "Failed to parse RSS channel."); + } + else if (0 == lstrcmpW(bstrNodeName, L"link")) + { + } + + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + + if (S_FALSE == hr) + { + hr = S_OK; + } + + *ppChannel = pNewChannel; + pNewChannel = NULL; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pChannelNodes); + ReleaseObject(pRssElement); + + ReleaseRssChannel(pNewChannel); + + return hr; +} + + +/******************************************************************** + ParseRssChannel - parses out an RSS channel from a loaded XML DOM element. + +*********************************************************************/ +static HRESULT ParseRssChannel( + __in IXMLDOMNode *pixnChannel, + __out RSS_CHANNEL **ppChannel + ) +{ + Assert(pixnChannel); + Assert(ppChannel); + + HRESULT hr = S_OK; + IXMLDOMNodeList *pNodeList = NULL; + + RSS_CHANNEL *pNewChannel = NULL; + long cItems = 0; + + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + + // + // First, calculate how many RSS items we're going to have and allocate + // the RSS_CHANNEL structure + // + hr = XmlSelectNodes(pixnChannel, L"item", &pNodeList); + RssExitOnFailure(hr, "Failed to select all RSS items in an RSS channel."); + + hr = pNodeList->get_length(&cItems); + RssExitOnFailure(hr, "Failed to count the number of RSS items in RSS channel."); + + pNewChannel = static_cast(MemAlloc(sizeof(RSS_CHANNEL) + sizeof(RSS_ITEM) * cItems, TRUE)); + RssExitOnNull(pNewChannel, hr, E_OUTOFMEMORY, "Failed to allocate RSS channel structure."); + + pNewChannel->cItems = cItems; + + // + // Process the elements under a channel now. + // + hr = pixnChannel->get_childNodes(&pNodeList); + RssExitOnFailure(hr, "Failed to get child nodes of RSS channel element."); + + cItems = 0; // reset the counter and use this to walk through the channel items + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (0 == lstrcmpW(bstrNodeName, L"title")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS channel title."); + + hr = StrAllocString(&pNewChannel->wzTitle, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS channel title."); + } + else if (0 == lstrcmpW(bstrNodeName, L"link")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS channel link."); + + hr = StrAllocString(&pNewChannel->wzLink, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS channel link."); + } + else if (0 == lstrcmpW(bstrNodeName, L"description")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS channel description."); + + hr = StrAllocString(&pNewChannel->wzDescription, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS channel description."); + } + else if (0 == lstrcmpW(bstrNodeName, L"ttl")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS channel description."); + + pNewChannel->dwTimeToLive = (DWORD)wcstoul(bstrNodeValue, NULL, 10); + } + else if (0 == lstrcmpW(bstrNodeName, L"item")) + { + hr = ParseRssItem(pNode, cItems, pNewChannel); + RssExitOnFailure(hr, "Failed to parse RSS item."); + + ++cItems; + } + else + { + hr = ParseRssUnknownElement(pNode, &pNewChannel->pUnknownElements); + RssExitOnFailure(hr, "Failed to parse unknown RSS channel element: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeValue); + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + + *ppChannel = pNewChannel; + pNewChannel = NULL; + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + + ReleaseRssChannel(pNewChannel); + + return hr; +} + + +/******************************************************************** + ParseRssItem - parses out an RSS item from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseRssItem( + __in IXMLDOMNode *pixnItem, + __in DWORD cItem, + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel + ) +{ + HRESULT hr = S_OK; + + RSS_ITEM *pItem = NULL; + IXMLDOMNodeList *pNodeList = NULL; + + IXMLDOMNode *pNode = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + + // + // First make sure we're dealing with a valid item. + // + if (pChannel->cItems <= cItem) + { + hr = E_UNEXPECTED; + RssExitOnFailure(hr, "Unexpected number of items parsed."); + } + + pItem = pChannel->rgItems + cItem; + + // + // Process the elements under an item now. + // + hr = pixnItem->get_childNodes(&pNodeList); + RssExitOnFailure(hr, "Failed to get child nodes of RSS item element."); + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName))) + { + if (0 == lstrcmpW(bstrNodeName, L"title")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS channel title."); + + hr = StrAllocString(&pItem->wzTitle, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS item title."); + } + else if (0 == lstrcmpW(bstrNodeName, L"link")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS channel link."); + + hr = StrAllocString(&pItem->wzLink, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS item link."); + } + else if (0 == lstrcmpW(bstrNodeName, L"description")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS item description."); + + hr = StrAllocString(&pItem->wzDescription, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS item description."); + } + else if (0 == lstrcmpW(bstrNodeName, L"guid")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS item guid."); + + hr = StrAllocString(&pItem->wzGuid, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS item guid."); + } + else if (0 == lstrcmpW(bstrNodeName, L"pubDate")) + { + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS item guid."); + + hr = TimeFromString(bstrNodeValue, &pItem->ftPublished); + RssExitOnFailure(hr, "Failed to convert RSS item time."); + } + else if (0 == lstrcmpW(bstrNodeName, L"enclosure")) + { + hr = XmlGetAttribute(pNode, L"url", &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS item enclosure url."); + + hr = StrAllocString(&pItem->wzEnclosureUrl, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS item enclosure url."); + ReleaseNullBSTR(bstrNodeValue); + + hr = XmlGetAttributeNumber(pNode, L"length", &pItem->dwEnclosureSize); + RssExitOnFailure(hr, "Failed to get RSS item enclosure length."); + + hr = XmlGetAttribute(pNode, L"type", &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get RSS item enclosure type."); + + hr = StrAllocString(&pItem->wzEnclosureType, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS item enclosure type."); + } + else + { + hr = ParseRssUnknownElement(pNode, &pItem->pUnknownElements); + RssExitOnFailure(hr, "Failed to parse unknown RSS item element: %ls", bstrNodeName); + } + + ReleaseNullBSTR(bstrNodeValue); + ReleaseNullBSTR(bstrNodeName); + ReleaseNullObject(pNode); + } + +LExit: + ReleaseBSTR(bstrNodeValue); + ReleaseBSTR(bstrNodeName); + ReleaseObject(pNode); + ReleaseObject(pNodeList); + + return hr; +} + + +/******************************************************************** + ParseRssUnknownElement - parses out an unknown item from the RSS feed from a loaded XML DOM node. + +*********************************************************************/ +static HRESULT ParseRssUnknownElement( + __in IXMLDOMNode *pNode, + __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement + ) +{ + Assert(ppUnknownElement); + + HRESULT hr = S_OK; + BSTR bstrNodeNamespace = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNode* pixnAttribute = NULL; + RSS_UNKNOWN_ELEMENT* pNewUnknownElement; + + pNewUnknownElement = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ELEMENT), TRUE)); + RssExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element."); + + hr = pNode->get_namespaceURI(&bstrNodeNamespace); + if (S_OK == hr) + { + hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0); + RssExitOnFailure(hr, "Failed to allocate RSS unknown element namespace."); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + RssExitOnFailure(hr, "Failed to get unknown element namespace."); + + hr = pNode->get_baseName(&bstrNodeName); + RssExitOnFailure(hr, "Failed to get unknown element name."); + + hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0); + RssExitOnFailure(hr, "Failed to allocate RSS unknown element name."); + + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get unknown element value."); + + hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS unknown element value."); + + hr = pNode->get_attributes(&pixnnmAttributes); + RssExitOnFailure(hr, "Failed get attributes on RSS unknown element."); + + while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute))) + { + hr = ParseRssUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes); + RssExitOnFailure(hr, "Failed to parse attribute on RSS unknown element."); + + ReleaseNullObject(pixnAttribute); + } + + if (S_FALSE == hr) + { + hr = S_OK; + } + RssExitOnFailure(hr, "Failed to enumerate all attributes on RSS unknown element."); + + RSS_UNKNOWN_ELEMENT** ppTail = ppUnknownElement; + while (*ppTail) + { + ppTail = &(*ppTail)->pNext; + } + + *ppTail = pNewUnknownElement; + pNewUnknownElement = NULL; + +LExit: + FreeRssUnknownElementList(pNewUnknownElement); + + ReleaseBSTR(bstrNodeNamespace); + ReleaseBSTR(bstrNodeName); + ReleaseBSTR(bstrNodeValue); + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixnAttribute); + + return hr; +} + + +/******************************************************************** + ParseRssUnknownAttribute - parses out attribute from an unknown element + +*********************************************************************/ +static HRESULT ParseRssUnknownAttribute( + __in IXMLDOMNode *pNode, + __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute + ) +{ + Assert(ppUnknownAttribute); + + HRESULT hr = S_OK; + BSTR bstrNodeNamespace = NULL; + BSTR bstrNodeName = NULL; + BSTR bstrNodeValue = NULL; + RSS_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute; + + pNewUnknownAttribute = static_cast(MemAlloc(sizeof(RSS_UNKNOWN_ATTRIBUTE), TRUE)); + RssExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute."); + + hr = pNode->get_namespaceURI(&bstrNodeNamespace); + if (S_OK == hr) + { + hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0); + RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute namespace."); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + RssExitOnFailure(hr, "Failed to get unknown attribute namespace."); + + hr = pNode->get_baseName(&bstrNodeName); + RssExitOnFailure(hr, "Failed to get unknown attribute name."); + + hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0); + RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute name."); + + hr = XmlGetText(pNode, &bstrNodeValue); + RssExitOnFailure(hr, "Failed to get unknown attribute value."); + + hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0); + RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute value."); + + RSS_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute; + while (*ppTail) + { + ppTail = &(*ppTail)->pNext; + } + + *ppTail = pNewUnknownAttribute; + pNewUnknownAttribute = NULL; + +LExit: + FreeRssUnknownAttributeList(pNewUnknownAttribute); + + ReleaseBSTR(bstrNodeNamespace); + ReleaseBSTR(bstrNodeName); + ReleaseBSTR(bstrNodeValue); + + return hr; +} + + +/******************************************************************** + FreeRssUnknownElement - releases all of the memory used by a list of unknown elements + +*********************************************************************/ +static void FreeRssUnknownElementList( + __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement + ) +{ + while (pUnknownElement) + { + RSS_UNKNOWN_ELEMENT* pFree = pUnknownElement; + pUnknownElement = pUnknownElement->pNext; + + FreeRssUnknownAttributeList(pFree->pAttributes); + ReleaseStr(pFree->wzNamespace); + ReleaseStr(pFree->wzElement); + ReleaseStr(pFree->wzValue); + MemFree(pFree); + } +} + + +/******************************************************************** + FreeRssUnknownAttribute - releases all of the memory used by a list of unknown attributes + +*********************************************************************/ +static void FreeRssUnknownAttributeList( + __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute + ) +{ + while (pUnknownAttribute) + { + RSS_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute; + pUnknownAttribute = pUnknownAttribute->pNext; + + ReleaseStr(pFree->wzNamespace); + ReleaseStr(pFree->wzAttribute); + ReleaseStr(pFree->wzValue); + MemFree(pFree); + } +} diff --git a/src/libs/dutil/WixToolset.DUtil/sceutil.cpp b/src/libs/dutil/WixToolset.DUtil/sceutil.cpp new file mode 100644 index 00000000..cdb1623b --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/sceutil.cpp @@ -0,0 +1,2489 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#include "sceutil.h" + +// Limit is documented as 4 GB, but for some reason the API's don't let us specify anything above 4091 MB. +#define MAX_SQLCE_DATABASE_SIZE 4091 + +// In case of some older versions of sqlce_oledb.h don't have these definitions, define some types. +#ifndef DBTYPEFOR_DBLENGTH +#ifdef _WIN64 +#define SKIP_SCE_COMPILE +#else +#define SCE_32BIT_ONLY +typedef DWORD DBLENGTH; +typedef LONG DBROWOFFSET; +typedef LONG DBROWCOUNT; +typedef DWORD DBCOUNTITEM; +typedef DWORD DBORDINAL; +typedef LONG DB_LORDINAL; +typedef DWORD DBBKMARK; +typedef DWORD DBBYTEOFFSET; +typedef DWORD DBREFCOUNT; +typedef DWORD DB_UPARAMS; +typedef LONG DB_LPARAMS; +typedef DWORD DBHASHVALUE; +typedef DWORD DB_DWRESERVE; +typedef LONG DB_LRESERVE; +typedef DWORD DB_URESERVE; +#endif + +#endif + +#ifndef SKIP_SCE_COMPILE // If the sce headers don't support 64-bit, don't build for 64-bit + +// structs +struct SCE_DATABASE_INTERNAL +{ + // In case we call DllGetClassObject on a specific file + HMODULE hSqlCeDll; + + volatile LONG dwTransactionRefcount; + IDBInitialize *pIDBInitialize; + IDBCreateSession *pIDBCreateSession; + ITransactionLocal *pITransactionLocal; + IDBProperties *pIDBProperties; + IOpenRowset *pIOpenRowset; + ISessionProperties *pISessionProperties; + + BOOL fChanges; // This database has changed + BOOL fPendingChanges; // Some changes are pending, upon transaction commit + BOOL fRollbackTransaction; // If this flag is true, the current transaction was requested to be rolled back + BOOL fTransactionBadState; // If this flag is true, we were unable to get out of a transaction, so starting a new transaction should fail + + // If the database was opened as read-only, we copied it here - so delete it on close + LPWSTR sczTempDbFile; +}; + +struct SCE_ROW +{ + SCE_DATABASE_INTERNAL *pDatabaseInternal; + + SCE_TABLE_SCHEMA *pTableSchema; + IRowset *pIRowset; + HROW hRow; + BOOL fInserting; + + DWORD dwBindingIndex; + DBBINDING *rgBinding; + SIZE_T cbOffset; + BYTE *pbData; +}; + +struct SCE_QUERY +{ + SCE_TABLE_SCHEMA *pTableSchema; + SCE_INDEX_SCHEMA *pIndexSchema; + SCE_DATABASE_INTERNAL *pDatabaseInternal; + + // Accessor build-up members + DWORD dwBindingIndex; + DBBINDING *rgBinding; + SIZE_T cbOffset; + BYTE *pbData; +}; + +struct SCE_QUERY_RESULTS +{ + SCE_DATABASE_INTERNAL *pDatabaseInternal; + IRowset *pIRowset; + SCE_TABLE_SCHEMA *pTableSchema; +}; + +extern const int SCE_ROW_HANDLE_BYTES = sizeof(SCE_ROW); +extern const int SCE_QUERY_HANDLE_BYTES = sizeof(SCE_QUERY); +extern const int SCE_QUERY_RESULTS_HANDLE_BYTES = sizeof(SCE_QUERY_RESULTS); + +// The following is the internal Sce-maintained table to tell the identifier and version of the schema +const SCE_COLUMN_SCHEMA SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA[] = +{ + { + L"AppIdentifier", + DBTYPE_WSTR, + 0, + FALSE, + TRUE, + FALSE, + NULL, + 0, + 0 + }, + { + L"Version", + DBTYPE_I4, + 0, + FALSE, + FALSE, + FALSE, + NULL, + 0, + 0 + } +}; + +const SCE_TABLE_SCHEMA SCE_INTERNAL_VERSION_TABLE_SCHEMA[] = +{ + L"SceSchemaTablev1", + _countof(SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA), + (SCE_COLUMN_SCHEMA *)SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA, + 0, + NULL, + NULL, + NULL +}; + +// internal function declarations + +// Creates an instance of SQL Server CE object, returning IDBInitialize object +// If a file path is provided in wzSqlCeDllPath parameter, it calls DllGetClassObject +// on that file specifically. Otherwise it calls CoCreateInstance +static HRESULT CreateSqlCe( + __in_z_opt LPCWSTR wzSqlCeDllPath, + __out IDBInitialize **ppIDBInitialize, + __out_opt HMODULE *phSqlCeDll + ); +static HRESULT RunQuery( + __in BOOL fRange, + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle, + __out SCE_QUERY_RESULTS **ppsqrhHandle + ); +static HRESULT FillOutColumnDescFromSchema( + __in const SCE_COLUMN_SCHEMA *pSchema, + __out DBCOLUMNDESC pColumnDesc + ); +static HRESULT EnsureSchema( + __in SCE_DATABASE *pDatabase, + __in SCE_DATABASE_SCHEMA *pDatabaseSchema + ); +static HRESULT OpenSchema( + __in SCE_DATABASE *pDatabase, + __in SCE_DATABASE_SCHEMA *pdsSchema + ); +static HRESULT SetColumnValue( + __in const SCE_TABLE_SCHEMA *pTableSchema, + __in DWORD dwColumnIndex, + __in_bcount_opt(cbSize) const BYTE *pbData, + __in SIZE_T cbSize, + __inout DBBINDING *pBinding, + __inout SIZE_T *pcbOffset, + __inout BYTE **ppbBuffer + ); +static HRESULT GetColumnValue( + __in SCE_ROW *pRow, + __in DWORD dwColumnIndex, + __out_opt BYTE **ppbData, + __out SIZE_T *pcbSize + ); +static HRESULT GetColumnValueFixed( + __in SCE_ROW *pRow, + __in DWORD dwColumnIndex, + __in DWORD cbSize, + __out BYTE *pbData + ); +static HRESULT EnsureLocalColumnConstraints( + __in ITableDefinition *pTableDefinition, + __in DBID *pTableID, + __in SCE_TABLE_SCHEMA *pTableSchema + ); +static HRESULT EnsureForeignColumnConstraints( + __in ITableDefinition *pTableDefinition, + __in DBID *pTableID, + __in SCE_TABLE_SCHEMA *pTableSchema, + __in SCE_DATABASE_SCHEMA *pDatabaseSchema + ); +static HRESULT SetSessionProperties( + __in ISessionProperties *pISessionProperties + ); +static HRESULT GetDatabaseSchemaInfo( + __in SCE_DATABASE *pDatabase, + __out LPWSTR *psczSchemaType, + __out DWORD *pdwVersion + ); +static HRESULT SetDatabaseSchemaInfo( + __in SCE_DATABASE *pDatabase, + __in LPCWSTR wzSchemaType, + __in DWORD dwVersion + ); +static void ReleaseDatabase( + SCE_DATABASE *pDatabase + ); +static void ReleaseDatabaseInternal( + SCE_DATABASE_INTERNAL *pDatabaseInternal + ); + +// function definitions +extern "C" HRESULT DAPI SceCreateDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __out SCE_DATABASE **ppDatabase + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDirectory = NULL; + SCE_DATABASE *pNewSceDatabase = NULL; + SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL; + IDBDataSourceAdmin *pIDBDataSourceAdmin = NULL; + DBPROPSET rgdbpDataSourcePropSet[2] = { }; + DBPROP rgdbpDataSourceProp[2] = { }; + DBPROP rgdbpDataSourceSsceProp[1] = { }; + + pNewSceDatabase = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE), TRUE)); + ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct"); + + pNewSceDatabaseInternal = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE)); + ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct"); + + pNewSceDatabase->sdbHandle = reinterpret_cast(pNewSceDatabaseInternal); + + hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll); + ExitOnFailure(hr, "Failed to get IDBInitialize interface"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBDataSourceAdmin, reinterpret_cast(&pIDBDataSourceAdmin)); + ExitOnFailure(hr, "Failed to get IDBDataSourceAdmin interface"); + + hr = PathGetDirectory(sczFile, &sczDirectory); + ExitOnFailure(hr, "Failed to get directory portion of path: %ls", sczFile); + + hr = DirEnsureExists(sczDirectory, NULL); + ExitOnFailure(hr, "Failed to ensure directory exists: %ls", sczDirectory); + + rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE; + rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[0].vValue.vt = VT_BSTR; + rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(sczFile); + + rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE; + rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[1].vValue.vt = VT_I4; + rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE; + + // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT + rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT; + rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; + rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp); + + rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE; + rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4; + rgdbpDataSourceSsceProp[0].vValue.intVal = MAX_SQLCE_DATABASE_SIZE; + + rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT; + rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp; + rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp); + + hr = pIDBDataSourceAdmin->CreateDataSource(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet, NULL, IID_IUnknown, NULL); + ExitOnFailure(hr, "Failed to create data source"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast(&pNewSceDatabaseInternal->pIDBProperties)); + ExitOnFailure(hr, "Failed to get IDBProperties interface"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast(&pNewSceDatabaseInternal->pIDBCreateSession)); + ExitOnFailure(hr, "Failed to get IDBCreateSession interface"); + + hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast(&pNewSceDatabaseInternal->pISessionProperties)); + ExitOnFailure(hr, "Failed to get ISessionProperties interface"); + + hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties); + ExitOnFailure(hr, "Failed to set session properties"); + + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast(&pNewSceDatabaseInternal->pIOpenRowset)); + ExitOnFailure(hr, "Failed to get IOpenRowset interface"); + + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast(&pNewSceDatabaseInternal->pITransactionLocal)); + ExitOnFailure(hr, "Failed to get ITransactionLocal interface"); + + *ppDatabase = pNewSceDatabase; + pNewSceDatabase = NULL; + +LExit: + ReleaseStr(sczDirectory); + ReleaseObject(pIDBDataSourceAdmin); + ReleaseDatabase(pNewSceDatabase); + ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal); + + return hr; +} + +extern "C" HRESULT DAPI SceOpenDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __in LPCWSTR wzExpectedSchemaType, + __in DWORD dwExpectedVersion, + __out SCE_DATABASE **ppDatabase, + __in BOOL fReadOnly + ) +{ + HRESULT hr = S_OK; + DWORD dwVersionFound = 0; + WCHAR wzTempDbFile[MAX_PATH]; + LPCWSTR wzPathToOpen = NULL; + LPWSTR sczSchemaType = NULL; + SCE_DATABASE *pNewSceDatabase = NULL; + SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL; + DBPROPSET rgdbpDataSourcePropSet[2] = { }; + DBPROP rgdbpDataSourceProp[2] = { }; + DBPROP rgdbpDataSourceSsceProp[1] = { }; + + pNewSceDatabase = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE), TRUE)); + ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct"); + + pNewSceDatabaseInternal = reinterpret_cast(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE)); + ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct"); + + pNewSceDatabase->sdbHandle = reinterpret_cast(pNewSceDatabaseInternal); + + hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll); + ExitOnFailure(hr, "Failed to get IDBInitialize interface"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast(&pNewSceDatabaseInternal->pIDBProperties)); + ExitOnFailure(hr, "Failed to get IDBProperties interface"); + + // TODO: had trouble getting SQL CE to read a file read-only, so we're copying it to a temp path for now. + if (fReadOnly) + { + hr = DirCreateTempPath(PathFile(sczFile), (LPWSTR)wzTempDbFile, _countof(wzTempDbFile)); + ExitOnFailure(hr, "Failed to get temp path"); + + hr = FileEnsureCopy(sczFile, (LPCWSTR)wzTempDbFile, TRUE); + ExitOnFailure(hr, "Failed to copy file to temp path"); + + hr = StrAllocString(&pNewSceDatabaseInternal->sczTempDbFile, (LPCWSTR)wzTempDbFile, 0); + ExitOnFailure(hr, "Failed to copy temp db file path"); + + wzPathToOpen = (LPCWSTR)wzTempDbFile; + } + else + { + wzPathToOpen = sczFile; + } + + rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE; + rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[0].vValue.vt = VT_BSTR; + rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(wzPathToOpen); + + rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE; + rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[1].vValue.vt = VT_I4; + rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE; + + // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT + rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT; + rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; + rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp); + + rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE; + rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4; + rgdbpDataSourceSsceProp[0].vValue.lVal = MAX_SQLCE_DATABASE_SIZE; + + rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT; + rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp; + rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp); + + hr = pNewSceDatabaseInternal->pIDBProperties->SetProperties(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet); + ExitOnFailure(hr, "Failed to set initial properties to open database"); + + hr = pNewSceDatabaseInternal->pIDBInitialize->Initialize(); + ExitOnFailure(hr, "Failed to open database: %ls", sczFile); + + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast(&pNewSceDatabaseInternal->pIDBCreateSession)); + ExitOnFailure(hr, "Failed to get IDBCreateSession interface"); + + hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast(&pNewSceDatabaseInternal->pISessionProperties)); + ExitOnFailure(hr, "Failed to get ISessionProperties interface"); + + hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties); + ExitOnFailure(hr, "Failed to set session properties"); + + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast(&pNewSceDatabaseInternal->pIOpenRowset)); + ExitOnFailure(hr, "Failed to get IOpenRowset interface"); + + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast(&pNewSceDatabaseInternal->pITransactionLocal)); + ExitOnFailure(hr, "Failed to get ITransactionLocal interface"); + + hr = GetDatabaseSchemaInfo(pNewSceDatabase, &sczSchemaType, &dwVersionFound); + ExitOnFailure(hr, "Failed to find schema version of database"); + + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczSchemaType, -1, wzExpectedSchemaType, -1)) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_FILE_TYPE); + ExitOnRootFailure(hr, "Tried to open wrong database type - expected type %ls, found type %ls", wzExpectedSchemaType, sczSchemaType); + } + else if (dwVersionFound != dwExpectedVersion) + { + hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION); + ExitOnRootFailure(hr, "Tried to open wrong database schema version - expected version %u, found version %u", dwExpectedVersion, dwVersionFound); + } + + *ppDatabase = pNewSceDatabase; + pNewSceDatabase = NULL; + +LExit: + ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal); + ReleaseStr(sczSchemaType); + ReleaseDatabase(pNewSceDatabase); + + return hr; +} + +extern "C" HRESULT DAPI SceEnsureDatabase( + __in_z LPCWSTR sczFile, + __in_z_opt LPCWSTR wzSqlCeDllPath, + __in LPCWSTR wzSchemaType, + __in DWORD dwExpectedVersion, + __in SCE_DATABASE_SCHEMA *pdsSchema, + __out SCE_DATABASE **ppDatabase + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE *pDatabase = NULL; + + if (FileExistsEx(sczFile, NULL)) + { + hr = SceOpenDatabase(sczFile, wzSqlCeDllPath, wzSchemaType, dwExpectedVersion, &pDatabase, FALSE); + ExitOnFailure(hr, "Failed to open database while ensuring database exists: %ls", sczFile); + } + else + { + hr = SceCreateDatabase(sczFile, wzSqlCeDllPath, &pDatabase); + ExitOnFailure(hr, "Failed to create database while ensuring database exists: %ls", sczFile); + + hr = SetDatabaseSchemaInfo(pDatabase, wzSchemaType, dwExpectedVersion); + ExitOnFailure(hr, "Failed to set schema version of database"); + } + + hr = EnsureSchema(pDatabase, pdsSchema); + ExitOnFailure(hr, "Failed to ensure schema is correct in database: %ls", sczFile); + + // Keep a pointer to the schema in the SCE_DATABASE object for future reference + pDatabase->pdsSchema = pdsSchema; + + *ppDatabase = pDatabase; + pDatabase = NULL; + +LExit: + ReleaseDatabase(pDatabase); + + return hr; +} + +extern "C" HRESULT DAPI SceIsTableEmpty( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out BOOL *pfEmpty + ) +{ + HRESULT hr = S_OK; + SCE_ROW_HANDLE row = NULL; + + hr = SceGetFirstRow(pDatabase, dwTableIndex, &row); + if (E_NOTFOUND == hr) + { + *pfEmpty = TRUE; + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to get first row while checking if table is empty"); + + *pfEmpty = FALSE; + +LExit: + ReleaseSceRow(row); + + return hr; +} + +extern "C" HRESULT DAPI SceGetFirstRow( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + DBCOUNTITEM cRowsObtained = 0; + HROW hRow = DB_NULL_HROW; + HROW *phRow = &hRow; + SCE_ROW *pRow = NULL; + SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); + + hr = pTable->pIRowset->RestartPosition(DB_NULL_HCHAPTER); + ExitOnFailure(hr, "Failed to reset IRowset position to beginning"); + + hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); + if (DB_S_ENDOFROWSET == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to get next first row"); + + pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); + + pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + pRow->hRow = hRow; + pRow->pTableSchema = pTable; + pRow->pIRowset = pTable->pIRowset; + pRow->pIRowset->AddRef(); + + *pRowHandle = reinterpret_cast(pRow); + +LExit: + return hr; +} + +HRESULT DAPI SceGetNextRow( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + DBCOUNTITEM cRowsObtained = 0; + HROW hRow = DB_NULL_HROW; + HROW *phRow = &hRow; + SCE_ROW *pRow = NULL; + SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); + + hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); + if (DB_S_ENDOFROWSET == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to get next first row"); + + pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); + + pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + pRow->hRow = hRow; + pRow->pTableSchema = pTable; + pRow->pIRowset = pTable->pIRowset; + pRow->pIRowset->AddRef(); + + *pRowHandle = reinterpret_cast(pRow); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceBeginTransaction( + __in SCE_DATABASE *pDatabase + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + + if (pDatabaseInternal->fTransactionBadState) + { + // We're in a hosed transaction state - we can't start a new one + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_RECOVERY_FAILURE)); + } + + ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount); + + if (1 == pDatabaseInternal->dwTransactionRefcount) + { + hr = pDatabaseInternal->pITransactionLocal->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL); + ExitOnFailure(hr, "Failed to start transaction"); + } + +LExit: + if (FAILED(hr)) + { + ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); + } + + return hr; +} + +extern "C" HRESULT DAPI SceCommitTransaction( + __in SCE_DATABASE *pDatabase + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + Assert(0 < pDatabaseInternal->dwTransactionRefcount); + + ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); + + if (0 == pDatabaseInternal->dwTransactionRefcount) + { + if (pDatabaseInternal->fRollbackTransaction) + { + hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE); + ExitOnFailure(hr, "Failed to abort transaction"); + } + else + { + hr = pDatabaseInternal->pITransactionLocal->Commit(FALSE, XACTTC_SYNC, 0); + ExitOnFailure(hr, "Failed to commit transaction"); + } + + if (pDatabaseInternal->fPendingChanges) + { + pDatabaseInternal->fPendingChanges = FALSE; + pDatabaseInternal->fChanges = TRUE; + } + + pDatabaseInternal->fRollbackTransaction = FALSE; + } + +LExit: + // If we tried to commit and failed, the caller should subsequently call rollback + if (FAILED(hr)) + { + ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount); + } + + return hr; +} + +extern "C" HRESULT DAPI SceRollbackTransaction( + __in SCE_DATABASE *pDatabase + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + Assert(0 < pDatabaseInternal->dwTransactionRefcount); + + ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount); + + if (0 == pDatabaseInternal->dwTransactionRefcount) + { + hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE); + ExitOnFailure(hr, "Failed to abort transaction"); + pDatabaseInternal->fPendingChanges = FALSE; + + pDatabaseInternal->fRollbackTransaction = FALSE; + } + else + { + pDatabaseInternal->fRollbackTransaction = TRUE; + } + +LExit: + // We're just in a bad state now. Don't increment the transaction refcount (what is the user going to do - call us again?) + // but mark the database as bad so the user gets an error if they try to start a new transaction. + if (FAILED(hr)) + { + TraceError(hr, "Failed to rollback transaction"); + pDatabaseInternal->fTransactionBadState = TRUE; + } + + return hr; +} + +extern "C" HRESULT DAPI SceDeleteRow( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(*pRowHandle); + IRowsetChange *pIRowsetChange = NULL; + DBROWSTATUS rowStatus = DBROWSTATUS_S_OK; + + hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pIRowsetChange)); + ExitOnFailure(hr, "Failed to get IRowsetChange interface"); + + hr = pIRowsetChange->DeleteRows(DB_NULL_HCHAPTER, 1, &pRow->hRow, &rowStatus); + ExitOnFailure(hr, "Failed to delete row with status: %u", rowStatus); + + ReleaseNullSceRow(*pRowHandle); + +LExit: + ReleaseObject(pIRowsetChange); + + return hr; +} + +extern "C" HRESULT DAPI ScePrepareInsert( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = NULL; + + pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); + + pRow->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + pRow->hRow = DB_NULL_HROW; + pRow->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); + pRow->pIRowset = pRow->pTableSchema->pIRowset; + pRow->pIRowset->AddRef(); + pRow->fInserting = TRUE; + + *pRowHandle = reinterpret_cast(pRow); + pRow = NULL; + +LExit: + ReleaseSceRow(pRow); + + return hr; +} + +extern "C" HRESULT DAPI SceFinishUpdate( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + IAccessor *pIAccessor = NULL; + IRowsetChange *pIRowsetChange = NULL; + DBBINDSTATUS *rgBindStatus = NULL; + HACCESSOR hAccessor = DB_NULL_HACCESSOR; + HROW hRow = DB_NULL_HROW; + + hr = pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); + ExitOnFailure(hr, "Failed to get IAccessor interface"); + +// This can be used when stepping through the debugger to see bind failures +#ifdef DEBUG + if (0 < pRow->dwBindingIndex) + { + hr = MemEnsureArraySize(reinterpret_cast(&rgBindStatus), pRow->dwBindingIndex, sizeof(DBBINDSTATUS), 0); + ExitOnFailure(hr, "Failed to ensure binding status array size"); + } +#endif + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pRow->dwBindingIndex, pRow->rgBinding, 0, &hAccessor, rgBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pIRowsetChange)); + ExitOnFailure(hr, "Failed to get IRowsetChange interface"); + + if (pRow->fInserting) + { + hr = pIRowsetChange->InsertRow(DB_NULL_HCHAPTER, hAccessor, pRow->pbData, &hRow); + ExitOnFailure(hr, "Failed to insert new row"); + + pRow->hRow = hRow; + ReleaseNullObject(pRow->pIRowset); + pRow->pIRowset = pRow->pTableSchema->pIRowset; + pRow->pIRowset->AddRef(); + } + else + { + hr = pIRowsetChange->SetData(pRow->hRow, hAccessor, pRow->pbData); + ExitOnFailure(hr, "Failed to update existing row"); + } + + if (0 < pRow->pDatabaseInternal->dwTransactionRefcount) + { + pRow->pDatabaseInternal->fPendingChanges = TRUE; + } + else + { + pRow->pDatabaseInternal->fChanges = TRUE; + } + +LExit: + if (DB_NULL_HACCESSOR != hAccessor) + { + pIAccessor->ReleaseAccessor(hAccessor, NULL); + } + ReleaseMem(rgBindStatus); + ReleaseObject(pIAccessor); + ReleaseObject(pIRowsetChange); + + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnBinary( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set binary, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, pbBuffer, cbBuffer, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as binary"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnDword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const DWORD dwValue + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set dword, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&dwValue), 4, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as binary"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnQword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const DWORD64 qwValue + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set qword, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&qwValue), 8, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as qword"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnBool( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const BOOL fValue + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + short int sValue = fValue ? 0xFFFF : 0x0000; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set bool, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&sValue), 2, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as binary"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnString( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in_z_opt LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + SIZE_T cbSize = (NULL == wzValue) ? 0 : ((lstrlenW(wzValue) + 1) * sizeof(WCHAR)); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set string, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(wzValue), cbSize, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as string: %ls", wzValue); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnNull( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set empty, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, NULL, 0, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as empty value"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceSetColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle, + __in DWORD dwColumnIndex, + __in const SYSTEMTIME *pst + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + DBTIMESTAMP dbTimeStamp = { }; + + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set systemtime, columns: %u", pRow->pTableSchema->cColumns); + + if (NULL == pRow->rgBinding) + { + pRow->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer"); + } + + dbTimeStamp.year = pst->wYear; + dbTimeStamp.month = pst->wMonth; + dbTimeStamp.day = pst->wDay; + dbTimeStamp.hour = pst->wHour; + dbTimeStamp.minute = pst->wMinute; + dbTimeStamp.second = pst->wSecond; + // Don't use .fraction because milliseconds are not reliable in SQL CE. They are rounded to the nearest 1/300th of a second, + // and it is not supported (or at least I can't figure out how) to query for an exact timestamp if milliseconds + // are involved (even when rounded the way SQL CE returns them). + + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast(&dbTimeStamp), sizeof(dbTimeStamp), &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData); + ExitOnFailure(hr, "Failed to set column value as DBTIMESTAMP"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnBinary( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out_opt BYTE **ppbBuffer, + __inout SIZE_T *pcbBuffer + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + + hr = GetColumnValue(pRow, dwColumnIndex, ppbBuffer, pcbBuffer); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get binary data out of column"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnDword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out DWORD *pdwValue + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + + hr = GetColumnValueFixed(pRow, dwColumnIndex, 4, reinterpret_cast(pdwValue)); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get dword data out of column"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnQword( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __in DWORD64 *pqwValue + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + + hr = GetColumnValueFixed(pRow, dwColumnIndex, 8, reinterpret_cast(pqwValue)); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get qword data out of column"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnBool( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out BOOL *pfValue + ) +{ + HRESULT hr = S_OK; + short int sValue = 0; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + + hr = GetColumnValueFixed(pRow, dwColumnIndex, 2, reinterpret_cast(&sValue)); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get data out of column"); + + if (sValue == 0x0000) + { + *pfValue = FALSE; + } + else + { + *pfValue = TRUE; + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnString( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out_z LPWSTR *psczValue + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + SIZE_T cbSize = 0; + + hr = GetColumnValue(pRow, dwColumnIndex, reinterpret_cast(psczValue), &cbSize); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get string data out of column"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI SceGetColumnSystemTime( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle, + __in DWORD dwColumnIndex, + __out SYSTEMTIME *pst + ) +{ + HRESULT hr = S_OK; + SCE_ROW *pRow = reinterpret_cast(rowReadHandle); + DBTIMESTAMP dbTimeStamp = { }; + + hr = GetColumnValueFixed(pRow, dwColumnIndex, sizeof(dbTimeStamp), reinterpret_cast(&dbTimeStamp)); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get string data out of column"); + + pst->wYear = dbTimeStamp.year; + pst->wMonth = dbTimeStamp.month; + pst->wDay = dbTimeStamp.day; + pst->wHour = dbTimeStamp.hour; + pst->wMinute = dbTimeStamp.minute; + pst->wSecond = dbTimeStamp.second; + +LExit: + return hr; +} + +extern "C" void DAPI SceCloseTable( + __in SCE_TABLE_SCHEMA *pTable + ) +{ + ReleaseNullObject(pTable->pIRowsetChange); + ReleaseNullObject(pTable->pIRowset); +} + +extern "C" BOOL DAPI SceDatabaseChanged( + __in SCE_DATABASE *pDatabase + ) +{ + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + + return pDatabaseInternal->fChanges; +} + +void DAPI SceResetDatabaseChanged( + __in SCE_DATABASE *pDatabase + ) +{ + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + + pDatabaseInternal->fChanges = FALSE; +} + +extern "C" HRESULT DAPI SceCloseDatabase( + __in SCE_DATABASE *pDatabase + ) +{ + HRESULT hr = S_OK; + + ReleaseDatabase(pDatabase); + + return hr; +} + +extern "C" HRESULT DAPI SceBeginQuery( + __in SCE_DATABASE *pDatabase, + __in DWORD dwTableIndex, + __in DWORD dwIndex, + __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + SCE_QUERY *psq = static_cast(MemAlloc(sizeof(SCE_QUERY), TRUE)); + ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate new sce query"); + + psq->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]); + psq->pIndexSchema = &(psq->pTableSchema->rgIndexes[dwIndex]); + psq->pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + + hr = ::SizeTMult(sizeof(DBBINDING), psq->pTableSchema->cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to begin query, columns: %u", psq->pTableSchema->cColumns); + + psq->rgBinding = static_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for new sce query"); + + *psqhHandle = static_cast(psq); + psq = NULL; + +LExit: + ReleaseSceQuery(psq); + + return hr; +} + +HRESULT DAPI SceSetQueryColumnBinary( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in_bcount(cbBuffer) const BYTE* pbBuffer, + __in SIZE_T cbBuffer + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], pbBuffer, cbBuffer, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as binary of size: %u", cbBuffer); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnDword( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const DWORD dwValue + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&dwValue), 4, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as dword"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnQword( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const DWORD64 qwValue + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&qwValue), 8, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as qword"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnBool( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const BOOL fValue + ) +{ + HRESULT hr = S_OK; + short int sValue = fValue ? 1 : 0; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&sValue), 2, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as boolean"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnString( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in_z_opt LPCWSTR wzString + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + SIZE_T cbSize = (NULL == wzString) ? 0 : ((lstrlenW(wzString) + 1) * sizeof(WCHAR)); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(wzString), cbSize, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as string"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnSystemTime( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle, + __in const SYSTEMTIME *pst + ) +{ + HRESULT hr = S_OK; + DBTIMESTAMP dbTimeStamp = { }; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + dbTimeStamp.year = pst->wYear; + dbTimeStamp.month = pst->wMonth; + dbTimeStamp.day = pst->wDay; + dbTimeStamp.hour = pst->wHour; + dbTimeStamp.minute = pst->wMinute; + dbTimeStamp.second = pst->wSecond; + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast(&dbTimeStamp), sizeof(dbTimeStamp), &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as DBTIMESTAMP"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceSetQueryColumnEmpty( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle + ) +{ + HRESULT hr = S_OK; + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], NULL, 0, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData); + ExitOnFailure(hr, "Failed to set query column value as empty value"); + + ++(pQuery->dwBindingIndex); + +LExit: + return hr; +} + +HRESULT DAPI SceRunQueryExact( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE *psqhHandle, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + SCE_QUERY_RESULTS *pQueryResults = NULL; + + hr = RunQuery(FALSE, *psqhHandle, &pQueryResults); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to run query exact"); + + hr = SceGetNextResultRow(reinterpret_cast(pQueryResults), pRowHandle); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get next row out of results"); + +LExit: + ReleaseNullSceQuery(*psqhHandle); + ReleaseSceQueryResults(pQueryResults); + + return hr; +} + +extern "C" HRESULT DAPI SceRunQueryRange( + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle, + __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle + ) +{ + HRESULT hr = S_OK; + SCE_QUERY_RESULTS **ppQueryResults = reinterpret_cast(psqrhHandle); + + hr = RunQuery(TRUE, *psqhHandle, ppQueryResults); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to run query for range"); + +LExit: + ReleaseNullSceQuery(*psqhHandle); + + return hr; +} + +extern "C" HRESULT DAPI SceGetNextResultRow( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle, + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle + ) +{ + HRESULT hr = S_OK; + HROW hRow = DB_NULL_HROW; + HROW *phRow = &hRow; + DBCOUNTITEM cRowsObtained = 0; + SCE_ROW *pRow = NULL; + SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast(sqrhHandle); + + Assert(pRowHandle && (*pRowHandle == NULL)); + + hr = pQueryResults->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow); + if (DB_S_ENDOFROWSET == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to get next first row"); + + pRow = reinterpret_cast(MemAlloc(sizeof(SCE_ROW), TRUE)); + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct"); + + pRow->pDatabaseInternal = reinterpret_cast(pQueryResults->pDatabaseInternal); + pRow->hRow = hRow; + pRow->pTableSchema = pQueryResults->pTableSchema; + pRow->pIRowset = pQueryResults->pIRowset; + pRow->pIRowset->AddRef(); + + *pRowHandle = reinterpret_cast(pRow); + pRow = NULL; + hRow = DB_NULL_HROW; + +LExit: + if (DB_NULL_HROW != hRow) + { + pQueryResults->pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL); + } + ReleaseSceRow(pRow); + + return hr; +} + +extern "C" void DAPI SceFreeRow( + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle + ) +{ + SCE_ROW *pRow = reinterpret_cast(rowHandle); + + if (DB_NULL_HROW != pRow->hRow) + { + pRow->pIRowset->ReleaseRows(1, &pRow->hRow, NULL, NULL, NULL); + } + ReleaseObject(pRow->pIRowset); + ReleaseMem(pRow->rgBinding); + ReleaseMem(pRow->pbData); + ReleaseMem(pRow); +} + +void DAPI SceFreeQuery( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle + ) +{ + SCE_QUERY *pQuery = reinterpret_cast(sqhHandle); + + ReleaseMem(pQuery->rgBinding); + ReleaseMem(pQuery->pbData); + ReleaseMem(pQuery); +} + +void DAPI SceFreeQueryResults( + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle + ) +{ + SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast(sqrhHandle); + + ReleaseObject(pQueryResults->pIRowset); + ReleaseMem(pQueryResults); +} + +// internal function definitions +static HRESULT CreateSqlCe( + __in_z_opt LPCWSTR wzSqlCeDllPath, + __out IDBInitialize **ppIDBInitialize, + __out_opt HMODULE *phSqlCeDll + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + LPWSTR sczDirectory = NULL; + LPWSTR sczDllFullPath = NULL; + + if (NULL == wzSqlCeDllPath) + { + hr = CoCreateInstance(CLSID_SQLSERVERCE, 0, CLSCTX_INPROC_SERVER, IID_IDBInitialize, reinterpret_cast(ppIDBInitialize)); + ExitOnFailure(hr, "Failed to get IDBInitialize interface"); + } + else + { + // First try loading DLL from the path of our EXE + hr = PathForCurrentProcess(&sczPath, NULL); + ExitOnFailure(hr, "Failed to get path for current process"); + + hr = PathGetDirectory(sczPath, &sczDirectory); + ExitOnFailure(hr, "Failed to get directory of current process"); + + hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath); + ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename"); + + *phSqlCeDll = ::LoadLibraryW(sczDllFullPath); + + // If that failed, fallback to loading from current path + if (NULL == *phSqlCeDll) + { + hr = DirGetCurrent(&sczDirectory); + ExitOnFailure(hr, "Failed to get current directory"); + + hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath); + ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename"); + + *phSqlCeDll = ::LoadLibraryW(sczDllFullPath); + ExitOnNullWithLastError(*phSqlCeDll, hr, "Failed to open Sql CE DLL: %ls", sczDllFullPath); + } + + HRESULT (WINAPI *pfnGetFactory)(REFCLSID, REFIID, void**); + pfnGetFactory = (HRESULT (WINAPI *)(REFCLSID, REFIID, void**))GetProcAddress(*phSqlCeDll, "DllGetClassObject"); + + IClassFactory* pFactory = NULL; + hr = pfnGetFactory(CLSID_SQLSERVERCE, IID_IClassFactory, (void**)&pFactory); + ExitOnFailure(hr, "Failed to get factory for IID_IDBInitialize from DLL: %ls", sczDllFullPath); + ExitOnNull(pFactory, hr, E_UNEXPECTED, "GetFactory returned success, but pFactory was NULL"); + + hr = pFactory->CreateInstance(NULL, IID_IDBInitialize, (void**)ppIDBInitialize); + pFactory->Release(); + } + +LExit: + ReleaseStr(sczPath); + ReleaseStr(sczDirectory); + ReleaseStr(sczDllFullPath); + + return hr; +} + +static HRESULT RunQuery( + __in BOOL fRange, + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle, + __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS **ppQueryResults + ) +{ + HRESULT hr = S_OK; + DBID tableID = { }; + DBID indexID = { }; + IAccessor *pIAccessor = NULL; + IRowsetIndex *pIRowsetIndex = NULL; + IRowset *pIRowset = NULL; + HACCESSOR hAccessor = DB_NULL_HACCESSOR; + SCE_QUERY *pQuery = reinterpret_cast(psqhHandle); + SCE_QUERY_RESULTS *pQueryResults = NULL; + DBPROPSET rgdbpIndexPropSet[1]; + DBPROP rgdbpIndexProp[1]; + + rgdbpIndexPropSet[0].cProperties = 1; + rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_ROWSET; + rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp; + + rgdbpIndexProp[0].dwPropertyID = DBPROP_IRowsetIndex; + rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpIndexProp[0].colid = DB_NULLID; + rgdbpIndexProp[0].vValue.vt = VT_BOOL; + rgdbpIndexProp[0].vValue.boolVal = VARIANT_TRUE; + + tableID.eKind = DBKIND_NAME; + tableID.uName.pwszName = const_cast(pQuery->pTableSchema->wzName); + + indexID.eKind = DBKIND_NAME; + indexID.uName.pwszName = const_cast(pQuery->pIndexSchema->wzName); + + hr = pQuery->pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, _countof(rgdbpIndexPropSet), rgdbpIndexPropSet, (IUnknown**) &pIRowsetIndex); + ExitOnFailure(hr, "Failed to open IRowsetIndex"); + + hr = pIRowsetIndex->QueryInterface(IID_IRowset, reinterpret_cast(&pIRowset)); + ExitOnFailure(hr, "Failed to get IRowset interface from IRowsetIndex"); + + hr = pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); + ExitOnFailure(hr, "Failed to get IAccessor interface"); + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pQuery->dwBindingIndex, pQuery->rgBinding, 0, &hAccessor, NULL); + ExitOnFailure(hr, "Failed to create accessor"); + + if (!fRange) + { + hr = pIRowsetIndex->Seek(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, DBSEEK_FIRSTEQ); + if (DB_E_NOTFOUND == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to seek to record"); + } + else + { + // If ALL columns in the index were specified, do a full key match + if (pQuery->dwBindingIndex == pQuery->pIndexSchema->cColumns) + { + hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, 0, NULL, DBRANGE_MATCH); + } + else + { + // Otherwise, just match the specified keys. + // We really want to use DBRANGE_MATCH_N_SHIFT here, but SQL CE doesn't appear to support it + // So instead, we set the start and end to the same partial key, and then allow inclusive matching + // This appears to accomplish the same thing + hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, pQuery->dwBindingIndex, pQuery->pbData, 0); + } + if (DB_E_NOTFOUND == hr || E_NOTFOUND == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + ExitOnFailure(hr, "Failed to set range to find records"); + } + + pQueryResults = reinterpret_cast(MemAlloc(sizeof(SCE_QUERY_RESULTS), TRUE)); + ExitOnNull(pQueryResults, hr, E_OUTOFMEMORY, "Failed to allocate query results struct"); + + pQueryResults->pDatabaseInternal = pQuery->pDatabaseInternal; + pQueryResults->pTableSchema = pQuery->pTableSchema; + pQueryResults->pIRowset = pIRowset; + pIRowset = NULL; + + *ppQueryResults = pQueryResults; + pQueryResults = NULL; + +LExit: + if (DB_NULL_HACCESSOR != hAccessor) + { + pIAccessor->ReleaseAccessor(hAccessor, NULL); + } + ReleaseObject(pIAccessor); + ReleaseObject(pIRowset); + ReleaseObject(pIRowsetIndex); + ReleaseMem(pQueryResults); + ReleaseSceQueryResults(pQueryResults); + + return hr; +} + +static HRESULT FillOutColumnDescFromSchema( + __in const SCE_COLUMN_SCHEMA *pColumnSchema, + __out DBCOLUMNDESC *pColumnDesc + ) +{ + HRESULT hr = S_OK; + DWORD dwColumnProperties = 0; + DWORD dwColumnPropertyIndex = 0; + BOOL fFixedSize = FALSE; + + pColumnDesc->dbcid.eKind = DBKIND_NAME; + pColumnDesc->dbcid.uName.pwszName = (WCHAR *)pColumnSchema->wzName; + pColumnDesc->wType = pColumnSchema->dbtColumnType; + pColumnDesc->ulColumnSize = pColumnSchema->dwLength; + if (0 == pColumnDesc->ulColumnSize && (DBTYPE_WSTR == pColumnDesc->wType || DBTYPE_BYTES == pColumnDesc->wType)) + { + fFixedSize = FALSE; + } + else + { + fFixedSize = TRUE; + } + + dwColumnProperties = 1; + if (pColumnSchema->fAutoIncrement) + { + ++dwColumnProperties; + } + if (!pColumnSchema->fNullable) + { + ++dwColumnProperties; + } + + if (0 < dwColumnProperties) + { + pColumnDesc->cPropertySets = 1; + pColumnDesc->rgPropertySets = reinterpret_cast(MemAlloc(sizeof(DBPROPSET), TRUE)); + ExitOnNull(pColumnDesc->rgPropertySets, hr, E_OUTOFMEMORY, "Failed to allocate propset object while setting up column parameters"); + + pColumnDesc->rgPropertySets[0].cProperties = dwColumnProperties; + pColumnDesc->rgPropertySets[0].guidPropertySet = DBPROPSET_COLUMN; + pColumnDesc->rgPropertySets[0].rgProperties = reinterpret_cast(MemAlloc(sizeof(DBPROP) * dwColumnProperties, TRUE)); + + dwColumnPropertyIndex = 0; + if (pColumnSchema->fAutoIncrement) + { + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_AUTOINCREMENT; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_TRUE; + ++dwColumnPropertyIndex; + } + if (!pColumnSchema->fNullable) + { + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_NULLABLE; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_FALSE; + ++dwColumnPropertyIndex; + } + + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_FIXEDLENGTH; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL; + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = fFixedSize ? VARIANT_TRUE : VARIANT_FALSE; + ++dwColumnPropertyIndex; + } + +LExit: + return hr; +} + +static HRESULT EnsureSchema( + __in SCE_DATABASE *pDatabase, + __in SCE_DATABASE_SCHEMA *pdsSchema + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + BOOL fInTransaction = FALSE; + BOOL fSchemaNeedsSetup = TRUE; + DBID tableID = { }; + DBID indexID = { }; + DBPROPSET rgdbpIndexPropSet[1]; + DBPROPSET rgdbpRowSetPropSet[1]; + DBPROP rgdbpIndexProp[1]; + DBPROP rgdbpRowSetProp[1]; + DBCOLUMNDESC *rgColumnDescriptions = NULL; + DBINDEXCOLUMNDESC *rgIndexColumnDescriptions = NULL; + DWORD cIndexColumnDescriptions = 0; + DWORD dwTableColumnIndex = 0; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + ITableDefinition *pTableDefinition = NULL; + IIndexDefinition *pIndexDefinition = NULL; + IRowsetIndex *pIRowsetIndex = NULL; + + rgdbpRowSetPropSet[0].cProperties = 1; + rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET; + rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp; + + rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange; + rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpRowSetProp[0].colid = DB_NULLID; + rgdbpRowSetProp[0].vValue.vt = VT_BOOL; + rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE; + + rgdbpIndexPropSet[0].cProperties = 1; + rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_INDEX; + rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp; + + rgdbpIndexProp[0].dwPropertyID = DBPROP_INDEX_NULLS; + rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpIndexProp[0].colid = DB_NULLID; + rgdbpIndexProp[0].vValue.vt = VT_I4; + rgdbpIndexProp[0].vValue.lVal = DBPROPVAL_IN_DISALLOWNULL; + + hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_ITableDefinition, reinterpret_cast(&pTableDefinition)); + ExitOnFailure(hr, "Failed to get ITableDefinition for table creation"); + + hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_IIndexDefinition, reinterpret_cast(&pIndexDefinition)); + ExitOnFailure(hr, "Failed to get IIndexDefinition for index creation"); + + hr = SceBeginTransaction(pDatabase); + ExitOnFailure(hr, "Failed to start transaction for ensuring schema"); + fInTransaction = TRUE; + + for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) + { + tableID.eKind = DBKIND_NAME; + tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); + + // Fill out each column description struct as appropriate, to be used for creating the table, or confirming the table's columns all exist + rgColumnDescriptions = static_cast(MemAlloc(sizeof(DBCOLUMNDESC) * pdsSchema->rgTables[dwTable].cColumns, TRUE)); + ExitOnNull(rgColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate column description array while creating table"); + + for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i) + { + hr = FillOutColumnDescFromSchema(pdsSchema->rgTables[dwTable].rgColumns + i, rgColumnDescriptions + i); + ExitOnFailure(hr, "Failed to fill out column description from schema"); + } + + // First try to open the table - or if it doesn't exist, create it + hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowset)); + if (DB_E_NOTABLE == hr) + { + // The table doesn't exist, so let's create it + hr = pTableDefinition->CreateTable(NULL, &tableID, pdsSchema->rgTables[dwTable].cColumns, rgColumnDescriptions, IID_IUnknown, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, NULL, NULL); + ExitOnFailure(hr, "Failed to create table: %ls", pdsSchema->rgTables[dwTable].wzName); + } + else + { + ExitOnFailure(hr, "Failed to open table %ls while ensuring schema", tableID.uName.pwszName); + + // Close any rowset we opened + ReleaseNullObject(pdsSchema->rgTables[dwTable].pIRowset); + + // If it does exist, make sure all columns are in the table + // Only nullable columns can be added to an existing table + for (DWORD i = 1; i < pdsSchema->rgTables[dwTable].cColumns; ++i) + { + if (pdsSchema->rgTables[dwTable].rgColumns[i].fNullable) + hr = pTableDefinition->AddColumn(&tableID, rgColumnDescriptions + i, NULL); + if (DB_E_DUPLICATECOLUMNID == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to add column %ls", pdsSchema->rgTables[dwTable].rgColumns[i].wzName); + } + } + +#pragma prefast(push) +#pragma prefast(disable:26010) + hr = EnsureLocalColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable); +#pragma prefast(pop) + ExitOnFailure(hr, "Failed to ensure local column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName); + + for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i) + { + if (NULL != rgColumnDescriptions[i].rgPropertySets) + { + ReleaseMem(rgColumnDescriptions[i].rgPropertySets[0].rgProperties); + ReleaseMem(rgColumnDescriptions[i].rgPropertySets); + } + } + + ReleaseNullMem(rgColumnDescriptions); + if (0 < pdsSchema->rgTables[dwTable].cIndexes) + { + // Now create indexes for the table + for (DWORD dwIndex = 0; dwIndex < pdsSchema->rgTables[dwTable].cIndexes; ++dwIndex) + { + indexID.eKind = DBKIND_NAME; + indexID.uName.pwszName = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName; + + // Check if the index exists + hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, 0, NULL, (IUnknown**) &pIRowsetIndex); + if (SUCCEEDED(hr)) + { + // TODO: If one with the same name exists, check if the schema actually matches + ReleaseNullObject(pIRowsetIndex); + continue; + } + hr = S_OK; + + hr = ::SizeTMult(sizeof(DBINDEXCOLUMNDESC), pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns, &cbAllocSize); + ExitOnFailure(hr, "Overflow while calculating allocation size for DBINDEXCOLUMNDESC, columns: %u", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns); + + rgIndexColumnDescriptions = reinterpret_cast(MemAlloc(cbAllocSize, TRUE)); + ExitOnNull(rgIndexColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate structure to hold index column descriptions"); + cIndexColumnDescriptions = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns; + + for (DWORD dwColumnIndex = 0; dwColumnIndex < cIndexColumnDescriptions; ++dwColumnIndex) + { + dwTableColumnIndex = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].rgColumns[dwColumnIndex]; + + rgIndexColumnDescriptions[dwColumnIndex].pColumnID = reinterpret_cast(MemAlloc(sizeof(DBID), TRUE)); + rgIndexColumnDescriptions[dwColumnIndex].pColumnID->eKind = DBKIND_NAME; + rgIndexColumnDescriptions[dwColumnIndex].pColumnID->uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].wzName); + rgIndexColumnDescriptions[dwColumnIndex].eIndexColOrder = pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].fDescending ? DBINDEX_COL_ORDER_DESC : DBINDEX_COL_ORDER_ASC; + } + + hr = pIndexDefinition->CreateIndex(&tableID, &indexID, static_cast(pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns), rgIndexColumnDescriptions, 1, rgdbpIndexPropSet, NULL); + if (DB_E_DUPLICATEINDEXID == hr) + { + // If the index already exists, no worries + hr = S_OK; + } + ExitOnFailure(hr, "Failed to create index named %ls into table named %ls", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName, pdsSchema->rgTables[dwTable].wzName); + + for (DWORD i = 0; i < cIndexColumnDescriptions; ++i) + { + ReleaseMem(rgIndexColumnDescriptions[i].pColumnID); + } + + cIndexColumnDescriptions = 0; + ReleaseNullMem(rgIndexColumnDescriptions); + } + } + } + + // Now once all tables have been created, create foreign key relationships + if (fSchemaNeedsSetup) + { + for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) + { + tableID.eKind = DBKIND_NAME; + tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); + + // Setup any constraints for the table's columns + hr = EnsureForeignColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable, pdsSchema); + ExitOnFailure(hr, "Failed to ensure foreign column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName); + } + } + + hr = SceCommitTransaction(pDatabase); + ExitOnFailure(hr, "Failed to commit transaction for ensuring schema"); + fInTransaction = FALSE; + + hr = OpenSchema(pDatabase, pdsSchema); + ExitOnFailure(hr, "Failed to open schema"); + +LExit: + ReleaseObject(pTableDefinition); + ReleaseObject(pIndexDefinition); + ReleaseObject(pIRowsetIndex); + + if (fInTransaction) + { + SceRollbackTransaction(pDatabase); + } + + for (DWORD i = 0; i < cIndexColumnDescriptions; ++i) + { + ReleaseMem(rgIndexColumnDescriptions[i].pColumnID); + } + + ReleaseMem(rgIndexColumnDescriptions); + ReleaseMem(rgColumnDescriptions); + + return hr; +} + +static HRESULT OpenSchema( + __in SCE_DATABASE *pDatabase, + __in SCE_DATABASE_SCHEMA *pdsSchema + ) +{ + HRESULT hr = S_OK; + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast(pDatabase->sdbHandle); + DBID tableID = { }; + DBPROPSET rgdbpRowSetPropSet[1]; + DBPROP rgdbpRowSetProp[1]; + + rgdbpRowSetPropSet[0].cProperties = 1; + rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET; + rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp; + + rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange; + rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpRowSetProp[0].colid = DB_NULLID; + rgdbpRowSetProp[0].vValue.vt = VT_BOOL; + rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE; + + // Finally, open all tables + for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) + { + tableID.eKind = DBKIND_NAME; + tableID.uName.pwszName = const_cast(pdsSchema->rgTables[dwTable].wzName); + + // And finally, open the table's standard interfaces + hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowset)); + ExitOnFailure(hr, "Failed to open table %u named %ls after ensuring all indexes and constraints are created", dwTable, pdsSchema->rgTables[dwTable].wzName); + + hr = pdsSchema->rgTables[dwTable].pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast(&pdsSchema->rgTables[dwTable].pIRowsetChange)); + ExitOnFailure(hr, "Failed to get IRowsetChange object for table: %ls", pdsSchema->rgTables[dwTable].wzName); + } + +LExit: + return hr; +} + +static HRESULT SetColumnValue( + __in const SCE_TABLE_SCHEMA *pTableSchema, + __in DWORD dwColumnIndex, + __in_bcount_opt(cbSize) const BYTE *pbData, + __in SIZE_T cbSize, + __inout DBBINDING *pBinding, + __inout SIZE_T *pcbOffset, + __inout BYTE **ppbBuffer + ) +{ + HRESULT hr = S_OK; + size_t cbNewOffset = *pcbOffset; + + pBinding->iOrdinal = dwColumnIndex + 1; // Skip bookmark column + pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED; + pBinding->dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS; + + pBinding->obLength = cbNewOffset; + + hr = ::SizeTAdd(cbNewOffset, sizeof(DBBYTEOFFSET), &cbNewOffset); + ExitOnFailure(hr, "Failed to add sizeof(DBBYTEOFFSET) to alloc size while setting column value"); + + pBinding->obValue = cbNewOffset; + + hr = ::SizeTAdd(cbNewOffset, cbSize, &cbNewOffset); + ExitOnFailure(hr, "Failed to add %u to alloc size while setting column value", cbSize); + + pBinding->obStatus = cbNewOffset; + pBinding->eParamIO = DBPARAMIO_INPUT; + + hr = ::SizeTAdd(cbNewOffset, sizeof(DBSTATUS), &cbNewOffset); + ExitOnFailure(hr, "Failed to add sizeof(DBSTATUS) to alloc size while setting column value"); + + pBinding->wType = pTableSchema->rgColumns[dwColumnIndex].dbtColumnType; + pBinding->cbMaxLen = static_cast(cbSize); + + if (NULL == *ppbBuffer) + { + *ppbBuffer = reinterpret_cast(MemAlloc(cbNewOffset, TRUE)); + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer while setting row string"); + } + else + { + *ppbBuffer = reinterpret_cast(MemReAlloc(*ppbBuffer, cbNewOffset, TRUE)); + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate buffer while setting row string"); + } + + *(reinterpret_cast(*ppbBuffer + *pcbOffset)) = static_cast(cbSize); + *pcbOffset += sizeof(DBBYTEOFFSET); + memcpy(*ppbBuffer + *pcbOffset, pbData, cbSize); + *pcbOffset += cbSize; + if (NULL == pbData) + { + *(reinterpret_cast(*ppbBuffer + *pcbOffset)) = DBSTATUS_S_ISNULL; + } + *pcbOffset += sizeof(DBSTATUS); + +LExit: + return hr; +} + +static HRESULT GetColumnValue( + __in SCE_ROW *pRow, + __in DWORD dwColumnIndex, + __out_opt BYTE **ppbData, + __out SIZE_T *pcbSize + ) +{ + HRESULT hr = S_OK; + const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema; + IAccessor *pIAccessor = NULL; + HACCESSOR hAccessorLength = DB_NULL_HACCESSOR; + HACCESSOR hAccessorValue = DB_NULL_HACCESSOR; + DWORD dwDataSize = 0; + void *pvRawData = NULL; + DBBINDING dbBinding = { }; + DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK; + + dbBinding.iOrdinal = dwColumnIndex + 1; + dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; + dbBinding.dwPart = DBPART_LENGTH; + dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType; + + pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); + ExitOnFailure(hr, "Failed to get IAccessor interface"); + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast(&dwDataSize)); + ExitOnFailure(hr, "Failed to get size of data"); + + // For variable-length columns, zero data returned means NULL + if (0 == dwDataSize) + { + ExitFunction1(hr = E_NOTFOUND); + } + + if (NULL != ppbData) + { + dbBinding.dwPart = DBPART_VALUE; + dbBinding.cbMaxLen = dwDataSize; + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + if (DBBINDSTATUS_OK != dbBindStatus) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Bad bind status while creating accessor to get value"); + } + + if (DBTYPE_WSTR == dbBinding.wType) + { + hr = StrAlloc(reinterpret_cast(&pvRawData), dwDataSize / sizeof(WCHAR)); + ExitOnFailure(hr, "Failed to allocate space for string data while reading column %u", dwColumnIndex); + } + else + { + pvRawData = MemAlloc(dwDataSize, TRUE); + ExitOnNull(pvRawData, hr, E_OUTOFMEMORY, "Failed to allocate space for data while reading column %u", dwColumnIndex); + } + + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, pvRawData); + ExitOnFailure(hr, "Failed to read data value"); + + ReleaseMem(*ppbData); + *ppbData = reinterpret_cast(pvRawData); + pvRawData = NULL; + } + + *pcbSize = dwDataSize; + +LExit: + ReleaseMem(pvRawData); + + if (DB_NULL_HACCESSOR != hAccessorLength) + { + pIAccessor->ReleaseAccessor(hAccessorLength, NULL); + } + if (DB_NULL_HACCESSOR != hAccessorValue) + { + pIAccessor->ReleaseAccessor(hAccessorValue, NULL); + } + ReleaseObject(pIAccessor); + + return hr; +} + +static HRESULT GetColumnValueFixed( + __in SCE_ROW *pRow, + __in DWORD dwColumnIndex, + __in DWORD cbSize, + __out BYTE *pbData + ) +{ + HRESULT hr = S_OK; + const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema; + IAccessor *pIAccessor = NULL; + HACCESSOR hAccessorLength = DB_NULL_HACCESSOR; + HACCESSOR hAccessorValue = DB_NULL_HACCESSOR; + DWORD dwDataSize = 0; + DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK; + DBBINDING dbBinding = { }; + + dbBinding.iOrdinal = dwColumnIndex + 1; + dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; + dbBinding.dwPart = DBPART_LENGTH; + dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType; + + pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast(&pIAccessor)); + ExitOnFailure(hr, "Failed to get IAccessor interface"); + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + if (DBBINDSTATUS_OK != dbBindStatus) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Bad bind status while creating accessor to get length of value"); + } + + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast(&dwDataSize)); + ExitOnFailure(hr, "Failed to get size of data"); + + if (0 == dwDataSize) + { + ExitFunction1(hr = E_NOTFOUND); + } + + dbBinding.dwPart = DBPART_VALUE; + dbBinding.cbMaxLen = cbSize; + + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus); + ExitOnFailure(hr, "Failed to create accessor"); + + if (DBBINDSTATUS_OK != dbBindStatus) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Bad bind status while creating accessor to get value"); + } + + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, reinterpret_cast(pbData)); + ExitOnFailure(hr, "Failed to read data value"); + +LExit: + if (DB_NULL_HACCESSOR != hAccessorLength) + { + pIAccessor->ReleaseAccessor(hAccessorLength, NULL); + } + if (DB_NULL_HACCESSOR != hAccessorValue) + { + pIAccessor->ReleaseAccessor(hAccessorValue, NULL); + } + ReleaseObject(pIAccessor); + + return hr; +} + +static HRESULT EnsureLocalColumnConstraints( + __in ITableDefinition *pTableDefinition, + __in DBID *pTableID, + __in SCE_TABLE_SCHEMA *pTableSchema + ) +{ + HRESULT hr = S_OK; + SCE_COLUMN_SCHEMA *pCurrentColumn = NULL; + DBCONSTRAINTDESC dbcdConstraint = { }; + DBID dbConstraintID = { }; + DBID dbLocalColumnID = { }; + ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL; + + hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast(&pTableDefinitionWithConstraints)); + ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints"); + + for (DWORD i = 0; i < pTableSchema->cColumns; ++i) + { + pCurrentColumn = pTableSchema->rgColumns + i; + + // Add a primary key constraint for this column, if one exists + if (pCurrentColumn->fPrimaryKey) + { + // Setup DBID for new constraint + dbConstraintID.eKind = DBKIND_NAME; + dbConstraintID.uName.pwszName = const_cast(L"PrimaryKey"); + dbcdConstraint.pConstraintID = &dbConstraintID; + + dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_PRIMARYKEY; + + dbLocalColumnID.eKind = DBKIND_NAME; + dbLocalColumnID.uName.pwszName = const_cast(pCurrentColumn->wzName); + dbcdConstraint.cColumns = 1; + dbcdConstraint.rgColumnList = &dbLocalColumnID; + + dbcdConstraint.pReferencedTableID = NULL; + dbcdConstraint.cForeignKeyColumns = 0; + dbcdConstraint.rgForeignKeyColumnList = NULL; + dbcdConstraint.pwszConstraintText = NULL; + dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION; + dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION; + dbcdConstraint.MatchType = DBMATCHTYPE_NONE; + dbcdConstraint.Deferrability = 0; + dbcdConstraint.cReserved = 0; + dbcdConstraint.rgReserved = NULL; + + hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint); + if (DB_E_DUPLICATECONSTRAINTID == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to add primary key constraint for column %ls, table %ls", pCurrentColumn->wzName, pTableSchema->wzName); + } + } + +LExit: + ReleaseObject(pTableDefinitionWithConstraints); + + return hr; +} + +static HRESULT EnsureForeignColumnConstraints( + __in ITableDefinition *pTableDefinition, + __in DBID *pTableID, + __in SCE_TABLE_SCHEMA *pTableSchema, + __in SCE_DATABASE_SCHEMA *pDatabaseSchema + ) +{ + HRESULT hr = S_OK; + SCE_COLUMN_SCHEMA *pCurrentColumn = NULL; + DBCONSTRAINTDESC dbcdConstraint = { }; + DBID dbConstraintID = { }; + DBID dbLocalColumnID = { }; + DBID dbForeignTableID = { }; + DBID dbForeignColumnID = { }; + ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL; + + hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast(&pTableDefinitionWithConstraints)); + ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints"); + + for (DWORD i = 0; i < pTableSchema->cColumns; ++i) + { + pCurrentColumn = pTableSchema->rgColumns + i; + + // Add a foreign key constraint for this column, if one exists + if (NULL != pCurrentColumn->wzRelationName) + { + // Setup DBID for new constraint + dbConstraintID.eKind = DBKIND_NAME; + dbConstraintID.uName.pwszName = const_cast(pCurrentColumn->wzRelationName); + dbcdConstraint.pConstraintID = &dbConstraintID; + + dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_FOREIGNKEY; + + dbForeignColumnID.eKind = DBKIND_NAME; + dbForeignColumnID.uName.pwszName = const_cast(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].rgColumns[pCurrentColumn->dwForeignKeyColumn].wzName); + dbcdConstraint.cColumns = 1; + dbcdConstraint.rgColumnList = &dbForeignColumnID; + + dbForeignTableID.eKind = DBKIND_NAME; + dbForeignTableID.uName.pwszName = const_cast(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].wzName); + dbcdConstraint.pReferencedTableID = &dbForeignTableID; + + dbLocalColumnID.eKind = DBKIND_NAME; + dbLocalColumnID.uName.pwszName = const_cast(pCurrentColumn->wzName); + dbcdConstraint.cForeignKeyColumns = 1; + dbcdConstraint.rgForeignKeyColumnList = &dbLocalColumnID; + + dbcdConstraint.pwszConstraintText = NULL; + dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION; + dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION; + dbcdConstraint.MatchType = DBMATCHTYPE_FULL; + dbcdConstraint.Deferrability = 0; + dbcdConstraint.cReserved = 0; + dbcdConstraint.rgReserved = NULL; + + hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint); + if (DB_E_DUPLICATECONSTRAINTID == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to add constraint named: %ls to table: %ls", pCurrentColumn->wzRelationName, pTableSchema->wzName); + } + } + +LExit: + ReleaseObject(pTableDefinitionWithConstraints); + + return hr; +} + +static HRESULT SetSessionProperties( + __in ISessionProperties *pISessionProperties + ) +{ + HRESULT hr = S_OK; + DBPROP rgdbpDataSourceProp[1]; + DBPROPSET rgdbpDataSourcePropSet[1]; + + rgdbpDataSourceProp[0].dwPropertyID = DBPROP_SSCE_TRANSACTION_COMMIT_MODE; + rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpDataSourceProp[0].vValue.vt = VT_I4; + rgdbpDataSourceProp[0].vValue.lVal = DBPROPVAL_SSCE_TCM_FLUSH; + + rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_SSCE_SESSION; + rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp; + rgdbpDataSourcePropSet[0].cProperties = 1; + + hr = pISessionProperties->SetProperties(1, rgdbpDataSourcePropSet); + ExitOnFailure(hr, "Failed to set session properties"); + +LExit: + return hr; +} + +static HRESULT GetDatabaseSchemaInfo( + __in SCE_DATABASE *pDatabase, + __out LPWSTR *psczSchemaType, + __out DWORD *pdwVersion + ) +{ + HRESULT hr = S_OK; + LPWSTR sczSchemaType = NULL; + DWORD dwVersionFound = 0; + SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0]; + SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable}; + // Database object with our alternate schema + SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema }; + SCE_ROW_HANDLE sceRow = NULL; + + hr = OpenSchema(pDatabase, &fullSchema); + ExitOnFailure(hr, "Failed to ensure internal version schema"); + + hr = SceGetFirstRow(&database, 0, &sceRow); + ExitOnFailure(hr, "Failed to get first row in internal version schema table"); + + hr = SceGetColumnString(sceRow, 0, &sczSchemaType); + ExitOnFailure(hr, "Failed to get internal schematype"); + + hr = SceGetColumnDword(sceRow, 1, &dwVersionFound); + ExitOnFailure(hr, "Failed to get internal version"); + + *psczSchemaType = sczSchemaType; + sczSchemaType = NULL; + *pdwVersion = dwVersionFound; + +LExit: + SceCloseTable(&schemaTable); // ignore failure + ReleaseStr(sczSchemaType); + ReleaseSceRow(sceRow); + + return hr; +} + +static HRESULT SetDatabaseSchemaInfo( + __in SCE_DATABASE *pDatabase, + __in LPCWSTR wzSchemaType, + __in DWORD dwVersion + ) +{ + HRESULT hr = S_OK; + BOOL fInSceTransaction = FALSE; + SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0]; + SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable}; + // Database object with our alternate schema + SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema }; + SCE_ROW_HANDLE sceRow = NULL; + + hr = EnsureSchema(pDatabase, &fullSchema); + ExitOnFailure(hr, "Failed to ensure internal version schema"); + + hr = SceBeginTransaction(&database); + ExitOnFailure(hr, "Failed to begin transaction"); + fInSceTransaction = TRUE; + + hr = SceGetFirstRow(&database, 0, &sceRow); + if (E_NOTFOUND == hr) + { + hr = ScePrepareInsert(&database, 0, &sceRow); + ExitOnFailure(hr, "Failed to insert only row into internal version schema table"); + } + else + { + ExitOnFailure(hr, "Failed to get first row in internal version schema table"); + } + + hr = SceSetColumnString(sceRow, 0, wzSchemaType); + ExitOnFailure(hr, "Failed to set internal schematype to: %ls", wzSchemaType); + + hr = SceSetColumnDword(sceRow, 1, dwVersion); + ExitOnFailure(hr, "Failed to set internal version to: %u", dwVersion); + + hr = SceFinishUpdate(sceRow); + ExitOnFailure(hr, "Failed to insert first row in internal version schema table"); + + hr = SceCommitTransaction(&database); + ExitOnFailure(hr, "Failed to commit transaction"); + fInSceTransaction = FALSE; + +LExit: + SceCloseTable(&schemaTable); // ignore failure + ReleaseSceRow(sceRow); + if (fInSceTransaction) + { + SceRollbackTransaction(&database); + } + + return hr; +} + +static void ReleaseDatabase( + SCE_DATABASE *pDatabase + ) +{ + if (NULL != pDatabase) + { + if (NULL != pDatabase->pdsSchema) + { + for (DWORD i = 0; i < pDatabase->pdsSchema->cTables; ++i) + { + SceCloseTable(pDatabase->pdsSchema->rgTables + i); + } + } + + if (NULL != pDatabase->sdbHandle) + { + ReleaseDatabaseInternal(reinterpret_cast(pDatabase->sdbHandle)); + } + } + ReleaseMem(pDatabase); +} + +static void ReleaseDatabaseInternal( + SCE_DATABASE_INTERNAL *pDatabaseInternal + ) +{ + HRESULT hr = S_OK; + + if (NULL != pDatabaseInternal) + { + ReleaseObject(pDatabaseInternal->pITransactionLocal); + ReleaseObject(pDatabaseInternal->pIOpenRowset); + ReleaseObject(pDatabaseInternal->pISessionProperties); + ReleaseObject(pDatabaseInternal->pIDBCreateSession); + ReleaseObject(pDatabaseInternal->pIDBProperties); + + if (NULL != pDatabaseInternal->pIDBInitialize) + { + hr = pDatabaseInternal->pIDBInitialize->Uninitialize(); + if (FAILED(hr)) + { + TraceError(hr, "Failed to call uninitialize on IDBInitialize"); + } + ReleaseObject(pDatabaseInternal->pIDBInitialize); + } + + if (NULL != pDatabaseInternal->hSqlCeDll) + { + if (!::FreeLibrary(pDatabaseInternal->hSqlCeDll)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to free sql ce dll"); + } + } + } + + // If there was a temp file we copied to (for read-only databases), delete it after close + if (NULL != pDatabaseInternal->sczTempDbFile) + { + hr = FileEnsureDelete(pDatabaseInternal->sczTempDbFile); + if (FAILED(hr)) + { + TraceError(hr, "Failed to delete temporary database file (copied here because the database was opened as read-only): %ls", pDatabaseInternal->sczTempDbFile); + } + ReleaseStr(pDatabaseInternal->sczTempDbFile); + } + + ReleaseMem(pDatabaseInternal); +} + +#endif // end SKIP_SCE_COMPILE diff --git a/src/libs/dutil/WixToolset.DUtil/shelutil.cpp b/src/libs/dutil/WixToolset.DUtil/shelutil.cpp new file mode 100644 index 00000000..2eb9a52a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/shelutil.cpp @@ -0,0 +1,342 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ShelExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) +#define ShelExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) +#define ShelExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) +#define ShelExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) +#define ShelExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SHELUTIL, e, x, s, __VA_ARGS__) +#define ShelExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SHELUTIL, g, x, s, __VA_ARGS__) + +static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW; + +static HRESULT GetDesktopShellView( + __in REFIID riid, + __out void **ppv + ); +static HRESULT GetShellDispatchFromView( + __in IShellView *psv, + __in REFIID riid, + __out void **ppv + ); + +/******************************************************************** + ShelFunctionOverride - overrides the shell functions. Typically used + for unit testing. + +*********************************************************************/ +extern "C" void DAPI ShelFunctionOverride( + __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW + ) +{ + vpfnShellExecuteExW = pfnShellExecuteExW ? pfnShellExecuteExW : ::ShellExecuteExW; +} + + +/******************************************************************** + ShelExec() - executes a target. + +*******************************************************************/ +extern "C" HRESULT DAPI ShelExec( + __in_z LPCWSTR wzTargetPath, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd, + __in_opt HWND hwndParent, + __out_opt HANDLE* phProcess + ) +{ + HRESULT hr = S_OK; + SHELLEXECUTEINFOW shExecInfo = {}; + + shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); + shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; + shExecInfo.hwnd = hwndParent; + shExecInfo.lpVerb = wzVerb; + shExecInfo.lpFile = wzTargetPath; + shExecInfo.lpParameters = wzParameters; + shExecInfo.lpDirectory = wzWorkingDirectory; + shExecInfo.nShow = nShowCmd; + + if (!vpfnShellExecuteExW(&shExecInfo)) + { + ShelExitWithLastError(hr, "ShellExecEx failed with return code: %d", Dutil_er); + } + + if (phProcess) + { + *phProcess = shExecInfo.hProcess; + shExecInfo.hProcess = NULL; + } + +LExit: + ReleaseHandle(shExecInfo.hProcess); + + return hr; +} + + +/******************************************************************** + ShelExecUnelevated() - executes a target unelevated. + +*******************************************************************/ +extern "C" HRESULT DAPI ShelExecUnelevated( + __in_z LPCWSTR wzTargetPath, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd + ) +{ + HRESULT hr = S_OK; + BSTR bstrTargetPath = NULL; + VARIANT vtParameters = { }; + VARIANT vtVerb = { }; + VARIANT vtWorkingDirectory = { }; + VARIANT vtShow = { }; + IShellView* psv = NULL; + IShellDispatch2* psd = NULL; + + bstrTargetPath = ::SysAllocString(wzTargetPath); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate target path BSTR."); + + if (wzParameters && *wzParameters) + { + vtParameters.vt = VT_BSTR; + vtParameters.bstrVal = ::SysAllocString(wzParameters); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate parameters BSTR."); + } + + if (wzVerb && *wzVerb) + { + vtVerb.vt = VT_BSTR; + vtVerb.bstrVal = ::SysAllocString(wzVerb); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate verb BSTR."); + } + + if (wzWorkingDirectory && *wzWorkingDirectory) + { + vtWorkingDirectory.vt = VT_BSTR; + vtWorkingDirectory.bstrVal = ::SysAllocString(wzWorkingDirectory); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate working directory BSTR."); + } + + vtShow.vt = VT_INT; + vtShow.intVal = nShowCmd; + + hr = GetDesktopShellView(IID_PPV_ARGS(&psv)); + ShelExitOnFailure(hr, "Failed to get desktop shell view."); + + hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd)); + ShelExitOnFailure(hr, "Failed to get shell dispatch from view."); + + hr = psd->ShellExecute(bstrTargetPath, vtParameters, vtWorkingDirectory, vtVerb, vtShow); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); + } + ShelExitOnRootFailure(hr, "Failed to launch unelevate executable: %ls", bstrTargetPath); + +LExit: + ReleaseObject(psd); + ReleaseObject(psv); + ReleaseBSTR(vtWorkingDirectory.bstrVal); + ReleaseBSTR(vtVerb.bstrVal); + ReleaseBSTR(vtParameters.bstrVal); + ReleaseBSTR(bstrTargetPath); + + return hr; +} + + +/******************************************************************** + ShelGetFolder() - gets a folder by CSIDL. + +*******************************************************************/ +extern "C" HRESULT DAPI ShelGetFolder( + __out_z LPWSTR* psczFolderPath, + __in int csidlFolder + ) +{ + HRESULT hr = S_OK; + WCHAR wzPath[MAX_PATH]; + + hr = ::SHGetFolderPathW(NULL, csidlFolder | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, wzPath); + ShelExitOnFailure(hr, "Failed to get folder path for CSIDL: %d", csidlFolder); + + hr = StrAllocString(psczFolderPath, wzPath, 0); + ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", wzPath); + + hr = PathBackslashTerminate(psczFolderPath); + ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); + +LExit: + return hr; +} + + +/******************************************************************** + ShelGetKnownFolder() - gets a folder by KNOWNFOLDERID. + + Note: return E_NOTIMPL if called on pre-Vista operating systems. +*******************************************************************/ +#ifndef REFKNOWNFOLDERID +#define REFKNOWNFOLDERID REFGUID +#endif + +#ifndef KF_FLAG_CREATE +#define KF_FLAG_CREATE 0x00008000 // Make sure that the folder already exists or create it and apply security specified in folder definition +#endif + +EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)( + REFKNOWNFOLDERID rfid, + DWORD dwFlags, + HANDLE hToken, + PWSTR *ppszPath + ); + +extern "C" HRESULT DAPI ShelGetKnownFolder( + __out_z LPWSTR* psczFolderPath, + __in REFKNOWNFOLDERID rfidFolder + ) +{ + HRESULT hr = S_OK; + HMODULE hShell32Dll = NULL; + PFN_SHGetKnownFolderPath pfn = NULL; + LPWSTR pwzPath = NULL; + + hr = LoadSystemLibrary(L"shell32.dll", &hShell32Dll); + if (E_MODNOTFOUND == hr) + { + TraceError(hr, "Failed to load shell32.dll"); + ExitFunction1(hr = E_NOTIMPL); + } + ShelExitOnFailure(hr, "Failed to load shell32.dll."); + + pfn = reinterpret_cast(::GetProcAddress(hShell32Dll, "SHGetKnownFolderPath")); + ShelExitOnNull(pfn, hr, E_NOTIMPL, "Failed to find SHGetKnownFolderPath entry point."); + + hr = pfn(rfidFolder, KF_FLAG_CREATE, NULL, &pwzPath); + ShelExitOnFailure(hr, "Failed to get known folder path."); + + hr = StrAllocString(psczFolderPath, pwzPath, 0); + ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", pwzPath); + + hr = PathBackslashTerminate(psczFolderPath); + ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); + +LExit: + if (pwzPath) + { + ::CoTaskMemFree(pwzPath); + } + + if (hShell32Dll) + { + ::FreeLibrary(hShell32Dll); + } + + return hr; +} + + +// Internal functions. + +static HRESULT GetDesktopShellView( + __in REFIID riid, + __out void **ppv + ) +{ + HRESULT hr = S_OK; + IShellWindows* psw = NULL; + HWND hwnd = NULL; + IDispatch* pdisp = NULL; + VARIANT vEmpty = {}; // VT_EMPTY + IShellBrowser* psb = NULL; + IShellFolder* psf = NULL; + IShellView* psv = NULL; + + // use the shell view for the desktop using the shell windows automation to find the + // desktop web browser and then grabs its view + // returns IShellView, IFolderView and related interfaces + hr = ::CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw)); + ShelExitOnFailure(hr, "Failed to get shell view."); + + hr = psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp); + if (S_OK == hr) + { + hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb)); + ShelExitOnFailure(hr, "Failed to get desktop window."); + + hr = psb->QueryActiveShellView(&psv); + ShelExitOnFailure(hr, "Failed to get active shell view."); + + hr = psv->QueryInterface(riid, ppv); + ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); + } + else if (S_FALSE == hr) + { + //Windows XP + hr = SHGetDesktopFolder(&psf); + ShelExitOnFailure(hr, "Failed to get desktop folder."); + + hr = psf->CreateViewObject(NULL, IID_IShellView, ppv); + ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); + } + else + { + ShelExitOnFailure(hr, "Failed to get desktop window."); + } + +LExit: + ReleaseObject(psv); + ReleaseObject(psb); + ReleaseObject(psf); + ReleaseObject(pdisp); + ReleaseObject(psw); + + return hr; +} + +static HRESULT GetShellDispatchFromView( + __in IShellView *psv, + __in REFIID riid, + __out void **ppv + ) +{ + HRESULT hr = S_OK; + IDispatch *pdispBackground = NULL; + IShellFolderViewDual *psfvd = NULL; + IDispatch *pdisp = NULL; + + // From a shell view object, gets its automation interface and from that get the shell + // application object that implements IShellDispatch2 and related interfaces. + hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground)); + ShelExitOnFailure(hr, "Failed to get the automation interface for shell."); + + hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd)); + ShelExitOnFailure(hr, "Failed to get shell folder view dual."); + + hr = psfvd->get_Application(&pdisp); + ShelExitOnFailure(hr, "Failed to application object."); + + hr = pdisp->QueryInterface(riid, ppv); + ShelExitOnFailure(hr, "Failed to get IShellDispatch2."); + +LExit: + ReleaseObject(pdisp); + ReleaseObject(psfvd); + ReleaseObject(pdispBackground); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/sqlutil.cpp b/src/libs/dutil/WixToolset.DUtil/sqlutil.cpp new file mode 100644 index 00000000..782c7088 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/sqlutil.cpp @@ -0,0 +1,1002 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// okay, this may look a little weird, but sqlutil.h cannot be in the +// pre-compiled header because we need to #define these things so the +// correct GUID's get pulled into this object file +#include +#define DBINITCONSTANTS +#include "sqlutil.h" + + +//Please note that only SQL native client 11 has TLS1.2 support +#define _SQLNCLI_OLEDB_DEPRECATE_WARNING + +#if !defined(SQLNCLI_VER) +#define SQLNCLI_VER 1100 +#endif + +#if SQLNCLI_VER >= 1100 +#if defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_) +#define SQLNCLI_CLSID CLSID_SQLNCLI11 +#endif // defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_) +extern const GUID OLEDBDECLSPEC _SQLNCLI_OLEDB_DEPRECATE_WARNING CLSID_SQLNCLI11 = { 0x397C2819L,0x8272,0x4532,{ 0xAD,0x3A,0xFB,0x5E,0x43,0xBE,0xAA,0x39 } }; +#endif // SQLNCLI_VER >= 1100 + +// Exit macros +#define SqlExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) +#define SqlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__) +#define SqlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__) +#define SqlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__) +#define SqlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__) +#define SqlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SQLUTIL, e, x, s, __VA_ARGS__) +#define SqlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SQLUTIL, g, x, s, __VA_ARGS__) + +// private prototypes +static HRESULT InitializeDatabaseConnection( + __in REFCLSID rclsid, + __in_z LPCSTR szFriendlyClsidName, + __in DBPROPSET rgdbpsetInit[], + __in_ecount(rgdbpsetInit) DWORD cdbpsetInit, + __out IDBCreateSession** ppidbSession + ); +HRESULT DumpErrorRecords(); +static HRESULT FileSpecToString( + __in const SQL_FILESPEC* psf, + __out LPWSTR* ppwz + ); +static HRESULT EscapeSqlIdentifier( + __in_z LPCWSTR wzDatabase, + __deref_out_z LPWSTR* ppwz + ); + + +/******************************************************************** + SqlConnectDatabase - establishes a connection to a database + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored +********************************************************************/ +extern "C" HRESULT DAPI SqlConnectDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out IDBCreateSession** ppidbSession + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase && ppidbSession); + + HRESULT hr = S_OK; + LPWSTR pwzServerInstance = NULL; + DBPROP rgdbpInit[4] = { }; + DBPROPSET rgdbpsetInit[1] = { }; + ULONG cProperties = 0; + + // if there is an instance + if (wzInstance && *wzInstance) + { + hr = StrAllocFormatted(&pwzServerInstance, L"%s\\%s", wzServer, wzInstance); + } + else + { + hr = StrAllocString(&pwzServerInstance, wzServer, 0); + } + SqlExitOnFailure(hr, "failed to allocate memory for the server instance"); + + // server[\instance] + rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_DATASOURCE; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(pwzServerInstance); + ++cProperties; + + // database + rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_CATALOG; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal= ::SysAllocString(wzDatabase); + ++cProperties; + + if (fIntegratedAuth) + { + // username + rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_INTEGRATED; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(L"SSPI"); // default windows authentication + ++cProperties; + } + else + { + // username + rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_USERID; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzUser); + ++cProperties; + + // password + rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_PASSWORD; + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED; + rgdbpInit[cProperties].colid = DB_NULLID; + ::VariantInit(&rgdbpInit[cProperties].vValue); + rgdbpInit[cProperties].vValue.vt = VT_BSTR; + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzPassword); + ++cProperties; + } + + // put the properties into a set + rgdbpsetInit[0].guidPropertySet = DBPROPSET_DBINIT; + rgdbpsetInit[0].rgProperties = rgdbpInit; + rgdbpsetInit[0].cProperties = cProperties; + + // obtain access to the SQL Native Client provider + hr = InitializeDatabaseConnection(SQLNCLI_CLSID, "SQL Native Client", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession); + if (FAILED(hr)) + { + SqlExitTrace(hr, "Could not initialize SQL Native Client, falling back to SQL OLE DB..."); + + // try OLE DB but if that fails return original error failure + HRESULT hr2 = InitializeDatabaseConnection(CLSID_SQLOLEDB, "SQL OLE DB", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession); + if (FAILED(hr2)) + { + SqlExitTrace(hr2, "Could not initialize SQL OLE DB either, giving up."); + } + else + { + hr = S_OK; + } + } + +LExit: + for (; 0 < cProperties; cProperties--) + { + ::VariantClear(&rgdbpInit[cProperties - 1].vValue); + } + + ReleaseStr(pwzServerInstance); + + return hr; +} + + +/******************************************************************** + SqlStartTransaction - Starts a new transaction that must be ended + +*********************************************************************/ +extern "C" HRESULT DAPI SqlStartTransaction( + __in IDBCreateSession* pidbSession, + __out IDBCreateCommand** ppidbCommand, + __out ITransaction** ppit + ) +{ + Assert(pidbSession && ppit); + + HRESULT hr = S_OK; + + hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)ppidbCommand); + SqlExitOnFailure(hr, "unable to create command from session"); + + hr = (*ppidbCommand)->QueryInterface(IID_ITransactionLocal, (LPVOID*)ppit); + SqlExitOnFailure(hr, "Unable to QueryInterface session to get ITransactionLocal"); + + hr = ((ITransactionLocal*)*ppit)->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL); + +LExit: + + return hr; +} + +/******************************************************************** + SqlEndTransaction - Ends the transaction + + NOTE: if fCommit, will commit the transaction, otherwise rolls back +*********************************************************************/ +extern "C" HRESULT DAPI SqlEndTransaction( + __in ITransaction* pit, + __in BOOL fCommit + ) +{ + Assert(pit); + + HRESULT hr = S_OK; + + if (fCommit) + { + hr = pit->Commit(FALSE, XACTTC_SYNC, 0); + SqlExitOnFailure(hr, "commit of transaction failed"); + } + else + { + hr = pit->Abort(NULL, FALSE, FALSE); + SqlExitOnFailure(hr, "abort of transaction failed"); + } + +LExit: + + return hr; +} + + +/******************************************************************** + SqlDatabaseExists - determines if database exists + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored + returns S_OK if database exist + returns S_FALSE if database does not exist + returns E_* on error +********************************************************************/ +extern "C" HRESULT DAPI SqlDatabaseExists( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + IDBCreateSession* pidbSession = NULL; + + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); + SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); + + hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); + +LExit: + ReleaseObject(pidbSession); + + return hr; +} + + +/******************************************************************** + SqlSessionDatabaseExists - determines if database exists + + NOTE: pidbSession must be connected to master database + returns S_OK if database exist + returns S_FALSE if database does not exist + returns E_* on error +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionDatabaseExists( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(pidbSession && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + + LPWSTR pwzQuery = NULL; + IRowset* pirs = NULL; + + DBCOUNTITEM cRows = 0; + HROW rghRows[1]; + HROW* prow = rghRows; + + // + // query to see if the database exists + // + hr = StrAllocFormatted(&pwzQuery, L"SELECT name FROM sysdatabases WHERE name='%s'", wzDatabase); + SqlExitOnFailure(hr, "failed to allocate query string to ensure database exists"); + + hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, &pirs, NULL, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to get database list from 'master' database"); + Assert(pirs); + + // + // check to see if the database was returned + // + hr = pirs->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRows, &prow); + SqlExitOnFailure(hr, "failed to get row with database name"); + + // succeeded but no database + if ((DB_S_ENDOFROWSET == hr) || (0 == cRows)) + { + hr = S_FALSE; + } + +LExit: + ReleaseObject(pirs); + ReleaseStr(pwzQuery); + + return hr; +} + + +/******************************************************************** + SqlDatabaseEnsureExists - creates a database if it does not exist + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored +********************************************************************/ +extern "C" HRESULT DAPI SqlDatabaseEnsureExists( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + IDBCreateSession* pidbSession = NULL; + + // + // connect to the master database to create the new database + // + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); + SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); + + hr = SqlSessionDatabaseEnsureExists(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); + + Assert(S_OK == hr); +LExit: + ReleaseObject(pidbSession); + + return hr; +} + + +/******************************************************************** + SqlSessionDatabaseEnsureExists - creates a database if it does not exist + + NOTE: pidbSession must be connected to the master database +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionDatabaseEnsureExists( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(pidbSession && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + + hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); + + if (S_FALSE == hr) + { + hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); + } + // else database already exists, return S_FALSE + + Assert(S_OK == hr); +LExit: + + return hr; +} + + +/******************************************************************** + SqlCreateDatabase - creates a database on the server + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored +********************************************************************/ +extern "C" HRESULT DAPI SqlCreateDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + IDBCreateSession* pidbSession = NULL; + + // + // connect to the master database to create the new database + // + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); + SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer); + + hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase); + + Assert(S_OK == hr); +LExit: + ReleaseObject(pidbSession); + + return hr; +} + + +/******************************************************************** + SqlSessionCreateDatabase - creates a database on the server + + NOTE: pidbSession must be connected to the master database +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionCreateDatabase( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __in_opt const SQL_FILESPEC* psfDatabase, + __in_opt const SQL_FILESPEC* psfLog, + __out_opt BSTR* pbstrErrorDescription + ) +{ + HRESULT hr = S_OK; + LPWSTR pwzDbFile = NULL; + LPWSTR pwzLogFile = NULL; + LPWSTR pwzQuery = NULL; + LPWSTR pwzDatabaseEscaped = NULL; + + if (psfDatabase) + { + hr = FileSpecToString(psfDatabase, &pwzDbFile); + SqlExitOnFailure(hr, "failed to convert db filespec to string"); + } + + if (psfLog) + { + hr = FileSpecToString(psfLog, &pwzLogFile); + SqlExitOnFailure(hr, "failed to convert log filespec to string"); + } + + hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped); + SqlExitOnFailure(hr, "failed to escape database string"); + + hr = StrAllocFormatted(&pwzQuery, L"CREATE DATABASE %s %s%s %s%s", pwzDatabaseEscaped, pwzDbFile ? L"ON " : L"", pwzDbFile ? pwzDbFile : L"", pwzLogFile ? L"LOG ON " : L"", pwzLogFile ? pwzLogFile : L""); + SqlExitOnFailure(hr, "failed to allocate query to create database: %ls", pwzDatabaseEscaped); + + hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to create database: %ls, Query: %ls", pwzDatabaseEscaped, pwzQuery); + +LExit: + ReleaseStr(pwzQuery); + ReleaseStr(pwzLogFile); + ReleaseStr(pwzDbFile); + ReleaseStr(pwzDatabaseEscaped); + + return hr; +} + + +/******************************************************************** + SqlDropDatabase - removes a database from a server if it exists + + NOTE: wzInstance is optional + if fIntegratedAuth is set then wzUser and wzPassword are ignored +********************************************************************/ +extern "C" HRESULT DAPI SqlDropDatabase( + __in_z LPCWSTR wzServer, + __in_z LPCWSTR wzInstance, + __in_z LPCWSTR wzDatabase, + __in BOOL fIntegratedAuth, + __in_z LPCWSTR wzUser, + __in_z LPCWSTR wzPassword, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(wzServer && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + IDBCreateSession* pidbSession = NULL; + + // + // connect to the master database to search for wzDatabase + // + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession); + SqlExitOnFailure(hr, "Failed to connect to 'master' database"); + + hr = SqlSessionDropDatabase(pidbSession, wzDatabase, pbstrErrorDescription); + +LExit: + ReleaseObject(pidbSession); + + return hr; +} + + +/******************************************************************** + SqlSessionDropDatabase - removes a database from a server if it exists + + NOTE: pidbSession must be connected to the master database +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionDropDatabase( + __in IDBCreateSession* pidbSession, + __in_z LPCWSTR wzDatabase, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(pidbSession && wzDatabase && *wzDatabase); + + HRESULT hr = S_OK; + LPWSTR pwzQuery = NULL; + LPWSTR pwzDatabaseEscaped = NULL; + + hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription); + SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase); + + hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped); + SqlExitOnFailure(hr, "failed to escape database string"); + + if (S_OK == hr) + { + hr = StrAllocFormatted(&pwzQuery, L"DROP DATABASE %s", pwzDatabaseEscaped); + SqlExitOnFailure(hr, "failed to allocate query to drop database: %ls", pwzDatabaseEscaped); + + hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription); + SqlExitOnFailure(hr, "Failed to drop database"); + } + +LExit: + ReleaseStr(pwzQuery); + ReleaseStr(pwzDatabaseEscaped); + + return hr; +} + + +/******************************************************************** + SqlSessionExecuteQuery - executes a query and returns the results if desired + + NOTE: ppirs and pcRoes and pbstrErrorDescription are optional +********************************************************************/ +extern "C" HRESULT DAPI SqlSessionExecuteQuery( + __in IDBCreateSession* pidbSession, + __in __sql_command LPCWSTR wzSql, + __out_opt IRowset** ppirs, + __out_opt DBROWCOUNT* pcRows, + __out_opt BSTR* pbstrErrorDescription + ) +{ + Assert(pidbSession); + + HRESULT hr = S_OK; + IDBCreateCommand* pidbCommand = NULL; + ICommandText* picmdText = NULL; + ICommand* picmd = NULL; + DBROWCOUNT cRows = 0; + + if (pcRows) + { + *pcRows = NULL; + } + + // + // create the command + // + hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)&pidbCommand); + SqlExitOnFailure(hr, "failed to create database session"); + hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd); + SqlExitOnFailure(hr, "failed to create command to execute session"); + + // + // set the sql text into the command + // + hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText); + SqlExitOnFailure(hr, "failed to get command text object for command"); + hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql); + SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); + + // + // execute the command + // + hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast(ppirs)); + SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); + + if (DB_S_ERRORSOCCURRED == hr) + { + hr = E_FAIL; + } + + if (pcRows) + { + *pcRows = cRows; + } + +LExit: + + if (FAILED(hr) && picmd && pbstrErrorDescription) + { + HRESULT hrGetErrors = SqlGetErrorInfo(picmd, IID_ICommandText, 0x409, NULL, pbstrErrorDescription); // TODO: use current locale instead of always American-English + if (FAILED(hrGetErrors)) + { + ReleaseBSTR(*pbstrErrorDescription); + } + } + + ReleaseObject(picmd); + ReleaseObject(picmdText); + ReleaseObject(pidbCommand); + + return hr; +} + + +/******************************************************************** + SqlCommandExecuteQuery - executes a SQL command and returns the results if desired + + NOTE: ppirs and pcRoes are optional +********************************************************************/ +extern "C" HRESULT DAPI SqlCommandExecuteQuery( + __in IDBCreateCommand* pidbCommand, + __in __sql_command LPCWSTR wzSql, + __out IRowset** ppirs, + __out DBROWCOUNT* pcRows + ) +{ + Assert(pidbCommand); + + HRESULT hr = S_OK; + ICommandText* picmdText = NULL; + ICommand* picmd = NULL; + DBROWCOUNT cRows = 0; + + if (pcRows) + { + *pcRows = NULL; + } + + // + // create the command + // + hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd); + SqlExitOnFailure(hr, "failed to create command to execute session"); + + // + // set the sql text into the command + // + hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText); + SqlExitOnFailure(hr, "failed to get command text object for command"); + hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql); + SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql); + + // + // execute the command + // + hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast(ppirs)); + SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql); + + if (DB_S_ERRORSOCCURRED == hr) + { + hr = E_FAIL; + } + + if (pcRows) + { + *pcRows = cRows; + } + +LExit: + ReleaseObject(picmd); + ReleaseObject(picmdText); + + return hr; +} + + +/******************************************************************** + SqlGetErrorInfo - gets error information from the last SQL function call + + NOTE: pbstrErrorSource and pbstrErrorDescription are optional +********************************************************************/ +extern "C" HRESULT DAPI SqlGetErrorInfo( + __in IUnknown* pObjectWithError, + __in REFIID IID_InterfaceWithError, + __in DWORD dwLocaleId, + __out_opt BSTR* pbstrErrorSource, + __out_opt BSTR* pbstrErrorDescription + ) +{ + HRESULT hr = S_OK; + Assert(pObjectWithError); + + // interfaces needed to extract error information out + ISupportErrorInfo* pISupportErrorInfo = NULL; + IErrorInfo* pIErrorInfoAll = NULL; + IErrorRecords* pIErrorRecords = NULL; + IErrorInfo* pIErrorInfoRecord = NULL; + + // only ask for error information if the interface supports it. + hr = pObjectWithError->QueryInterface(IID_ISupportErrorInfo,(void**)&pISupportErrorInfo); + SqlExitOnFailure(hr, "No error information was found for object."); + + hr = pISupportErrorInfo->InterfaceSupportsErrorInfo(IID_InterfaceWithError); + SqlExitOnFailure(hr, "InterfaceWithError is not supported for object with error"); + + // ignore the return of GetErrorInfo it can succeed and return a NULL pointer in pIErrorInfoAll anyway + hr = ::GetErrorInfo(0, &pIErrorInfoAll); + SqlExitOnFailure(hr, "failed to get error info"); + + if (S_OK == hr && pIErrorInfoAll) + { + // see if it's a valid OLE DB IErrorInfo interface that exposes a list of records + hr = pIErrorInfoAll->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords); + if (SUCCEEDED(hr)) + { + ULONG cErrors = 0; + pIErrorRecords->GetRecordCount(&cErrors); + + // get the error information for each record + for (ULONG i = 0; i < cErrors; ++i) + { + hr = pIErrorRecords->GetErrorInfo(i, dwLocaleId, &pIErrorInfoRecord); + if (SUCCEEDED(hr)) + { + if (pbstrErrorSource) + { + pIErrorInfoRecord->GetSource(pbstrErrorSource); + } + if (pbstrErrorDescription) + { + pIErrorInfoRecord->GetDescription(pbstrErrorDescription); + } + + ReleaseNullObject(pIErrorInfoRecord); + + break; // TODO: return more than one error in the future! + } + } + + ReleaseNullObject(pIErrorRecords); + } + else // we have a simple error record + { + if (pbstrErrorSource) + { + pIErrorInfoAll->GetSource(pbstrErrorSource); + } + if (pbstrErrorDescription) + { + pIErrorInfoAll->GetDescription(pbstrErrorDescription); + } + } + } + else + { + hr = E_NOMOREITEMS; + } + +LExit: + ReleaseObject(pIErrorInfoRecord); + ReleaseObject(pIErrorRecords); + ReleaseObject(pIErrorInfoAll); + ReleaseObject(pISupportErrorInfo); + + return hr; +} + + +// +// private +// + +static HRESULT InitializeDatabaseConnection( + __in REFCLSID rclsid, + __in_z LPCSTR szFriendlyClsidName, + __in DBPROPSET rgdbpsetInit[], + __in_ecount(rgdbpsetInit) DWORD cdbpsetInit, + __out IDBCreateSession** ppidbSession +) +{ + Unused(szFriendlyClsidName); // only used in DEBUG builds + + HRESULT hr = S_OK; + IDBInitialize* pidbInitialize = NULL; + IDBProperties* pidbProperties = NULL; + + hr = ::CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize, (LPVOID*)&pidbInitialize); + SqlExitOnFailure(hr, "failed to initialize %s", szFriendlyClsidName); + + // create and set the property set + hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties); + SqlExitOnFailure(hr, "failed to get IID_IDBProperties for %s", szFriendlyClsidName); + + hr = pidbProperties->SetProperties(cdbpsetInit, rgdbpsetInit); + SqlExitOnFailure(hr, "failed to set properties for %s", szFriendlyClsidName); + + // initialize connection to datasource + hr = pidbInitialize->Initialize(); + if (FAILED(hr)) + { + DumpErrorRecords(); + } + SqlExitOnFailure(hr, "failed to initialize connection for %s", szFriendlyClsidName); + + hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession); + SqlExitOnFailure(hr, "failed to query for connection session for %s", szFriendlyClsidName); + +LExit: + ReleaseObject(pidbProperties); + ReleaseObject(pidbInitialize); + + return hr; +} + +HRESULT DumpErrorRecords() +{ + HRESULT hr = S_OK; + IErrorInfo* pIErrorInfo = NULL; + IErrorRecords* pIErrorRecords = NULL; + IErrorInfo* pIErrorInfoRecord = NULL; + BSTR bstrDescription = NULL; + ULONG i = 0; + ULONG cRecords = 0; + ERRORINFO ErrorInfo = { }; + + // Get IErrorInfo pointer from OLE. + hr = ::GetErrorInfo(0, &pIErrorInfo); + if (FAILED(hr)) + { + ExitFunction(); + } + + // QI for IID_IErrorRecords. + hr = pIErrorInfo->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords); + if (FAILED(hr)) + { + ExitFunction(); + } + + // Get error record count. + hr = pIErrorRecords->GetRecordCount(&cRecords); + if (FAILED(hr)) + { + ExitFunction(); + } + + // Loop through the error records. + for (i = 0; i < cRecords; i++) + { + // Get pIErrorInfo from pIErrorRecords. + hr = pIErrorRecords->GetErrorInfo(i, 1033, &pIErrorInfoRecord); + + if (SUCCEEDED(hr)) + { + // Get error description and source. + hr = pIErrorInfoRecord->GetDescription(&bstrDescription); + + // Retrieve the ErrorInfo structures. + hr = pIErrorRecords->GetBasicErrorInfo(i, &ErrorInfo); + + SqlExitTrace(ErrorInfo.hrError, "SQL error %lu/%lu: %ls", i + 1, cRecords, bstrDescription); + + ReleaseNullObject(pIErrorInfoRecord); + ReleaseNullBSTR(bstrDescription); + } + } + +LExit: + ReleaseNullBSTR(bstrDescription); + ReleaseObject(pIErrorInfoRecord); + ReleaseObject(pIErrorRecords); + ReleaseObject(pIErrorInfo); + + return hr; +} + +/******************************************************************** + FileSpecToString + +*********************************************************************/ +static HRESULT FileSpecToString( + __in const SQL_FILESPEC* psf, + __out LPWSTR* ppwz + ) +{ + Assert(psf && ppwz); + + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + + hr = StrAllocString(&pwz, L"(", 1024); + SqlExitOnFailure(hr, "failed to allocate string for database file info"); + + SqlExitOnNull(*psf->wzName, hr, E_INVALIDARG, "logical name not specified in database file info"); + SqlExitOnNull(*psf->wzFilename, hr, E_INVALIDARG, "filename not specified in database file info"); + + hr = StrAllocFormatted(&pwz, L"%sNAME=%s", pwz, psf->wzName); + SqlExitOnFailure(hr, "failed to format database file info name: %ls", psf->wzName); + + hr = StrAllocFormatted(&pwz, L"%s, FILENAME='%s'", pwz, psf->wzFilename); + SqlExitOnFailure(hr, "failed to format database file info filename: %ls", psf->wzFilename); + + if (0 != psf->wzSize[0]) + { + hr = StrAllocFormatted(&pwz, L"%s, SIZE=%s", pwz, psf->wzSize); + SqlExitOnFailure(hr, "failed to format database file info size: %ls", psf->wzSize); + } + + if (0 != psf->wzMaxSize[0]) + { + hr = StrAllocFormatted(&pwz, L"%s, MAXSIZE=%s", pwz, psf->wzMaxSize); + SqlExitOnFailure(hr, "failed to format database file info maxsize: %ls", psf->wzMaxSize); + } + + if (0 != psf->wzGrow[0]) + { + hr = StrAllocFormatted(&pwz, L"%s, FILEGROWTH=%s", pwz, psf->wzGrow); + SqlExitOnFailure(hr, "failed to format database file info growth: %ls", psf->wzGrow); + } + + hr = StrAllocFormatted(&pwz, L"%s)", pwz); + SqlExitOnFailure(hr, "failed to allocate string for file spec"); + + *ppwz = pwz; + pwz = NULL; // null here so it doesn't get freed below + +LExit: + ReleaseStr(pwz); + return hr; +} + +static HRESULT EscapeSqlIdentifier( + __in_z LPCWSTR wzIdentifier, + __deref_out_z LPWSTR* ppwz + ) +{ + Assert(ppwz); + + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + + if (wzIdentifier == NULL) + { + //Just ignore a NULL identifier and clear out the result + ReleaseNullStr(*ppwz); + ExitFunction(); + } + + int cchIdentifier = lstrlenW(wzIdentifier); + + //If an empty string or already escaped just copy + if (cchIdentifier == 0 || (wzIdentifier[0] == '[' && wzIdentifier[cchIdentifier-1] == ']')) + { + hr = StrAllocString(&pwz, wzIdentifier, 0); + SqlExitOnFailure(hr, "failed to format database name: %ls", wzIdentifier); + } + else + { + //escape it + hr = StrAllocFormatted(&pwz, L"[%s]", wzIdentifier); + SqlExitOnFailure(hr, "failed to format escaped database name: %ls", wzIdentifier); + } + + *ppwz = pwz; + pwz = NULL; // null here so it doesn't get freed below + +LExit: + ReleaseStr(pwz); + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/srputil.cpp b/src/libs/dutil/WixToolset.DUtil/srputil.cpp new file mode 100644 index 00000000..e44536cc --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/srputil.cpp @@ -0,0 +1,252 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define SrpExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__) +#define SrpExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SRPUTIL, p, x, e, s, __VA_ARGS__) +#define SrpExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, p, x, s, __VA_ARGS__) +#define SrpExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SRPUTIL, p, x, e, s, __VA_ARGS__) +#define SrpExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, p, x, s, __VA_ARGS__) +#define SrpExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SRPUTIL, e, x, s, __VA_ARGS__) +#define SrpExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SRPUTIL, g, x, s, __VA_ARGS__) + + +typedef BOOL (WINAPI *PFN_SETRESTOREPTW)( + __in PRESTOREPOINTINFOW pRestorePtSpec, + __out PSTATEMGRSTATUS pSMgrStatus + ); + +static PFN_SETRESTOREPTW vpfnSRSetRestorePointW = NULL; +static HMODULE vhSrClientDll = NULL; + + +static HRESULT InitializeComSecurity(); + + +DAPI_(HRESULT) SrpInitialize( + __in BOOL fInitializeComSecurity + ) +{ + HRESULT hr = S_OK; + + hr = LoadSystemLibrary(L"srclient.dll", &vhSrClientDll); + if (FAILED(hr)) + { + ExitFunction1(hr = E_NOTIMPL); + } + + vpfnSRSetRestorePointW = reinterpret_cast(::GetProcAddress(vhSrClientDll, "SRSetRestorePointW")); + SrpExitOnNullWithLastError(vpfnSRSetRestorePointW, hr, "Failed to find set restore point proc address."); + + // If allowed, initialize COM security to enable NetworkService, + // LocalService and System to make callbacks to the process + // calling System Restore. This is required for any process + // that calls SRSetRestorePoint. + if (fInitializeComSecurity) + { + hr = InitializeComSecurity(); + SrpExitOnFailure(hr, "Failed to initialize security for COM to talk to system restore."); + } + +LExit: + if (FAILED(hr) && vhSrClientDll) + { + SrpUninitialize(); + } + + return hr; +} + +DAPI_(void) SrpUninitialize() +{ + if (vhSrClientDll) + { + ::FreeLibrary(vhSrClientDll); + vhSrClientDll = NULL; + vpfnSRSetRestorePointW = NULL; + } +} + +DAPI_(HRESULT) SrpCreateRestorePoint( + __in_z LPCWSTR wzApplicationName, + __in SRP_ACTION action + ) +{ + HRESULT hr = S_OK; + RESTOREPOINTINFOW restorePoint = { }; + STATEMGRSTATUS status = { }; + + if (!vpfnSRSetRestorePointW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + restorePoint.dwEventType = BEGIN_SYSTEM_CHANGE; + restorePoint.dwRestorePtType = (SRP_ACTION_INSTALL == action) ? APPLICATION_INSTALL : (SRP_ACTION_UNINSTALL == action) ? APPLICATION_UNINSTALL : MODIFY_SETTINGS; + ::StringCbCopyW(restorePoint.szDescription, sizeof(restorePoint.szDescription), wzApplicationName); + + if (!vpfnSRSetRestorePointW(&restorePoint, &status)) + { + SrpExitOnWin32Error(status.nStatus, hr, "Failed to create system restore point."); + } + +LExit: + return hr; +} + + +// internal functions. + +static HRESULT InitializeComSecurity() +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + SECURITY_DESCRIPTOR sd = {0}; + EXPLICIT_ACCESS ea[5] = {0}; + ACL* pAcl = NULL; + ULONGLONG rgSidBA[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + ULONGLONG rgSidLS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + ULONGLONG rgSidNS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + ULONGLONG rgSidPS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + ULONGLONG rgSidSY[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0}; + DWORD cbSid = 0; + + // Create the security descriptor explicitly as follows because + // CoInitializeSecurity() will not accept the relative security descriptors + // returned by ConvertStringSecurityDescriptorToSecurityDescriptor(). + // + // The result is a security descriptor that is equivalent to the following + // security descriptor definition language (SDDL) string: + // + // O:BAG:BAD:(A;;0x1;;;LS)(A;;0x1;;;NS)(A;;0x1;;;PS)(A;;0x1;;;SY)(A;;0x1;;;BA) + // + + // Initialize the security descriptor. + if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) + { + SrpExitWithLastError(hr, "Failed to initialize security descriptor for system restore."); + } + + // Create an administrator group security identifier (SID). + cbSid = sizeof(rgSidBA); + if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, rgSidBA, &cbSid)) + { + SrpExitWithLastError(hr, "Failed to create administrator SID for system restore."); + } + + // Create a local service security identifier (SID). + cbSid = sizeof(rgSidLS); + if (!::CreateWellKnownSid(WinLocalServiceSid, NULL, rgSidLS, &cbSid)) + { + SrpExitWithLastError(hr, "Failed to create local service SID for system restore."); + } + + // Create a network service security identifier (SID). + cbSid = sizeof(rgSidNS); + if (!::CreateWellKnownSid(WinNetworkServiceSid, NULL, rgSidNS, &cbSid)) + { + SrpExitWithLastError(hr, "Failed to create network service SID for system restore."); + } + + // Create a personal account security identifier (SID). + cbSid = sizeof(rgSidPS); + if (!::CreateWellKnownSid(WinSelfSid, NULL, rgSidPS, &cbSid)) + { + SrpExitWithLastError(hr, "Failed to create self SID for system restore."); + } + + // Create a local service security identifier (SID). + cbSid = sizeof(rgSidSY); + if (!::CreateWellKnownSid(WinLocalSystemSid, NULL, rgSidSY, &cbSid)) + { + SrpExitWithLastError(hr, "Failed to create local system SID for system restore."); + } + + // Setup the access control entries (ACE) for COM. COM_RIGHTS_EXECUTE and + // COM_RIGHTS_EXECUTE_LOCAL are the minimum access rights required. + ea[0].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[0].grfAccessMode = SET_ACCESS; + ea[0].grfInheritance = NO_INHERITANCE; + ea[0].Trustee.pMultipleTrustee = NULL; + ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[0].Trustee.ptstrName = (LPTSTR)rgSidBA; + + ea[1].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[1].grfAccessMode = SET_ACCESS; + ea[1].grfInheritance = NO_INHERITANCE; + ea[1].Trustee.pMultipleTrustee = NULL; + ea[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[1].Trustee.ptstrName = (LPTSTR)rgSidLS; + + ea[2].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[2].grfAccessMode = SET_ACCESS; + ea[2].grfInheritance = NO_INHERITANCE; + ea[2].Trustee.pMultipleTrustee = NULL; + ea[2].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[2].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[2].Trustee.ptstrName = (LPTSTR)rgSidNS; + + ea[3].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[3].grfAccessMode = SET_ACCESS; + ea[3].grfInheritance = NO_INHERITANCE; + ea[3].Trustee.pMultipleTrustee = NULL; + ea[3].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[3].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[3].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[3].Trustee.ptstrName = (LPTSTR)rgSidPS; + + ea[4].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; + ea[4].grfAccessMode = SET_ACCESS; + ea[4].grfInheritance = NO_INHERITANCE; + ea[4].Trustee.pMultipleTrustee = NULL; + ea[4].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[4].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[4].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea[4].Trustee.ptstrName = (LPTSTR)rgSidSY; + + // Create an access control list (ACL) using this ACE list. + er = ::SetEntriesInAcl(countof(ea), ea, NULL, &pAcl); + SrpExitOnWin32Error(er, hr, "Failed to create ACL for system restore."); + + // Set the security descriptor owner to Administrators. + if (!::SetSecurityDescriptorOwner(&sd, rgSidBA, FALSE)) + { + SrpExitWithLastError(hr, "Failed to set administrators owner for system restore."); + } + + // Set the security descriptor group to Administrators. + if (!::SetSecurityDescriptorGroup(&sd, rgSidBA, FALSE)) + { + SrpExitWithLastError(hr, "Failed to set administrators group access for system restore."); + } + + // Set the discretionary access control list (DACL) to the ACL. + if (!::SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE)) + { + SrpExitWithLastError(hr, "Failed to set DACL for system restore."); + } + + // Note that an explicit security descriptor is being passed in. + hr= ::CoInitializeSecurity(&sd, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_DISABLE_AAA | EOAC_NO_CUSTOM_MARSHAL, NULL); + SrpExitOnFailure(hr, "Failed to initialize COM security for system restore."); + +LExit: + if (pAcl) + { + ::LocalFree(pAcl); + } + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/strutil.cpp b/src/libs/dutil/WixToolset.DUtil/strutil.cpp new file mode 100644 index 00000000..550d6169 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/strutil.cpp @@ -0,0 +1,2824 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define StrExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__) +#define StrExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__) +#define StrExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__) +#define StrExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__) +#define StrExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__) +#define StrExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_STRUTIL, e, x, s, __VA_ARGS__) +#define StrExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_STRUTIL, g, x, s, __VA_ARGS__) + +#define ARRAY_GROWTH_SIZE 5 + +// Forward declarations. +static HRESULT AllocHelper( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in SIZE_T cch, + __in BOOL fZeroOnRealloc + ); +static HRESULT AllocStringHelper( + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in BOOL fZeroOnRealloc + ); +static HRESULT AllocConcatHelper( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in BOOL fZeroOnRealloc + ); +static HRESULT AllocFormattedArgsHelper( + __deref_out_z LPWSTR* ppwz, + __in BOOL fZeroOnRealloc, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ); +static HRESULT StrAllocStringMapInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in DWORD dwMapFlags + ); + +/******************************************************************** +StrAlloc - allocates or reuses dynamic string memory + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAlloc( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in SIZE_T cch + ) +{ + return AllocHelper(ppwz, cch, FALSE); +} + +/******************************************************************** +StrAllocSecure - allocates or reuses dynamic string memory +If the memory needs to reallocated, calls SecureZeroMemory on the +original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAllocSecure( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in SIZE_T cch + ) +{ + return AllocHelper(ppwz, cch, TRUE); +} + +/******************************************************************** +AllocHelper - allocates or reuses dynamic string memory +If fZeroOnRealloc is true and the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +static HRESULT AllocHelper( + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in SIZE_T cch, + __in BOOL fZeroOnRealloc + ) +{ + Assert(ppwz && cch); + + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + + if (cch >= MAXDWORD / sizeof(WCHAR)) + { + hr = E_OUTOFMEMORY; + StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + } + + if (*ppwz) + { + if (fZeroOnRealloc) + { + LPVOID pvNew = NULL; + hr = MemReAllocSecure(*ppwz, sizeof(WCHAR)* cch, FALSE, &pvNew); + StrExitOnFailure(hr, "Failed to reallocate string"); + pwz = static_cast(pvNew); + } + else + { + pwz = static_cast(MemReAlloc(*ppwz, sizeof(WCHAR)* cch, FALSE)); + } + } + else + { + pwz = static_cast(MemAlloc(sizeof(WCHAR) * cch, TRUE)); + } + + StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + + *ppwz = pwz; +LExit: + return hr; +} + + +/******************************************************************** +StrTrimCapacity - Frees any unnecessary memory associated with a string. + Purely used for optimization, generally only when a string + has been changing size, and will no longer grow. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +HRESULT DAPI StrTrimCapacity( + __deref_out_z LPWSTR* ppwz + ) +{ + Assert(ppwz); + + HRESULT hr = S_OK; + SIZE_T cchLen = 0; + + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); + StrExitOnRootFailure(hr, "Failed to calculate length of string"); + + ++cchLen; // Add 1 for null-terminator + + hr = StrAlloc(ppwz, cchLen); + StrExitOnFailure(hr, "Failed to reallocate string"); + +LExit: + return hr; +} + + +/******************************************************************** +StrTrimWhitespace - allocates or reuses dynamic string memory and copies + in an existing string, excluding whitespace + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +HRESULT DAPI StrTrimWhitespace( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource + ) +{ + HRESULT hr = S_OK; + size_t i = 0; + LPWSTR sczResult = NULL; + + // Ignore beginning whitespace + while (L' ' == *wzSource || L'\t' == *wzSource) + { + wzSource++; + } + + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &i); + StrExitOnRootFailure(hr, "Failed to get length of string"); + + // Overwrite ending whitespace with null characters + if (0 < i) + { + // start from the last non-null-terminator character in the array + for (i = i - 1; i > 0; --i) + { + if (L' ' != wzSource[i] && L'\t' != wzSource[i]) + { + break; + } + } + + ++i; + } + + hr = StrAllocString(&sczResult, wzSource, i); + StrExitOnFailure(hr, "Failed to copy result string"); + + // Output result + *ppwz = sczResult; + sczResult = NULL; + +LExit: + ReleaseStr(sczResult); + + return hr; +} + + +/******************************************************************** +StrAnsiAlloc - allocates or reuses dynamic ANSI string memory + +NOTE: caller is responsible for freeing ppsz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAlloc( + __deref_out_ecount_part(cch, 0) LPSTR* ppsz, + __in SIZE_T cch + ) +{ + Assert(ppsz && cch); + + HRESULT hr = S_OK; + LPSTR psz = NULL; + + if (cch >= MAXDWORD / sizeof(WCHAR)) + { + hr = E_OUTOFMEMORY; + StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + } + + if (*ppsz) + { + psz = static_cast(MemReAlloc(*ppsz, sizeof(CHAR) * cch, FALSE)); + } + else + { + psz = static_cast(MemAlloc(sizeof(CHAR) * cch, TRUE)); + } + + StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + + *ppsz = psz; +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiTrimCapacity - Frees any unnecessary memory associated with a string. + Purely used for optimization, generally only when a string + has been changing size, and will no longer grow. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +HRESULT DAPI StrAnsiTrimCapacity( + __deref_out_z LPSTR* ppz + ) +{ + Assert(ppz); + + HRESULT hr = S_OK; + SIZE_T cchLen = 0; + +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); +#pragma prefast(pop) + StrExitOnFailure(hr, "Failed to calculate length of string"); + + ++cchLen; // Add 1 for null-terminator + + hr = StrAnsiAlloc(ppz, cchLen); + StrExitOnFailure(hr, "Failed to reallocate string"); + +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiTrimWhitespace - allocates or reuses dynamic string memory and copies + in an existing string, excluding whitespace + +NOTE: caller is responsible for freeing ppz even if function fails +********************************************************************/ +HRESULT DAPI StrAnsiTrimWhitespace( + __deref_out_z LPSTR* ppz, + __in_z LPCSTR szSource + ) +{ + HRESULT hr = S_OK; + size_t i = 0; + LPSTR sczResult = NULL; + + // Ignore beginning whitespace + while (' ' == *szSource || '\t' == *szSource) + { + szSource++; + } + + hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, &i); + StrExitOnRootFailure(hr, "Failed to get length of string"); + + // Overwrite ending whitespace with null characters + if (0 < i) + { + // start from the last non-null-terminator character in the array + for (i = i - 1; i > 0; --i) + { + if (L' ' != szSource[i] && L'\t' != szSource[i]) + { + break; + } + } + + ++i; + } + + hr = StrAnsiAllocStringAnsi(&sczResult, szSource, i); + StrExitOnFailure(hr, "Failed to copy result string"); + + // Output result + *ppz = sczResult; + sczResult = NULL; + +LExit: + ReleaseStr(sczResult); + + return hr; +} + +/******************************************************************** +StrAllocString - allocates or reuses dynamic string memory and copies in an existing string + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocString( + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + return AllocStringHelper(ppwz, wzSource, cchSource, FALSE); +} + +/******************************************************************** +StrAllocStringSecure - allocates or reuses dynamic string memory and +copies in an existing string. If the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocStringSecure( + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + return AllocStringHelper(ppwz, wzSource, cchSource, TRUE); +} + +/******************************************************************** +AllocStringHelper - allocates or reuses dynamic string memory and copies in an existing string +If fZeroOnRealloc is true and the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +static HRESULT AllocStringHelper( + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in BOOL fZeroOnRealloc + ) +{ + Assert(ppwz && wzSource); // && *wzSource); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + + if (*ppwz) + { + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters + } + + if (0 == cchSource && wzSource) + { + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); + StrExitOnRootFailure(hr, "failed to get length of source string"); + } + + SIZE_T cchNeeded; + hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator + StrExitOnRootFailure(hr, "source string is too long"); + + if (cch < cchNeeded) + { + cch = cchNeeded; + hr = AllocHelper(ppwz, cch, fZeroOnRealloc); + StrExitOnFailure(hr, "failed to allocate string from string."); + } + + // copy everything (the NULL terminator will be included) + hr = ::StringCchCopyNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiAllocString - allocates or reuses dynamic ANSI string memory and copies in an existing string + +NOTE: caller is responsible for freeing ppsz even if function fails +NOTE: cchSource must equal the length of wzSource (not including the NULL terminator) +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAllocString( + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in UINT uiCodepage + ) +{ + Assert(ppsz && wzSource); + + HRESULT hr = S_OK; + LPSTR psz = NULL; + SIZE_T cch = 0; + SIZE_T cchDest = cchSource; // at least enough + + if (*ppsz) + { + cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(CHAR); //convert the count in bytes to count in characters + } + + if (0 == cchSource) + { + cchDest = ::WideCharToMultiByte(uiCodepage, 0, wzSource, -1, NULL, 0, NULL, NULL); + if (0 == cchDest) + { + StrExitWithLastError(hr, "failed to get required size for conversion to ANSI: %ls", wzSource); + } + + --cchDest; // subtract one because WideChageToMultiByte includes space for the NULL terminator that we track below + } + else if (L'\0' == wzSource[cchSource - 1]) // if the source already had a null terminator, don't count that in the character count because we track it below + { + cchDest = cchSource - 1; + } + + if (cch < cchDest + 1) + { + cch = cchDest + 1; // add one for the NULL terminator + if (cch >= MAXDWORD / sizeof(WCHAR)) + { + hr = E_OUTOFMEMORY; + StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + } + + if (*ppsz) + { + psz = static_cast(MemReAlloc(*ppsz, sizeof(CHAR) * cch, TRUE)); + } + else + { + psz = static_cast(MemAlloc(sizeof(CHAR) * cch, TRUE)); + } + StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + + *ppsz = psz; + } + + if (0 == ::WideCharToMultiByte(uiCodepage, 0, wzSource, 0 == cchSource ? -1 : (int)cchSource, *ppsz, (int)cch, NULL, NULL)) + { + StrExitWithLastError(hr, "failed to convert to ansi: %ls", wzSource); + } + (*ppsz)[cchDest] = L'\0'; + +LExit: + return hr; +} + + +/******************************************************************** +StrAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing ANSI string + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource must equal the length of wzSource (not including the NULL terminator) +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocStringAnsi( + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz, + __in_z LPCSTR szSource, + __in SIZE_T cchSource, + __in UINT uiCodepage + ) +{ + Assert(ppwz && szSource); + + HRESULT hr = S_OK; + LPWSTR pwz = NULL; + SIZE_T cch = 0; + SIZE_T cchDest = cchSource; // at least enough + + if (*ppwz) + { + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters + } + + if (0 == cchSource) + { + cchDest = ::MultiByteToWideChar(uiCodepage, 0, szSource, -1, NULL, 0); + if (0 == cchDest) + { + StrExitWithLastError(hr, "failed to get required size for conversion to unicode: %s", szSource); + } + + --cchDest; //subtract one because MultiByteToWideChar includes space for the NULL terminator that we track below + } + else if (L'\0' == szSource[cchSource - 1]) // if the source already had a null terminator, don't count that in the character count because we track it below + { + cchDest = cchSource - 1; + } + + if (cch < cchDest + 1) + { + cch = cchDest + 1; + if (cch >= MAXDWORD / sizeof(WCHAR)) + { + hr = E_OUTOFMEMORY; + StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch); + } + + if (*ppwz) + { + pwz = static_cast(MemReAlloc(*ppwz, sizeof(WCHAR) * cch, TRUE)); + } + else + { + pwz = static_cast(MemAlloc(sizeof(WCHAR) * cch, TRUE)); + } + + StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch); + + *ppwz = pwz; + } + + if (0 == ::MultiByteToWideChar(uiCodepage, 0, szSource, 0 == cchSource ? -1 : (int)cchSource, *ppwz, (int)cch)) + { + StrExitWithLastError(hr, "failed to convert to unicode: %s", szSource); + } + (*ppwz)[cchDest] = L'\0'; + +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing string + +NOTE: caller is responsible for freeing ppsz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +HRESULT DAPI StrAnsiAllocStringAnsi( + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz, + __in_z LPCSTR szSource, + __in SIZE_T cchSource + ) +{ + Assert(ppsz && szSource); // && *szSource); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + + if (*ppsz) + { + cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnRootFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(CHAR); //convert the count in bytes to count in characters + } + + if (0 == cchSource && szSource) + { + hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); + StrExitOnRootFailure(hr, "failed to get length of source string"); + } + + SIZE_T cchNeeded; + hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator + StrExitOnRootFailure(hr, "source string is too long"); + + if (cch < cchNeeded) + { + cch = cchNeeded; + hr = StrAnsiAlloc(ppsz, cch); + StrExitOnFailure(hr, "failed to allocate string from string."); + } + + // copy everything (the NULL terminator will be included) +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchCopyNExA(*ppsz, cch, szSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); +#pragma prefast(pop) + +LExit: + return hr; +} + + +/******************************************************************** +StrAllocPrefix - allocates or reuses dynamic string memory and + prefixes a string + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchPrefix does not have to equal the length of wzPrefix +NOTE: if cchPrefix == 0, length of wzPrefix is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocPrefix( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzPrefix, + __in SIZE_T cchPrefix + ) +{ + Assert(ppwz && wzPrefix); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cchLen = 0; + + if (*ppwz) + { + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters + + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); + StrExitOnFailure(hr, "Failed to calculate length of string"); + } + + Assert(cchLen <= cch); + + if (0 == cchPrefix) + { + hr = ::StringCchLengthW(wzPrefix, STRSAFE_MAX_CCH, reinterpret_cast(&cchPrefix)); + StrExitOnFailure(hr, "Failed to calculate length of string"); + } + + if (cch - cchLen < cchPrefix + 1) + { + cch = cchPrefix + cchLen + 1; + hr = StrAlloc(ppwz, cch); + StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzPrefix); + } + + if (*ppwz) + { + SIZE_T cb = cch * sizeof(WCHAR); + SIZE_T cbPrefix = cchPrefix * sizeof(WCHAR); + + memmove(*ppwz + cchPrefix, *ppwz, cb - cbPrefix); + memcpy(*ppwz, wzPrefix, cbPrefix); + } + else + { + hr = E_UNEXPECTED; + StrExitOnFailure(hr, "for some reason our buffer is still null"); + } + +LExit: + return hr; +} + + +/******************************************************************** +StrAllocConcat - allocates or reuses dynamic string memory and adds an existing string + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocConcat( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + return AllocConcatHelper(ppwz, wzSource, cchSource, FALSE); +} + + +/******************************************************************** +StrAllocConcatSecure - allocates or reuses dynamic string memory and +adds an existing string. If the memory needs to reallocated, calls +SecureZeroMemory on the original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAllocConcatSecure( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + return AllocConcatHelper(ppwz, wzSource, cchSource, TRUE); +} + + +/******************************************************************** +AllocConcatHelper - allocates or reuses dynamic string memory and adds an existing string +If fZeroOnRealloc is true and the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +NOTE: cchSource does not have to equal the length of wzSource +NOTE: if cchSource == 0, length of wzSource is used instead +********************************************************************/ +static HRESULT AllocConcatHelper( + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in BOOL fZeroOnRealloc + ) +{ + Assert(ppwz && wzSource); // && *wzSource); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cchLen = 0; + + if (*ppwz) + { + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters + + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); + StrExitOnFailure(hr, "Failed to calculate length of string"); + } + + Assert(cchLen <= cch); + + if (0 == cchSource) + { + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); + StrExitOnFailure(hr, "Failed to calculate length of string"); + } + + if (cch - cchLen < cchSource + 1) + { + cch = (cchSource + cchLen + 1) * 2; + hr = AllocHelper(ppwz, cch, fZeroOnRealloc); + StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzSource); + } + + if (*ppwz) + { + hr = ::StringCchCatNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + } + else + { + hr = E_UNEXPECTED; + StrExitOnFailure(hr, "for some reason our buffer is still null"); + } + +LExit: + return hr; +} + + +/******************************************************************** +StrAnsiAllocConcat - allocates or reuses dynamic string memory and adds an existing string + +NOTE: caller is responsible for freeing ppz even if function fails +NOTE: cchSource does not have to equal the length of pzSource +NOTE: if cchSource == 0, length of pzSource is used instead +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAllocConcat( + __deref_out_z LPSTR* ppz, + __in_z LPCSTR pzSource, + __in SIZE_T cchSource + ) +{ + Assert(ppz && pzSource); // && *pzSource); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + SIZE_T cchLen = 0; + + if (*ppz) + { + cch = MemSize(*ppz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(CHAR); // convert the count in bytes to count in characters + +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); +#pragma prefast(pop) + StrExitOnFailure(hr, "Failed to calculate length of string"); + } + + Assert(cchLen <= cch); + + if (0 == cchSource) + { +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchLengthA(pzSource, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); +#pragma prefast(pop) + StrExitOnFailure(hr, "Failed to calculate length of string"); + } + + if (cch - cchLen < cchSource + 1) + { + cch = (cchSource + cchLen + 1) * 2; + hr = StrAnsiAlloc(ppz, cch); + StrExitOnFailure(hr, "failed to allocate string from string: %hs", pzSource); + } + + if (*ppz) + { +#pragma prefast(push) +#pragma prefast(disable:25068) + hr = ::StringCchCatNExA(*ppz, cch, pzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); +#pragma prefast(pop) + } + else + { + hr = E_UNEXPECTED; + StrExitOnFailure(hr, "for some reason our buffer is still null"); + } + +LExit: + return hr; +} + + +/******************************************************************** +StrAllocFormatted - allocates or reuses dynamic string memory and formats it + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT __cdecl StrAllocFormatted( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgs(ppwz, wzFormat, args); + va_end(args); + + return hr; +} + + +/******************************************************************** +StrAllocConcatFormatted - allocates or reuses dynamic string memory +and adds a formatted string + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT __cdecl StrAllocConcatFormatted( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + LPWSTR sczFormatted = NULL; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgs(&sczFormatted, wzFormat, args); + va_end(args); + StrExitOnFailure(hr, "Failed to allocate formatted string"); + + hr = StrAllocConcat(ppwz, sczFormatted, 0); + +LExit: + ReleaseStr(sczFormatted); + + return hr; +} + + +/******************************************************************** +StrAllocConcatFormattedSecure - allocates or reuses dynamic string +memory and adds a formatted string. If the memory needs to be +reallocated, calls SecureZeroMemory on original block of memory after +it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT __cdecl StrAllocConcatFormattedSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + LPWSTR sczFormatted = NULL; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgsSecure(&sczFormatted, wzFormat, args); + va_end(args); + StrExitOnFailure(hr, "Failed to allocate formatted string"); + + hr = StrAllocConcatSecure(ppwz, sczFormatted, 0); + +LExit: + ReleaseStr(sczFormatted); + + return hr; +} + + +/******************************************************************** +StrAllocFormattedSecure - allocates or reuses dynamic string memory +and formats it. If the memory needs to be reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT __cdecl StrAllocFormattedSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args); + va_end(args); + + return hr; +} + + +/******************************************************************** +StrAnsiAllocFormatted - allocates or reuses dynamic ANSI string memory and formats it + +NOTE: caller is responsible for freeing ppsz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAllocFormatted( + __deref_out_z LPSTR* ppsz, + __in __format_string LPCSTR szFormat, + ... + ) +{ + Assert(ppsz && szFormat && *szFormat); + + HRESULT hr = S_OK; + va_list args; + + va_start(args, szFormat); + hr = StrAnsiAllocFormattedArgs(ppsz, szFormat, args); + va_end(args); + + return hr; +} + + +/******************************************************************** +StrAllocFormattedArgs - allocates or reuses dynamic string memory +and formats it with the passed in args + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAllocFormattedArgs( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ) +{ + return AllocFormattedArgsHelper(ppwz, FALSE, wzFormat, args); +} + + +/******************************************************************** +StrAllocFormattedArgsSecure - allocates or reuses dynamic string memory +and formats it with the passed in args. + +If the memory needs to reallocated, calls SecureZeroMemory on the +original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAllocFormattedArgsSecure( + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ) +{ + return AllocFormattedArgsHelper(ppwz, TRUE, wzFormat, args); +} + + +/******************************************************************** +AllocFormattedArgsHelper - allocates or reuses dynamic string memory +and formats it with the passed in args. + +If fZeroOnRealloc is true and the memory needs to reallocated, +calls SecureZeroMemory on original block of memory after it is moved. + +NOTE: caller is responsible for freeing ppwz even if function fails +********************************************************************/ +static HRESULT AllocFormattedArgsHelper( + __deref_out_z LPWSTR* ppwz, + __in BOOL fZeroOnRealloc, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ) +{ + Assert(ppwz && wzFormat && *wzFormat); + + HRESULT hr = S_OK; + SIZE_T cch = 0; + LPWSTR pwzOriginal = NULL; + SIZE_T cbOriginal = 0; + size_t cchOriginal = 0; + + if (*ppwz) + { + cbOriginal = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cbOriginal) + { + hr = E_INVALIDARG; + StrExitOnRootFailure(hr, "failed to get size of destination string"); + } + + cch = cbOriginal / sizeof(WCHAR); //convert the count in bytes to count in characters + + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, &cchOriginal); + StrExitOnRootFailure(hr, "failed to get length of original string"); + } + + if (0 == cch) // if there is no space in the string buffer + { + cch = 256; + + hr = AllocHelper(ppwz, cch, fZeroOnRealloc); + StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); + } + + // format the message (grow until it fits or there is a failure) + do + { + hr = ::StringCchVPrintfW(*ppwz, cch, wzFormat, args); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + if (!pwzOriginal) + { + // this allows you to pass the original string as a formatting argument and not crash + // save the original string and free it after the printf is complete + pwzOriginal = *ppwz; + *ppwz = NULL; + + // StringCchVPrintfW starts writing to the string... + // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...); + pwzOriginal[cchOriginal] = 0; + } + + cch *= 2; + + hr = AllocHelper(ppwz, cch, fZeroOnRealloc); + StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat); + + hr = S_FALSE; + } + } while (S_FALSE == hr); + StrExitOnRootFailure(hr, "failed to format string"); + +LExit: + if (pwzOriginal && fZeroOnRealloc) + { + SecureZeroMemory(pwzOriginal, cbOriginal); + } + + ReleaseStr(pwzOriginal); + + return hr; +} + + +/******************************************************************** +StrAnsiAllocFormattedArgs - allocates or reuses dynamic ANSI string memory +and formats it with the passed in args + +NOTE: caller is responsible for freeing ppsz even if function fails +********************************************************************/ +extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs( + __deref_out_z LPSTR* ppsz, + __in __format_string LPCSTR szFormat, + __in va_list args + ) +{ + Assert(ppsz && szFormat && *szFormat); + + HRESULT hr = S_OK; + SIZE_T cch = *ppsz ? MemSize(*ppsz) / sizeof(CHAR) : 0; + LPSTR pszOriginal = NULL; + size_t cchOriginal = 0; + + if (*ppsz) + { + cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1) + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnRootFailure(hr, "failed to get size of destination string"); + } + cch /= sizeof(CHAR); //convert the count in bytes to count in characters + + hr = ::StringCchLengthA(*ppsz, STRSAFE_MAX_CCH, &cchOriginal); + StrExitOnRootFailure(hr, "failed to get length of original string"); + } + + if (0 == cch) // if there is no space in the string buffer + { + cch = 256; + hr = StrAnsiAlloc(ppsz, cch); + StrExitOnFailure(hr, "failed to allocate string to format: %s", szFormat); + } + + // format the message (grow until it fits or there is a failure) + do + { +#pragma prefast(push) +#pragma prefast(disable:25068) // We intentionally don't use the unicode API here + hr = ::StringCchVPrintfA(*ppsz, cch, szFormat, args); +#pragma prefast(pop) + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + if (!pszOriginal) + { + // this allows you to pass the original string as a formatting argument and not crash + // save the original string and free it after the printf is complete + pszOriginal = *ppsz; + *ppsz = NULL; + // StringCchVPrintfW starts writing to the string... + // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...); + pszOriginal[cchOriginal] = 0; + } + cch *= 2; + hr = StrAnsiAlloc(ppsz, cch); + StrExitOnFailure(hr, "failed to allocate string to format: %hs", szFormat); + hr = S_FALSE; + } + } while (S_FALSE == hr); + StrExitOnRootFailure(hr, "failed to format string"); + +LExit: + ReleaseStr(pszOriginal); + + return hr; +} + + +/******************************************************************** +StrAllocFromError - returns the string for a particular error. + +********************************************************************/ +extern "C" HRESULT DAPI StrAllocFromError( + __inout LPWSTR *ppwzMessage, + __in HRESULT hrError, + __in_opt HMODULE hModule, + ... + ) +{ + HRESULT hr = S_OK; + DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM; + LPVOID pvMessage = NULL; + DWORD cchMessage = 0; + + if (hModule) + { + dwFlags |= FORMAT_MESSAGE_FROM_HMODULE; + } + + va_list args; + va_start(args, hModule); + cchMessage = ::FormatMessageW(dwFlags, static_cast(hModule), hrError, 0, reinterpret_cast(&pvMessage), 0, &args); + va_end(args); + + if (0 == cchMessage) + { + StrExitWithLastError(hr, "Failed to format message for error: 0x%x", hrError); + } + + hr = StrAllocString(ppwzMessage, reinterpret_cast(pvMessage), cchMessage); + StrExitOnFailure(hr, "Failed to allocate string for message."); + +LExit: + if (pvMessage) + { + ::LocalFree(pvMessage); + } + + return hr; +} + + +/******************************************************************** +StrMaxLength - returns maximum number of characters that can be stored in dynamic string p + +NOTE: assumes Unicode string +********************************************************************/ +extern "C" HRESULT DAPI StrMaxLength( + __in LPCVOID p, + __out SIZE_T* pcch + ) +{ + Assert(pcch); + + HRESULT hr = S_OK; + + if (p) + { + *pcch = MemSize(p); // get size of entire buffer + if (-1 == *pcch) + { + ExitFunction1(hr = E_FAIL); + } + + *pcch /= sizeof(WCHAR); // reduce to count of characters + } + else + { + *pcch = 0; + } + Assert(S_OK == hr); + +LExit: + return hr; +} + + +/******************************************************************** +StrSize - returns count of bytes in dynamic string p + +********************************************************************/ +extern "C" HRESULT DAPI StrSize( + __in LPCVOID p, + __out SIZE_T* pcb + ) +{ + Assert(p && pcb); + + HRESULT hr = S_OK; + + *pcb = MemSize(p); + if (-1 == *pcb) + { + hr = E_FAIL; + } + + return hr; +} + +/******************************************************************** +StrFree - releases dynamic string memory allocated by any StrAlloc*() functions + +********************************************************************/ +extern "C" HRESULT DAPI StrFree( + __in LPVOID p + ) +{ + Assert(p); + + HRESULT hr = MemFree(p); + StrExitOnFailure(hr, "failed to free string"); + +LExit: + return hr; +} + + +/**************************************************************************** +StrReplaceStringAll - Replaces wzOldSubString in ppOriginal with a wzNewSubString. +Replaces all instances. + +****************************************************************************/ +extern "C" HRESULT DAPI StrReplaceStringAll( + __inout LPWSTR* ppwzOriginal, + __in_z LPCWSTR wzOldSubString, + __in_z LPCWSTR wzNewSubString + ) +{ + HRESULT hr = S_OK; + DWORD dwStartIndex = 0; + + do + { + hr = StrReplaceString(ppwzOriginal, &dwStartIndex, wzOldSubString, wzNewSubString); + StrExitOnFailure(hr, "Failed to replace substring"); + } + while (S_OK == hr); + + hr = (0 == dwStartIndex) ? S_FALSE : S_OK; + +LExit: + return hr; +} + + +/**************************************************************************** +StrReplaceString - Replaces wzOldSubString in ppOriginal with a wzNewSubString. +Search for old substring starts at dwStartIndex. Does only 1 replace. + +****************************************************************************/ +extern "C" HRESULT DAPI StrReplaceString( + __inout LPWSTR* ppwzOriginal, + __inout DWORD* pdwStartIndex, + __in_z LPCWSTR wzOldSubString, + __in_z LPCWSTR wzNewSubString + ) +{ + Assert(ppwzOriginal && wzOldSubString && wzNewSubString); + + HRESULT hr = S_FALSE; + LPCWSTR wzSubLocation = NULL; + LPWSTR pwzBuffer = NULL; + size_t cchOldSubString = 0; + size_t cchNewSubString = 0; + + if (!*ppwzOriginal) + { + ExitFunction(); + } + + wzSubLocation = wcsstr(*ppwzOriginal + *pdwStartIndex, wzOldSubString); + if (!wzSubLocation) + { + ExitFunction(); + } + + if (wzOldSubString) + { + hr = ::StringCchLengthW(wzOldSubString, STRSAFE_MAX_CCH, &cchOldSubString); + StrExitOnRootFailure(hr, "Failed to get old string length."); + } + + if (wzNewSubString) + { + hr = ::StringCchLengthW(wzNewSubString, STRSAFE_MAX_CCH, &cchNewSubString); + StrExitOnRootFailure(hr, "Failed to get new string length."); + } + + hr = ::PtrdiffTToDWord(wzSubLocation - *ppwzOriginal, pdwStartIndex); + StrExitOnRootFailure(hr, "Failed to diff pointers."); + + hr = StrAllocString(&pwzBuffer, *ppwzOriginal, wzSubLocation - *ppwzOriginal); + StrExitOnFailure(hr, "Failed to duplicate string."); + + pwzBuffer[wzSubLocation - *ppwzOriginal] = '\0'; + + hr = StrAllocConcat(&pwzBuffer, wzNewSubString, 0); + StrExitOnFailure(hr, "Failed to append new string."); + + hr = StrAllocConcat(&pwzBuffer, wzSubLocation + cchOldSubString, 0); + StrExitOnFailure(hr, "Failed to append post string."); + + hr = StrFree(*ppwzOriginal); + StrExitOnFailure(hr, "Failed to free original string."); + + *ppwzOriginal = pwzBuffer; + *pdwStartIndex = *pdwStartIndex + static_cast(cchNewSubString); + hr = S_OK; + +LExit: + return hr; +} + + +static inline BYTE HexCharToByte( + __in WCHAR wc + ) +{ + Assert(L'0' <= wc && wc <= L'9' || L'a' <= wc && wc <= L'f' || L'A' <= wc && wc <= L'F'); // make sure wc is a hex character + + BYTE b; + if (L'0' <= wc && wc <= L'9') + { + b = (BYTE)(wc - L'0'); + } + else if ('a' <= wc && wc <= 'f') + { + b = (BYTE)(wc - L'0' - (L'a' - L'9' - 1)); + } + else // must be (L'A' <= wc && wc <= L'F') + { + b = (BYTE)(wc - L'0' - (L'A' - L'9' - 1)); + } + + Assert(0 <= b && b <= 15); + return b; +} + + +/**************************************************************************** +StrHexEncode - converts an array of bytes to a text string + +NOTE: wzDest must have space for cbSource * 2 + 1 characters +****************************************************************************/ +extern "C" HRESULT DAPI StrHexEncode( + __in_ecount(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __out_ecount(cchDest) LPWSTR wzDest, + __in SIZE_T cchDest + ) +{ + Assert(pbSource && wzDest); + + HRESULT hr = S_OK; + DWORD i; + BYTE b; + + if (cchDest < 2 * cbSource + 1) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); + } + + for (i = 0; i < cbSource; ++i) + { + b = (*pbSource) >> 4; + *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1)); + b = (*pbSource) & 0xF; + *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1)); + + ++pbSource; + } + + *wzDest = 0; + +LExit: + return hr; +} + + +/**************************************************************************** +StrAllocHexEncode - converts an array of bytes to an allocated text string + +****************************************************************************/ +HRESULT DAPI StrAllocHexEncode( + __in_ecount(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest + ) +{ + HRESULT hr = S_OK; + SIZE_T cchSource = sizeof(WCHAR) * (cbSource + 1); + + hr = StrAlloc(ppwzDest, cchSource); + StrExitOnFailure(hr, "Failed to allocate hex string."); + + hr = StrHexEncode(pbSource, cbSource, *ppwzDest, cchSource); + StrExitOnFailure(hr, "Failed to encode hex string."); + +LExit: + return hr; +} + + +/**************************************************************************** +StrHexDecode - converts a string of text to array of bytes + +NOTE: wzSource must contain even number of characters +****************************************************************************/ +extern "C" HRESULT DAPI StrHexDecode( + __in_z LPCWSTR wzSource, + __out_bcount(cbDest) BYTE* pbDest, + __in SIZE_T cbDest + ) +{ + Assert(wzSource && pbDest); + + HRESULT hr = S_OK; + size_t cchSource = 0; + size_t i = 0; + BYTE b = 0; + + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource); + StrExitOnRootFailure(hr, "Failed to get length of hex string: %ls", wzSource); + + Assert(0 == cchSource % 2); + if (cbDest < cchSource / 2) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + StrExitOnRootFailure(hr, "Insufficient buffer to decode string '%ls' len: %Iu into %Iu bytes.", wzSource, cchSource, cbDest); + } + + for (i = 0; i < cchSource / 2; ++i) + { + b = HexCharToByte(*wzSource++); + (*pbDest) = b << 4; + + b = HexCharToByte(*wzSource++); + (*pbDest) |= b & 0xF; + + ++pbDest; + } + +LExit: + return hr; +} + + +/**************************************************************************** +StrAllocHexDecode - allocates a byte array hex-converted from string of text + +NOTE: wzSource must contain even number of characters +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocHexDecode( + __in_z LPCWSTR wzSource, + __out_bcount(*pcbDest) BYTE** ppbDest, + __out_opt DWORD* pcbDest + ) +{ + Assert(wzSource && *wzSource && ppbDest); + + HRESULT hr = S_OK; + size_t cch = 0; + BYTE* pb = NULL; + DWORD cb = 0; + + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cch); + StrExitOnFailure(hr, "Failed to calculate length of source string."); + + if (cch % 2) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "Invalid source parameter, string must be even length or it cannot be decoded."); + } + + cb = static_cast(cch / 2); + pb = static_cast(MemAlloc(cb, TRUE)); + StrExitOnNull(pb, hr, E_OUTOFMEMORY, "Failed to allocate memory for hex decode."); + + hr = StrHexDecode(wzSource, pb, cb); + StrExitOnFailure(hr, "Failed to decode source string."); + + *ppbDest = pb; + pb = NULL; + + if (pcbDest) + { + *pcbDest = cb; + } + +LExit: + ReleaseMem(pb); + + return hr; +} + + +/**************************************************************************** +Base85 encoding/decoding data + +****************************************************************************/ +const WCHAR Base85EncodeTable[] = L"!%'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~"; + +const BYTE Base85DecodeTable[256] = +{ + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 0, 85, 85, 85, 1, 85, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 85, 85, 85, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 85, 52, 53, 54, + 85, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85 +}; + +const UINT Base85PowerTable[4] = { 1, 85, 85*85, 85*85*85 }; + + +/**************************************************************************** +StrAllocBase85Encode - converts an array of bytes into an XML compatible string + +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocBase85Encode( + __in_bcount_opt(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __deref_out_z LPWSTR* pwzDest + ) +{ + HRESULT hr = S_OK; + SIZE_T cchDest = 0; + LPWSTR wzDest; + DWORD_PTR iSource = 0; + DWORD_PTR iDest = 0; + + if (!pwzDest || !pbSource) + { + return E_INVALIDARG; + } + + // calc actual size of output + cchDest = cbSource / 4; + cchDest += cchDest * 4; + if (cbSource & 3) + { + cchDest += (cbSource & 3) + 1; + } + ++cchDest; // add room for null terminator + + hr = StrAlloc(pwzDest, cchDest); + StrExitOnFailure(hr, "failed to allocate destination string"); + + wzDest = *pwzDest; + + // first, encode full words + for (iSource = 0, iDest = 0; (iSource + 4 < cbSource) && (iDest + 5 < cchDest); iSource += 4, iDest += 5) + { + DWORD n = pbSource[iSource] + (pbSource[iSource + 1] << 8) + (pbSource[iSource + 2] << 16) + (pbSource[iSource + 3] << 24); + DWORD k = n / 85; + + //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); + wzDest[iDest] = Base85EncodeTable[n - k * 85]; + n = k / 85; + + //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable)); + wzDest[iDest + 1] = Base85EncodeTable[k - n * 85]; + k = n / 85; + + //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); + wzDest[iDest + 2] = Base85EncodeTable[n - k * 85]; + n = k / 85; + + //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable)); + wzDest[iDest + 3] = Base85EncodeTable[k - n * 85]; + + __assume(n <= DWORD_MAX / 85 / 85 / 85 / 85); + + //Assert(0 <= n && n < countof(Base85EncodeTable)); + wzDest[iDest + 4] = Base85EncodeTable[n]; + } + + // encode any remaining bytes + if (iSource < cbSource) + { + DWORD n = 0; + for (DWORD i = 0; iSource + i < cbSource; ++i) + { + n += pbSource[iSource + i] << (i << 3); + } + + for (/* iSource already initialized */; iSource < cbSource && iDest < cchDest; ++iSource, ++iDest) + { + DWORD k = n / 85; + + //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable)); + wzDest[iDest] = Base85EncodeTable[n - k * 85]; + + n = k; + } + + wzDest[iDest] = Base85EncodeTable[n]; + ++iDest; + } + Assert(iSource == cbSource); + Assert(iDest == cchDest - 1); + + wzDest[iDest] = L'\0'; + hr = S_OK; + +LExit: + return hr; +} + + +/**************************************************************************** +StrAllocBase85Decode - converts a string of text to array of bytes + +NOTE: Use MemFree() to release the allocated stream of bytes +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocBase85Decode( + __in_z LPCWSTR wzSource, + __deref_out_bcount(*pcbDest) BYTE** ppbDest, + __out SIZE_T* pcbDest + ) +{ + HRESULT hr = S_OK; + size_t cchSource = 0; + DWORD_PTR i, n, k; + + BYTE* pbDest = 0; + SIZE_T cbDest = 0; + + if (!wzSource || !ppbDest || !pcbDest) + { + ExitFunction1(hr = E_INVALIDARG); + } + + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource); + StrExitOnRootFailure(hr, "failed to get length of base 85 string: %ls", wzSource); + + // evaluate size of output and check it + k = cchSource / 5; + cbDest = k << 2; + k = cchSource - k * 5; + if (k) + { + if (1 == k) + { + // decode error -- encoded size cannot equal 1 mod 5 + return E_UNEXPECTED; + } + + cbDest += k - 1; + } + + *ppbDest = static_cast(MemAlloc(cbDest, FALSE)); + StrExitOnNull(*ppbDest, hr, E_OUTOFMEMORY, "failed allocate memory to decode the string"); + + pbDest = *ppbDest; + *pcbDest = cbDest; + + // decode full words first + while (5 <= cchSource) + { + k = Base85DecodeTable[wzSource[0]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + n = k; + + k = Base85DecodeTable[wzSource[1]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + n += k * 85; + + k = Base85DecodeTable[wzSource[2]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + n += k * (85 * 85); + + k = Base85DecodeTable[wzSource[3]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + n += k * (85 * 85 * 85); + + k = Base85DecodeTable[wzSource[4]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + k *= (85 * 85 * 85 * 85); + + // if (k + n > (1u << 32)) <=> (k > ~n) then decode error + if (k > ~n) + { + // overflow + return E_UNEXPECTED; + } + + n += k; + + pbDest[0] = (BYTE) n; + pbDest[1] = (BYTE) (n >> 8); + pbDest[2] = (BYTE) (n >> 16); + pbDest[3] = (BYTE) (n >> 24); + + wzSource += 5; + pbDest += 4; + cchSource -= 5; + } + + if (cchSource) + { + n = 0; + for (i = 0; i < cchSource; ++i) + { + k = Base85DecodeTable[wzSource[i]]; + if (85 == k) + { + // illegal symbol + return E_UNEXPECTED; + } + + n += k * Base85PowerTable[i]; + } + + for (i = 1; i < cchSource; ++i) + { + *pbDest++ = (BYTE)n; + n >>= 8; + } + + if (0 != n) + { + // decode error + return E_UNEXPECTED; + } + } + + hr = S_OK; + +LExit: + return hr; +} + + +/**************************************************************************** +MultiSzLen - calculates the length of a MULTISZ string including all nulls +including the double null terminator at the end of the MULTISZ. + +NOTE: returns 0 if the multisz in not properly terminated with two nulls +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzLen( + __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz, + __out SIZE_T* pcch +) +{ + Assert(pcch); + + HRESULT hr = S_OK; + LPCWSTR wz = pwzMultiSz; + DWORD_PTR dwMaxSize = 0; + + hr = StrMaxLength(pwzMultiSz, &dwMaxSize); + StrExitOnFailure(hr, "failed to get the max size of a string while calculating MULTISZ length"); + + *pcch = 0; + while (*pcch < dwMaxSize) + { + if (L'\0' == *wz && L'\0' == *(wz + 1)) + { + break; + } + + ++wz; + *pcch = *pcch + 1; + } + + // Add two for the last 2 NULLs (that we looked ahead at) + *pcch = *pcch + 2; + + // If we've walked off the end then the length is 0 + if (*pcch > dwMaxSize) + { + *pcch = 0; + } + +LExit: + return hr; +} + + +/**************************************************************************** +MultiSzPrepend - prepends a string onto the front of a MUTLISZ + +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzPrepend( + __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz, + __inout_opt SIZE_T* pcchMultiSz, + __in __nullnullterminated LPCWSTR pwzInsert + ) +{ + Assert(ppwzMultiSz && pwzInsert && *pwzInsert); + + HRESULT hr =S_OK; + LPWSTR pwzResult = NULL; + SIZE_T cchResult = 0; + SIZE_T cchInsert = 0; + SIZE_T cchMultiSz = 0; + + // Get the lengths of the MULTISZ (and prime it if it's not initialized) + if (pcchMultiSz && 0 != *pcchMultiSz) + { + cchMultiSz = *pcchMultiSz; + } + else + { + hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); + StrExitOnFailure(hr, "failed to get length of multisz"); + } + + hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast(&cchInsert)); + StrExitOnRootFailure(hr, "failed to get length of insert string"); + + cchResult = cchInsert + cchMultiSz + 1; + + // Allocate the result buffer + hr = StrAlloc(&pwzResult, cchResult + 1); + StrExitOnFailure(hr, "failed to allocate result string"); + + // Prepend + hr = ::StringCchCopyW(pwzResult, cchResult, pwzInsert); + StrExitOnRootFailure(hr, "failed to copy prepend string: %ls", pwzInsert); + + // If there was no MULTISZ, double null terminate our result, otherwise, copy the MULTISZ in + if (0 == cchMultiSz) + { + pwzResult[cchResult] = L'\0'; + ++cchResult; + } + else + { + // Copy the rest + ::CopyMemory(pwzResult + cchInsert + 1, *ppwzMultiSz, cchMultiSz * sizeof(WCHAR)); + + // Free the old buffer + ReleaseNullStr(*ppwzMultiSz); + } + + // Set the result + *ppwzMultiSz = pwzResult; + + if (pcchMultiSz) + { + *pcchMultiSz = cchResult; + } + + pwzResult = NULL; + +LExit: + ReleaseNullStr(pwzResult); + + return hr; +} + +/**************************************************************************** +MultiSzFindSubstring - case insensitive find of a string in a MULTISZ that contains the +specified sub string and returns the index of the +string in the MULTISZ, the address, neither, or both + +NOTE: returns S_FALSE if the string is not found +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzFindSubstring( + __in __nullnullterminated LPCWSTR pwzMultiSz, + __in __nullnullterminated LPCWSTR pwzSubstring, + __out_opt DWORD_PTR* pdwIndex, + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn + ) +{ + Assert(pwzMultiSz && *pwzMultiSz && pwzSubstring && *pwzSubstring); + + HRESULT hr = S_FALSE; // Assume we won't find it (the glass is half empty) + LPCWSTR wz = pwzMultiSz; + DWORD_PTR dwIndex = 0; + SIZE_T cchMultiSz = 0; + SIZE_T cchProgress = 0; + + hr = MultiSzLen(pwzMultiSz, &cchMultiSz); + StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + + // Find the string containing the sub string + hr = S_OK; + while (NULL == wcsistr(wz, pwzSubstring)) + { + // Slide through to the end of the current string + while (L'\0' != *wz && cchProgress < cchMultiSz) + { + ++wz; + ++cchProgress; + } + + // If we're done, we're done + if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz) + { + hr = S_FALSE; + break; + } + + // Move on to the next string + ++wz; + ++dwIndex; + } + Assert(S_OK == hr || S_FALSE == hr); + + // If we found it give them what they want + if (S_OK == hr) + { + if (pdwIndex) + { + *pdwIndex = dwIndex; + } + + if (ppwzFoundIn) + { + *ppwzFoundIn = wz; + } + } + +LExit: + return hr; +} + +/**************************************************************************** +MultiSzFindString - finds a string in a MULTISZ and returns the index of +the string in the MULTISZ, the address or both + +NOTE: returns S_FALSE if the string is not found +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzFindString( + __in __nullnullterminated LPCWSTR pwzMultiSz, + __in __nullnullterminated LPCWSTR pwzString, + __out_opt DWORD_PTR* pdwIndex, + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound + ) +{ + Assert(pwzMultiSz && *pwzMultiSz && pwzString && *pwzString && (pdwIndex || ppwzFound)); + + HRESULT hr = S_FALSE; // Assume we won't find it + LPCWSTR wz = pwzMultiSz; + DWORD_PTR dwIndex = 0; + SIZE_T cchMutliSz = 0; + SIZE_T cchProgress = 0; + + hr = MultiSzLen(pwzMultiSz, &cchMutliSz); + StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + + // Find the string + hr = S_OK; + while (0 != lstrcmpW(wz, pwzString)) + { + // Slide through to the end of the current string + while (L'\0' != *wz && cchProgress < cchMutliSz) + { + ++wz; + ++cchProgress; + } + + // If we're done, we're done + if (L'\0' == *(wz + 1) || cchProgress >= cchMutliSz) + { + hr = S_FALSE; + break; + } + + // Move on to the next string + ++wz; + ++dwIndex; + } + Assert(S_OK == hr || S_FALSE == hr); + + // If we found it give them what they want + if (S_OK == hr) + { + if (pdwIndex) + { + *pdwIndex = dwIndex; + } + + if (ppwzFound) + { + *ppwzFound = wz; + } + } + +LExit: + return hr; +} + +/**************************************************************************** +MultiSzRemoveString - removes string from a MULTISZ at the specified +index + +NOTE: does an in place removal without shrinking the memory allocation + +NOTE: returns S_FALSE if the MULTISZ has fewer strings than dwIndex +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzRemoveString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __in DWORD_PTR dwIndex + ) +{ + Assert(ppwzMultiSz && *ppwzMultiSz); + + HRESULT hr = S_OK; + LPCWSTR wz = *ppwzMultiSz; + LPCWSTR wzNext = NULL; + DWORD_PTR dwCurrentIndex = 0; + SIZE_T cchMultiSz = 0; + SIZE_T cchProgress = 0; + + hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); + StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + + // Find the index we want to remove + hr = S_OK; + while (dwCurrentIndex < dwIndex) + { + // Slide through to the end of the current string + while (L'\0' != *wz && cchProgress < cchMultiSz) + { + ++wz; + ++cchProgress; + } + + // If we're done, we're done + if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz) + { + hr = S_FALSE; + break; + } + + // Move on to the next string + ++wz; + ++cchProgress; + ++dwCurrentIndex; + } + Assert(S_OK == hr || S_FALSE == hr); + + // If we found the index to be removed + if (S_OK == hr) + { + wzNext = wz; + + // Slide through to the end of the current string + while (L'\0' != *wzNext && cchProgress < cchMultiSz) + { + ++wzNext; + ++cchProgress; + } + + // Something weird has happened if we're past the end of the MULTISZ + if (cchProgress > cchMultiSz) + { + hr = E_UNEXPECTED; + StrExitOnFailure(hr, "failed to move past the string to be removed from MULTISZ"); + } + + // Move on to the next character + ++wzNext; + ++cchProgress; + + ::MoveMemory((LPVOID)wz, (LPVOID)wzNext, (cchMultiSz - cchProgress) * sizeof(WCHAR)); + } + +LExit: + return hr; +} + +/**************************************************************************** +MultiSzInsertString - inserts new string at the specified index + +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzInsertString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __inout_opt SIZE_T* pcchMultiSz, + __in DWORD_PTR dwIndex, + __in_z LPCWSTR pwzInsert + ) +{ + Assert(ppwzMultiSz && pwzInsert && *pwzInsert); + + HRESULT hr = S_OK; + LPCWSTR wz = *ppwzMultiSz; + DWORD_PTR dwCurrentIndex = 0; + SIZE_T cchProgress = 0; + LPWSTR pwzResult = NULL; + SIZE_T cchResult = 0; + SIZE_T cchString = 0; + SIZE_T cchMultiSz = 0; + + hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast(&cchString)); + StrExitOnRootFailure(hr, "failed to get length of insert string"); + + if (pcchMultiSz && 0 != *pcchMultiSz) + { + cchMultiSz = *pcchMultiSz; + } + else + { + hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz); + StrExitOnFailure(hr, "failed to get the length of a MULTISZ string"); + } + + // Find the index we want to insert at + hr = S_OK; + while (dwCurrentIndex < dwIndex) + { + // Slide through to the end of the current string + while (L'\0' != *wz && cchProgress < cchMultiSz) + { + ++wz; + ++cchProgress; + } + + // If we're done, we're done + if ((dwCurrentIndex + 1 != dwIndex && L'\0' == *(wz + 1)) || cchProgress >= cchMultiSz) + { + hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND); + StrExitOnRootFailure(hr, "requested to insert into an invalid index: %u in a MULTISZ", dwIndex); + } + + // Move on to the next string + ++wz; + ++cchProgress; + ++dwCurrentIndex; + } + + // + // Insert the string + // + cchResult = cchMultiSz + cchString + 1; + + hr = StrAlloc(&pwzResult, cchResult); + StrExitOnFailure(hr, "failed to allocate result string for MULTISZ insert"); + + // Copy the part before the insert + ::CopyMemory(pwzResult, *ppwzMultiSz, cchProgress * sizeof(WCHAR)); + + // Copy the insert part + ::CopyMemory(pwzResult + cchProgress, pwzInsert, (cchString + 1) * sizeof(WCHAR)); + + // Copy the part after the insert + ::CopyMemory(pwzResult + cchProgress + cchString + 1, wz, (cchMultiSz - cchProgress) * sizeof(WCHAR)); + + // Free the old buffer + ReleaseNullStr(*ppwzMultiSz); + + // Set the result + *ppwzMultiSz = pwzResult; + + // If they wanted the resulting length, let 'em have it + if (pcchMultiSz) + { + *pcchMultiSz = cchResult; + } + + pwzResult = NULL; + +LExit: + ReleaseStr(pwzResult); + + return hr; +} + +/**************************************************************************** +MultiSzReplaceString - replaces string at the specified index with a new one + +****************************************************************************/ +extern "C" HRESULT DAPI MultiSzReplaceString( + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz, + __in DWORD_PTR dwIndex, + __in_z LPCWSTR pwzString + ) +{ + Assert(ppwzMultiSz && pwzString && *pwzString); + + HRESULT hr = S_OK; + + hr = MultiSzRemoveString(ppwzMultiSz, dwIndex); + StrExitOnFailure(hr, "failed to remove string from MULTISZ at the specified index: %u", dwIndex); + + hr = MultiSzInsertString(ppwzMultiSz, NULL, dwIndex, pwzString); + StrExitOnFailure(hr, "failed to insert string into MULTISZ at the specified index: %u", dwIndex); + +LExit: + return hr; +} + + +/**************************************************************************** +wcsistr - case insensitive find a substring + +****************************************************************************/ +extern "C" LPCWSTR DAPI wcsistr( + __in_z LPCWSTR wzString, + __in_z LPCWSTR wzCharSet + ) +{ + LPCWSTR wzSource = wzString; + LPCWSTR wzSearch = NULL; + SIZE_T cchSourceIndex = 0; + + // Walk through wzString (the source string) one character at a time + while (*wzSource) + { + cchSourceIndex = 0; + wzSearch = wzCharSet; + + // Look ahead in the source string until we get a full match or we hit the end of the source + while (L'\0' != wzSource[cchSourceIndex] && L'\0' != *wzSearch && towlower(wzSource[cchSourceIndex]) == towlower(*wzSearch)) + { + ++cchSourceIndex; + ++wzSearch; + } + + // If we found it, return the point that we found it at + if (L'\0' == *wzSearch) + { + return wzSource; + } + + // Walk ahead one character + ++wzSource; + } + + return NULL; +} + +/**************************************************************************** +StrStringToInt16 - converts a string to a signed 16-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToInt16( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out SHORT* psOut + ) +{ + HRESULT hr = S_OK; + LONGLONG ll = 0; + + hr = StrStringToInt64(wzIn, cchIn, &ll); + StrExitOnFailure(hr, "Failed to parse int64."); + + if (SHORT_MAX < ll || SHORT_MIN > ll) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + *psOut = (SHORT)ll; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToUInt16 - converts a string to an unsigned 16-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToUInt16( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out USHORT* pusOut + ) +{ + HRESULT hr = S_OK; + ULONGLONG ull = 0; + + hr = StrStringToUInt64(wzIn, cchIn, &ull); + StrExitOnFailure(hr, "Failed to parse uint64."); + + if (USHORT_MAX < ull) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + *pusOut = (USHORT)ull; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToInt32 - converts a string to a signed 32-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToInt32( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out INT* piOut + ) +{ + HRESULT hr = S_OK; + LONGLONG ll = 0; + + hr = StrStringToInt64(wzIn, cchIn, &ll); + StrExitOnFailure(hr, "Failed to parse int64."); + + if (INT_MAX < ll || INT_MIN > ll) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + *piOut = (INT)ll; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToUInt32 - converts a string to an unsigned 32-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToUInt32( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out UINT* puiOut + ) +{ + HRESULT hr = S_OK; + ULONGLONG ull = 0; + + hr = StrStringToUInt64(wzIn, cchIn, &ull); + StrExitOnFailure(hr, "Failed to parse uint64."); + + if (UINT_MAX < ull) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + *puiOut = (UINT)ull; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToInt64 - converts a string to a signed 64-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToInt64( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out LONGLONG* pllOut + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + INT iSign = 1; + INT nDigit = 0; + LARGE_INTEGER liValue = { }; + size_t cchString = 0; + + // get string length if not provided + if (0 >= cchIn) + { + hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString); + StrExitOnRootFailure(hr, "Failed to get length of string."); + + cchIn = (DWORD)cchString; + if (0 >= cchIn) + { + ExitFunction1(hr = E_INVALIDARG); + } + } + + // check sign + if (L'-' == wzIn[0]) + { + if (1 >= cchIn) + { + ExitFunction1(hr = E_INVALIDARG); + } + i = 1; + iSign = -1; + } + + // read digits + while (i < cchIn) + { + nDigit = wzIn[i] - L'0'; + if (0 > nDigit || 9 < nDigit) + { + ExitFunction1(hr = E_INVALIDARG); + } + liValue.QuadPart = liValue.QuadPart * 10 + nDigit * iSign; + + if ((liValue.HighPart ^ iSign) & INT_MIN) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + ++i; + } + + *pllOut = liValue.QuadPart; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToUInt64 - converts a string to an unsigned 64-bit integer. + +****************************************************************************/ +extern "C" HRESULT DAPI StrStringToUInt64( + __in_z LPCWSTR wzIn, + __in DWORD cchIn, + __out ULONGLONG* pullOut + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + DWORD nDigit = 0; + ULONGLONG ullValue = 0; + ULONGLONG ull = 0; + size_t cchString = 0; + + // get string length if not provided + if (0 >= cchIn) + { + hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString); + StrExitOnRootFailure(hr, "Failed to get length of string."); + + cchIn = (DWORD)cchString; + if (0 >= cchIn) + { + ExitFunction1(hr = E_INVALIDARG); + } + } + + // read digits + while (i < cchIn) + { + nDigit = wzIn[i] - L'0'; + if (9 < nDigit) + { + ExitFunction1(hr = E_INVALIDARG); + } + ull = (ULONGLONG)(ullValue * 10 + nDigit); + + if (ull < ullValue) + { + ExitFunction1(hr = DISP_E_OVERFLOW); + } + ullValue = ull; + ++i; + } + + *pullOut = ullValue; + +LExit: + return hr; +} + +/**************************************************************************** +StrStringToUpper - alters the given string in-place to be entirely uppercase + +****************************************************************************/ +void DAPI StrStringToUpper( + __inout_z LPWSTR wzIn + ) +{ + ::CharUpperBuffW(wzIn, lstrlenW(wzIn)); +} + +/**************************************************************************** +StrStringToLower - alters the given string in-place to be entirely lowercase + +****************************************************************************/ +void DAPI StrStringToLower( + __inout_z LPWSTR wzIn + ) +{ + ::CharLowerBuffW(wzIn, lstrlenW(wzIn)); +} + +/**************************************************************************** +StrAllocStringToUpperInvariant - creates an upper-case copy of a string. + +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocStringToUpperInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_UPPERCASE); +} + +/**************************************************************************** +StrAllocStringToLowerInvariant - creates an lower-case copy of a string. + +****************************************************************************/ +extern "C" HRESULT DAPI StrAllocStringToLowerInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_LOWERCASE); +} + +/**************************************************************************** +StrArrayAllocString - Allocates a string array. + +****************************************************************************/ +extern "C" HRESULT DAPI StrArrayAllocString( + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, + __inout LPUINT pcStrArray, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource + ) +{ + HRESULT hr = S_OK; + UINT cNewStrArray; + + hr = ::UIntAdd(*pcStrArray, 1, &cNewStrArray); + StrExitOnFailure(hr, "Failed to increment the string array element count."); + + hr = MemEnsureArraySize(reinterpret_cast(prgsczStrArray), cNewStrArray, sizeof(LPWSTR), ARRAY_GROWTH_SIZE); + StrExitOnFailure(hr, "Failed to allocate memory for the string array."); + + hr = StrAllocString(&(*prgsczStrArray)[*pcStrArray], wzSource, cchSource); + StrExitOnFailure(hr, "Failed to allocate and assign the string."); + + *pcStrArray = cNewStrArray; + +LExit: + return hr; +} + +/**************************************************************************** +StrArrayFree - Frees a string array. + +Use ReleaseNullStrArray to nullify the arguments. + +****************************************************************************/ +extern "C" HRESULT DAPI StrArrayFree( + __in_ecount(cStrArray) LPWSTR *rgsczStrArray, + __in UINT cStrArray + ) +{ + HRESULT hr = S_OK; + + for (UINT i = 0; i < cStrArray; ++i) + { + if (NULL != rgsczStrArray[i]) + { + hr = StrFree(rgsczStrArray[i]); + StrExitOnFailure(hr, "Failed to free the string at index %u.", i); + } + } + + hr = MemFree(rgsczStrArray); + StrExitOnFailure(hr, "Failed to free memory for the string array."); + +LExit: + return hr; +} + +/**************************************************************************** +StrSplitAllocArray - Splits a string into an array. + +****************************************************************************/ +extern "C" HRESULT DAPI StrSplitAllocArray( + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray, + __inout LPUINT pcStrArray, + __in_z LPCWSTR wzSource, + __in_z LPCWSTR wzDelim + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCopy = NULL; + LPWSTR wzContext = NULL; + + // Copy wzSource so it is not modified. + hr = StrAllocString(&sczCopy, wzSource, 0); + StrExitOnFailure(hr, "Failed to copy the source string."); + + for (LPCWSTR wzToken = ::wcstok_s(sczCopy, wzDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, wzDelim, &wzContext)) + { + hr = StrArrayAllocString(prgsczStrArray, pcStrArray, wzToken, 0); + StrExitOnFailure(hr, "Failed to add the string to the string array."); + } + +LExit: + ReleaseStr(sczCopy); + + return hr; +} + +/**************************************************************************** +StrAllocStringMapInvariant - helper function for the ToUpper and ToLower. + +Note: Assumes source and destination buffers will be the same. +****************************************************************************/ +static HRESULT StrAllocStringMapInvariant( + __deref_out_z LPWSTR* pscz, + __in_z LPCWSTR wzSource, + __in SIZE_T cchSource, + __in DWORD dwMapFlags + ) +{ + HRESULT hr = S_OK; + + hr = StrAllocString(pscz, wzSource, cchSource); + StrExitOnFailure(hr, "Failed to allocate a copy of the source string."); + + if (0 == cchSource) + { + // Need the actual string size for LCMapString. This includes the null-terminator + // but LCMapString doesn't care either way. + hr = ::StringCchLengthW(*pscz, INT_MAX, reinterpret_cast(&cchSource)); + StrExitOnRootFailure(hr, "Failed to get the length of the string."); + } + else if (INT_MAX < cchSource) + { + StrExitOnRootFailure(hr = E_INVALIDARG, "Source string is too long: %Iu", cchSource); + } + + // Convert the copy of the string to upper or lower case in-place. + if (0 == ::LCMapStringW(LOCALE_INVARIANT, dwMapFlags, *pscz, static_cast(cchSource), *pscz, static_cast(cchSource))) + { + StrExitWithLastError(hr, "Failed to convert the string case."); + } + +LExit: + return hr; +} + +/**************************************************************************** +StrSecureZeroString - zeroes out string to the make sure the contents +don't remain in memory. + +****************************************************************************/ +extern "C" DAPI_(HRESULT) StrSecureZeroString( + __in LPWSTR pwz + ) +{ + HRESULT hr = S_OK; + SIZE_T cch; + + if (pwz) + { + cch = MemSize(pwz); + if (-1 == cch) + { + hr = E_INVALIDARG; + StrExitOnFailure(hr, "Failed to get size of string"); + } + else + { + SecureZeroMemory(pwz, cch); + } + } + +LExit: + return hr; +} + +/**************************************************************************** +StrSecureZeroFreeString - zeroes out string to the make sure the contents +don't remain in memory, then frees the string. + +****************************************************************************/ +extern "C" DAPI_(HRESULT) StrSecureZeroFreeString( + __in LPWSTR pwz + ) +{ + HRESULT hr = S_OK; + + hr = StrSecureZeroString(pwz); + ReleaseStr(pwz); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/svcutil.cpp b/src/libs/dutil/WixToolset.DUtil/svcutil.cpp new file mode 100644 index 00000000..1a39bfee --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/svcutil.cpp @@ -0,0 +1,59 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define SvcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__) +#define SvcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SVCUTIL, p, x, e, s, __VA_ARGS__) +#define SvcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, p, x, s, __VA_ARGS__) +#define SvcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SVCUTIL, p, x, e, s, __VA_ARGS__) +#define SvcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, p, x, s, __VA_ARGS__) +#define SvcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SVCUTIL, e, x, s, __VA_ARGS__) +#define SvcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SVCUTIL, g, x, s, __VA_ARGS__) + +/******************************************************************** +SvcQueryConfig - queries the configuration of a service + +********************************************************************/ +extern "C" HRESULT DAPI SvcQueryConfig( + __in SC_HANDLE sch, + __out QUERY_SERVICE_CONFIGW** ppConfig + ) +{ + HRESULT hr = S_OK; + QUERY_SERVICE_CONFIGW* pConfig = NULL; + DWORD cbConfig = 0; + + if (!::QueryServiceConfigW(sch, NULL, 0, &cbConfig)) + { + DWORD er = ::GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == er) + { + pConfig = static_cast(MemAlloc(cbConfig, TRUE)); + SvcExitOnNull(pConfig, hr, E_OUTOFMEMORY, "Failed to allocate memory to get configuration."); + + if (!::QueryServiceConfigW(sch, pConfig, cbConfig, &cbConfig)) + { + SvcExitWithLastError(hr, "Failed to read service configuration."); + } + } + else + { + SvcExitOnWin32Error(er, hr, "Failed to query service configuration."); + } + } + + *ppConfig = pConfig; + pConfig = NULL; + +LExit: + ReleaseMem(pConfig); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/thmutil.cpp b/src/libs/dutil/WixToolset.DUtil/thmutil.cpp new file mode 100644 index 00000000..d200a0ea --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/thmutil.cpp @@ -0,0 +1,5709 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ThmExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__) +#define ThmExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__) +#define ThmExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__) +#define ThmExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__) +#define ThmExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__) +#define ThmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_THMUTIL, e, x, s, __VA_ARGS__) +#define ThmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_THMUTIL, g, x, s, __VA_ARGS__) + +// from CommCtrl.h +#ifndef BS_COMMANDLINK +#define BS_COMMANDLINK 0x0000000EL +#endif + +#ifndef BCM_SETNOTE +#define BCM_SETNOTE (BCM_FIRST + 0x0009) +#endif + +#ifndef BCM_SETSHIELD +#define BCM_SETSHIELD (BCM_FIRST + 0x000C) +#endif + +#ifndef LWS_NOPREFIX +#define LWS_NOPREFIX 0x0004 +#endif + +const DWORD THEME_INVALID_ID = 0xFFFFFFFF; +const COLORREF THEME_INVISIBLE_COLORREF = 0xFFFFFFFF; +const DWORD GROW_FONT_INSTANCES = 3; +const DWORD GROW_WINDOW_TEXT = 250; +const LPCWSTR THEME_WC_HYPERLINK = L"ThemeHyperLink"; +const LPCWSTR THEME_WC_PANEL = L"ThemePanel"; +const LPCWSTR THEME_WC_STATICOWNERDRAW = L"ThemeStaticOwnerDraw"; + +static Gdiplus::GdiplusStartupInput vgsi; +static Gdiplus::GdiplusStartupOutput vgso = { }; +static ULONG_PTR vgdiToken = 0; +static ULONG_PTR vgdiHookToken = 0; +static HMODULE vhHyperlinkRegisteredModule = NULL; +static HMODULE vhPanelRegisteredModule = NULL; +static HMODULE vhStaticOwnerDrawRegisteredModule = NULL; +static WNDPROC vpfnStaticOwnerDrawBaseWndProc = NULL; +static HMODULE vhModuleMsftEdit = NULL; +static HMODULE vhModuleRichEd = NULL; +static HCURSOR vhCursorHand = NULL; + +enum INTERNAL_CONTROL_STYLE +{ + INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED = 0x0001, + INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE = 0x0002, + INTERNAL_CONTROL_STYLE_DISABLED = 0x0004, + INTERNAL_CONTROL_STYLE_HIDDEN = 0x0008, + INTERNAL_CONTROL_STYLE_OWNER_DRAW = 0x0010, +}; + +struct MEMBUFFER_FOR_RICHEDIT +{ + BYTE* rgbData; + DWORD cbData; + + DWORD iData; +}; + + +// prototypes +static HRESULT RegisterWindowClasses( + __in_opt HMODULE hModule + ); +static HRESULT ParseTheme( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMDocument* pixd, + __out THEME** ppTheme + ); +static HRESULT ParseImage( + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __out HBITMAP* phImage + ); +static HRESULT ParseIcon( + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __out HICON* phIcon + ); +static HRESULT ParseWindow( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMElement* pElement, + __in THEME* pTheme + ); +static HRESULT GetFontColor( + __in IXMLDOMNode* pixn, + __in_z LPCWSTR wzAttributeName, + __out COLORREF* pColorRef, + __out DWORD* pdwSystemColor + ); +static HRESULT ParseFonts( + __in IXMLDOMElement* pElement, + __in THEME* pTheme + ); +static HRESULT ParsePages( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme + ); +static HRESULT ParseImageLists( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme + ); +static HRESULT ParseControls( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in_opt THEME_PAGE* pPage + ); +static HRESULT ParseControl( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pixn, + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in BOOL fSkipDimensions, + __in_opt THEME_PAGE* pPage + ); +static HRESULT ParseActions( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ); +static HRESULT ParseColumns( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ); +static HRESULT ParseRadioButtons( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pixn, + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in THEME_PAGE* pPage + ); +static HRESULT ParseTabs( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ); +static HRESULT ParseText( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __inout BOOL* pfAnyChildren +); +static HRESULT ParseTooltips( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __inout BOOL* pfAnyChildren + ); +static HRESULT ParseNotes( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __out BOOL* pfAnyChildren + ); +static HRESULT StopBillboard( + __in THEME* pTheme, + __in DWORD dwControl + ); +static HRESULT StartBillboard( + __in THEME* pTheme, + __in DWORD dwControl + ); +static HRESULT EnsureFontInstance( + __in THEME* pTheme, + __in THEME_FONT* pFont, + __out THEME_FONT_INSTANCE** ppFontInstance + ); +static HRESULT FindImageList( + __in THEME* pTheme, + __in_z LPCWSTR wzImageListName, + __out HIMAGELIST *phImageList + ); +static HRESULT LoadControls( + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, + __in DWORD cAssignControlIds + ); +static HRESULT ShowControl( + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in int nCmdShow, + __in BOOL fSaveEditboxes, + __in THEME_SHOW_PAGE_REASON reason, + __in DWORD dwPageId, + __out_opt HWND* phwndFocus + ); +static HRESULT ShowControls( + __in THEME* pTheme, + __in_opt const THEME_CONTROL* pParentControl, + __in int nCmdShow, + __in BOOL fSaveEditboxes, + __in THEME_SHOW_PAGE_REASON reason, + __in DWORD dwPageId + ); +static HRESULT DrawButton( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ); +static void DrawControlText( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl, + __in BOOL fCentered, + __in BOOL fDrawFocusRect + ); +static HRESULT DrawHyperlink( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ); +static HRESULT DrawImage( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ); +static HRESULT DrawProgressBar( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ); +static BOOL DrawHoverControl( + __in THEME* pTheme, + __in BOOL fHover + ); +static DWORD CALLBACK RichEditStreamFromFileHandleCallback( + __in DWORD_PTR dwCookie, + __in_bcount(cb) LPBYTE pbBuff, + __in LONG cb, + __in LONG *pcb + ); +static DWORD CALLBACK RichEditStreamFromMemoryCallback( + __in DWORD_PTR dwCookie, + __in_bcount(cb) LPBYTE pbBuff, + __in LONG cb, + __in LONG *pcb + ); +static void FreeFontInstance( + __in THEME_FONT_INSTANCE* pFontInstance + ); +static void FreeFont( + __in THEME_FONT* pFont + ); +static void FreePage( + __in THEME_PAGE* pPage + ); +static void FreeControl( + __in THEME_CONTROL* pControl + ); +static void FreeConditionalText( + __in THEME_CONDITIONAL_TEXT* pConditionalText + ); +static void FreeImageList( + __in THEME_IMAGELIST* pImageList + ); +static void FreeAction( + __in THEME_ACTION* pAction + ); +static void FreeColumn( + __in THEME_COLUMN* pColumn + ); +static void FreeTab( + __in THEME_TAB* pTab + ); +static void CALLBACK OnBillboardTimer( + __in THEME* pTheme, + __in HWND hwnd, + __in UINT_PTR idEvent + ); +static void OnBrowseDirectory( + __in THEME* pTheme, + __in HWND hWnd, + __in const THEME_ACTION* pAction + ); +static BOOL OnButtonClicked( + __in THEME* pTheme, + __in HWND hWnd, + __in const THEME_CONTROL* pControl + ); +static BOOL OnDpiChanged( + __in THEME* pTheme, + __in WPARAM wParam, + __in LPARAM lParam + ); +static void OnNcCreate( + __in THEME* pTheme, + __in HWND hWnd, + __in LPARAM lParam + ); +static HRESULT OnRichEditEnLink( + __in LPARAM lParam, + __in HWND hWndRichEdit, + __in HWND hWnd + ); +static BOOL ControlIsType( + __in const THEME* pTheme, + __in DWORD dwControl, + __in THEME_CONTROL_TYPE type + ); +static const THEME_CONTROL* FindControlFromHWnd( + __in const THEME* pTheme, + __in HWND hWnd, + __in_opt const THEME_CONTROL* pParentControl = NULL + ); +static void GetControlDimensions( + __in const THEME_CONTROL* pControl, + __in const RECT* prcParent, + __out int* piWidth, + __out int* piHeight, + __out int* piX, + __out int* piY + ); +// Using iWidth as total width of listview, base width of columns, and "Expands" flag on columns +// calculates final width of each column (storing result in each column's nWidth value) +static HRESULT SizeListViewColumns( + __inout THEME_CONTROL* pControl + ); +static LRESULT CALLBACK ControlGroupDefWindowProc( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); +static LRESULT CALLBACK PanelWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); +static LRESULT CALLBACK StaticOwnerDrawWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); +static HRESULT LocalizeControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in const WIX_LOCALIZATION *pWixLoc + ); +static HRESULT LocalizeControl( + __in THEME_CONTROL* pControl, + __in const WIX_LOCALIZATION *pWixLoc + ); +static HRESULT LoadControlsString( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in HMODULE hResModule + ); +static HRESULT LoadControlString( + __in THEME_CONTROL* pControl, + __in HMODULE hResModule + ); +static void ResizeControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in const RECT* prcParent + ); +static void ResizeControl( + __in THEME_CONTROL* pControl, + __in const RECT* prcParent + ); +static void ScaleThemeFromWindow( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y + ); +static void ScaleTheme( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y, + __in DWORD dwStyle, + __in BOOL fMenu, + __in DWORD dwExStyle + ); +static void ScaleControls( + __in THEME* pTheme, + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in UINT nDpi + ); +static void ScaleControl( + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in UINT nDpi + ); +static void GetControls( + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __out DWORD** ppcControls, + __out THEME_CONTROL*** pprgControls + ); +static void GetControls( + __in const THEME* pTheme, + __in_opt const THEME_CONTROL* pParentControl, + __out DWORD& cControls, + __out THEME_CONTROL*& rgControls + ); +static void UnloadControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls + ); + + +// Public functions. + +DAPI_(HRESULT) ThemeInitialize( + __in_opt HMODULE hModule + ) +{ + HRESULT hr = S_OK; + INITCOMMONCONTROLSEX icex = { }; + + DpiuInitialize(); + + hr = XmlInitialize(); + ThmExitOnFailure(hr, "Failed to initialize XML."); + + hr = RegisterWindowClasses(hModule); + ThmExitOnFailure(hr, "Failed to register theme window classes."); + + // Initialize GDI+ and common controls. + vgsi.SuppressBackgroundThread = TRUE; + + hr = GdipInitialize(&vgsi, &vgdiToken, &vgso); + ThmExitOnFailure(hr, "Failed to initialize GDI+."); + + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS | ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_TAB_CLASSES | ICC_LINK_CLASS; + ::InitCommonControlsEx(&icex); + + (*vgso.NotificationHook)(&vgdiHookToken); + +LExit: + return hr; +} + + +DAPI_(void) ThemeUninitialize() +{ + if (vhModuleMsftEdit) + { + ::FreeLibrary(vhModuleMsftEdit); + vhModuleMsftEdit = NULL; + } + + if (vhModuleRichEd) + { + ::FreeLibrary(vhModuleRichEd); + vhModuleRichEd = NULL; + } + + if (vhHyperlinkRegisteredModule) + { + ::UnregisterClassW(THEME_WC_HYPERLINK, vhHyperlinkRegisteredModule); + vhHyperlinkRegisteredModule = NULL; + } + + if (vhPanelRegisteredModule) + { + ::UnregisterClassW(THEME_WC_PANEL, vhPanelRegisteredModule); + vhPanelRegisteredModule = NULL; + } + + if (vhStaticOwnerDrawRegisteredModule) + { + ::UnregisterClassW(THEME_WC_STATICOWNERDRAW, vhStaticOwnerDrawRegisteredModule); + vhStaticOwnerDrawRegisteredModule = NULL; + vpfnStaticOwnerDrawBaseWndProc = NULL; + } + + if (vgdiToken) + { + GdipUninitialize(vgdiToken); + vgdiToken = 0; + } + + XmlUninitialize(); + DpiuUninitialize(); +} + + +DAPI_(HRESULT) ThemeLoadFromFile( + __in_z LPCWSTR wzThemeFile, + __out THEME** ppTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixd = NULL; + LPWSTR sczRelativePath = NULL; + + hr = XmlLoadDocumentFromFile(wzThemeFile, &pixd); + ThmExitOnFailure(hr, "Failed to load theme resource as XML document."); + + hr = PathGetDirectory(wzThemeFile, &sczRelativePath); + ThmExitOnFailure(hr, "Failed to get relative path from theme file."); + + hr = ParseTheme(NULL, sczRelativePath, pixd, ppTheme); + ThmExitOnFailure(hr, "Failed to parse theme."); + +LExit: + ReleaseStr(sczRelativePath); + ReleaseObject(pixd); + + return hr; +} + + +DAPI_(HRESULT) ThemeLoadFromResource( + __in_opt HMODULE hModule, + __in_z LPCSTR szResource, + __out THEME** ppTheme + ) +{ + HRESULT hr = S_OK; + LPVOID pvResource = NULL; + DWORD cbResource = 0; + LPWSTR sczXml = NULL; + IXMLDOMDocument* pixd = NULL; + + hr = ResReadData(hModule, szResource, &pvResource, &cbResource); + ThmExitOnFailure(hr, "Failed to read theme from resource."); + + hr = StrAllocStringAnsi(&sczXml, reinterpret_cast(pvResource), cbResource, CP_UTF8); + ThmExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string."); + + hr = XmlLoadDocument(sczXml, &pixd); + ThmExitOnFailure(hr, "Failed to load theme resource as XML document."); + + hr = ParseTheme(hModule, NULL, pixd, ppTheme); + ThmExitOnFailure(hr, "Failed to parse theme."); + +LExit: + ReleaseObject(pixd); + ReleaseStr(sczXml); + + return hr; +} + + +DAPI_(void) ThemeFree( + __in THEME* pTheme + ) +{ + if (pTheme) + { + for (DWORD i = 0; i < pTheme->cFonts; ++i) + { + FreeFont(pTheme->rgFonts + i); + } + + for (DWORD i = 0; i < pTheme->cPages; ++i) + { + FreePage(pTheme->rgPages + i); + } + + for (DWORD i = 0; i < pTheme->cImageLists; ++i) + { + FreeImageList(pTheme->rgImageLists + i); + } + + for (DWORD i = 0; i < pTheme->cControls; ++i) + { + FreeControl(pTheme->rgControls + i); + } + + ReleaseMem(pTheme->rgControls); + ReleaseMem(pTheme->rgPages); + ReleaseMem(pTheme->rgFonts); + + if (pTheme->hImage) + { + ::DeleteBitmap(pTheme->hImage); + } + + ReleaseStr(pTheme->sczCaption); + ReleaseMem(pTheme); + } +} + +DAPI_(HRESULT) ThemeRegisterVariableCallbacks( + __in THEME* pTheme, + __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition, + __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString, + __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable, + __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable, + __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable, + __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable, + __in_opt LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); + + pTheme->pfnEvaluateCondition = pfnEvaluateCondition; + pTheme->pfnFormatString = pfnFormatString; + pTheme->pfnGetNumericVariable = pfnGetNumericVariable; + pTheme->pfnSetNumericVariable = pfnSetNumericVariable; + pTheme->pfnGetStringVariable = pfnGetStringVariable; + pTheme->pfnSetStringVariable = pfnSetStringVariable; + pTheme->pvVariableContext = pvContext; + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeCreateParentWindow( + __in THEME* pTheme, + __in DWORD dwExStyle, + __in LPCWSTR szClassName, + __in LPCWSTR szWindowName, + __in DWORD dwStyle, + __in int x, + __in int y, + __in_opt HWND hwndParent, + __in_opt HINSTANCE hInstance, + __in_opt LPVOID lpParam, + __in THEME_WINDOW_INITIAL_POSITION initialPosition, + __out_opt HWND* phWnd + ) +{ + HRESULT hr = S_OK; + DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; + POINT pt = { }; + RECT* pMonitorRect = NULL; + HMENU hMenu = NULL; + HWND hWnd = NULL; + + if (pTheme->hwndParent) + { + ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeCreateParentWindow called after the theme was loaded."); + } + + if (THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES == initialPosition) + { + pt.x = x; + pt.y = y; + hr = DpiuGetMonitorContextFromPoint(&pt, &pMonitorContext); + if (SUCCEEDED(hr)) + { + pMonitorRect = &pMonitorContext->mi.rcWork; + if (pMonitorContext->nDpi != pTheme->nDpi) + { + ScaleTheme(pTheme, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top, dwStyle, NULL != hMenu, dwExStyle); + } + + x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pTheme->nWindowWidth) / 2; + y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pTheme->nWindowHeight) / 2; + } + else + { + hr = S_OK; + x = CW_USEDEFAULT; + y = CW_USEDEFAULT; + } + } + + hWnd = ::CreateWindowExW(dwExStyle, szClassName, szWindowName, dwStyle, x, y, pTheme->nWindowWidth, pTheme->nWindowHeight, hwndParent, hMenu, hInstance, lpParam); + ThmExitOnNullWithLastError(hWnd, hr, "Failed to create theme parent window."); + ThmExitOnNull(pTheme->hwndParent, hr, E_INVALIDSTATE, "Theme parent window is not set, make sure ThemeDefWindowProc is called for WM_NCCREATE."); + AssertSz(hWnd == pTheme->hwndParent, "Theme parent window does not equal newly created window."); + + if (phWnd) + { + *phWnd = hWnd; + } + +LExit: + ReleaseMem(pMonitorContext); + + return hr; +} + + +DAPI_(HRESULT) ThemeLoadControls( + __in THEME* pTheme, + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, + __in DWORD cAssignControlIds + ) +{ + HRESULT hr = S_OK; + + if (!pTheme->hwndParent) + { + ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeLoadControls called before theme parent window created."); + } + + hr = LoadControls(pTheme, NULL, rgAssignControlIds, cAssignControlIds); + +LExit: + return hr; +} + + +DAPI_(void) ThemeUnloadControls( + __in THEME* pTheme + ) +{ + UnloadControls(pTheme->cControls, pTheme->rgControls); + + pTheme->hwndHover = NULL; + pTheme->hwndParent = NULL; +} + +DAPI_(HRESULT) ThemeLocalize( + __in THEME *pTheme, + __in const WIX_LOCALIZATION *pWixLoc + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCaption = NULL; + + hr = LocLocalizeString(pWixLoc, &pTheme->sczCaption); + ThmExitOnFailure(hr, "Failed to localize theme caption."); + + if (pTheme->pfnFormatString) + { + hr = pTheme->pfnFormatString(pTheme->sczCaption, &sczCaption, pTheme->pvVariableContext); + if (SUCCEEDED(hr)) + { + hr = ThemeUpdateCaption(pTheme, sczCaption); + } + } + + hr = LocalizeControls(pTheme->cControls, pTheme->rgControls, pWixLoc); + +LExit: + ReleaseStr(sczCaption); + + return hr; +} + +/******************************************************************** + ThemeLoadStrings - Loads string resources. + Must be called after loading a theme and before calling + ThemeLoadControls. +*******************************************************************/ +DAPI_(HRESULT) ThemeLoadStrings( + __in THEME* pTheme, + __in HMODULE hResModule + ) +{ + HRESULT hr = S_OK; + ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first."); + + if (UINT_MAX != pTheme->uStringId) + { + hr = ResReadString(hResModule, pTheme->uStringId, &pTheme->sczCaption); + ThmExitOnFailure(hr, "Failed to load theme caption."); + } + + hr = LoadControlsString(pTheme->cControls, pTheme->rgControls, hResModule); + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeLoadRichEditFromFile( + __in THEME* pTheme, + __in DWORD dwControl, + __in_z LPCWSTR wzFileName, + __in HMODULE hModule + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFile = NULL; + HANDLE hFile = INVALID_HANDLE_VALUE; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + hr = PathRelativeToModule(&sczFile, wzFileName, hModule); + ThmExitOnFailure(hr, "Failed to read resource data."); + + hFile = ::CreateFileW(sczFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ThmExitWithLastError(hr, "Failed to open RTF file."); + } + else + { + LONGLONG llRtfSize; + hr = FileSizeByHandle(hFile, &llRtfSize); + if (SUCCEEDED(hr)) + { + ::SendMessageW(hWnd, EM_EXLIMITTEXT, 0, static_cast(llRtfSize)); + } + + EDITSTREAM es = { }; + es.pfnCallback = RichEditStreamFromFileHandleCallback; + es.dwCookie = reinterpret_cast(hFile); + + ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast(&es)); + hr = es.dwError; + ThmExitOnFailure(hr, "Failed to update RTF stream."); + } + +LExit: + ReleaseStr(sczFile); + ReleaseFile(hFile); + + return hr; +} + + +DAPI_(HRESULT) ThemeLoadRichEditFromResource( + __in THEME* pTheme, + __in DWORD dwControl, + __in_z LPCSTR szResourceName, + __in HMODULE hModule + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + return ThemeLoadRichEditFromResourceToHWnd(hWnd, szResourceName, hModule); +} + +DAPI_(HRESULT) ThemeLoadRichEditFromResourceToHWnd( + __in HWND hWnd, + __in_z LPCSTR szResourceName, + __in HMODULE hModule + ) +{ + HRESULT hr = S_OK; + MEMBUFFER_FOR_RICHEDIT buffer = { }; + EDITSTREAM es = { }; + + hr = ResReadData(hModule, szResourceName, reinterpret_cast(&buffer.rgbData), &buffer.cbData); + ThmExitOnFailure(hr, "Failed to read resource data."); + + es.pfnCallback = RichEditStreamFromMemoryCallback; + es.dwCookie = reinterpret_cast(&buffer); + + ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast(&es)); + hr = es.dwError; + ThmExitOnFailure(hr, "Failed to update RTF stream."); + +LExit: + return hr; +} + + +DAPI_(BOOL) ThemeHandleKeyboardMessage( + __in_opt THEME* pTheme, + __in HWND /*hWnd*/, + __in MSG* pMsg + ) +{ + return pTheme ? ::IsDialogMessageW(pTheme->hwndParent, pMsg) : FALSE; +} + + +extern "C" LRESULT CALLBACK ThemeDefWindowProc( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + RECT rcParent = { }; + RECT *pRect = NULL; + + if (pTheme) + { + switch (uMsg) + { + case WM_NCCREATE: + if (pTheme->hwndParent) + { + AssertSz(FALSE, "WM_NCCREATE called multiple times"); + } + else + { + OnNcCreate(pTheme, hWnd, lParam); + } + break; + + case WM_NCHITTEST: + if (pTheme->dwStyle & WS_POPUP) + { + return HTCAPTION; // allow pop-up windows to be moved by grabbing any non-control. + } + break; + + case WM_DPICHANGED: + if (OnDpiChanged(pTheme, wParam, lParam)) + { + return 0; + } + break; + + case WM_SIZING: + if (pTheme->fAutoResize) + { + pRect = reinterpret_cast(lParam); + if (pRect->right - pRect->left < pTheme->nMinimumWidth) + { + if (wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT) + { + pRect->left = pRect->right - pTheme->nMinimumWidth; + } + else + { + pRect->right = pRect->left + pTheme->nMinimumWidth; + } + } + if (pRect->bottom - pRect->top < pTheme->nMinimumHeight) + { + if (wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT) + { + pRect->bottom = pRect->top + pTheme->nMinimumHeight; + } + else + { + pRect->top = pRect->bottom - pTheme->nMinimumHeight; + } + } + + return TRUE; + } + break; + + case WM_SIZE: + if (pTheme->fAutoResize || pTheme->fForceResize) + { + pTheme->fForceResize = FALSE; + ::GetClientRect(pTheme->hwndParent, &rcParent); + ResizeControls(pTheme->cControls, pTheme->rgControls, &rcParent); + return 0; + } + break; + } + } + + return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); +} + + +DAPI_(void) ThemeGetPageIds( + __in const THEME* pTheme, + __in_ecount(cGetPages) LPCWSTR* rgwzFindNames, + __inout_ecount(cGetPages) DWORD* rgdwPageIds, + __in DWORD cGetPages + ) +{ + for (DWORD i = 0; i < cGetPages; ++i) + { + LPCWSTR wzFindName = rgwzFindNames[i]; + for (DWORD j = 0; j < pTheme->cPages; ++j) + { + LPCWSTR wzPageName = pTheme->rgPages[j].sczName; + if (wzPageName && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPageName, -1, wzFindName, -1)) + { + rgdwPageIds[i] = j + 1; // add one to make the page ids 1-based (so zero is invalid). + break; + } + } + } +} + + +DAPI_(THEME_PAGE*) ThemeGetPage( + __in const THEME* pTheme, + __in DWORD dwPage + ) +{ + DWORD iPage = dwPage - 1; + THEME_PAGE* pPage = NULL; + + if (iPage < pTheme->cPages) + { + pPage = pTheme->rgPages + iPage; + } + + return pPage; +} + + +DAPI_(HRESULT) ThemeShowPage( + __in THEME* pTheme, + __in DWORD dwPage, + __in int nCmdShow + ) +{ + return ThemeShowPageEx(pTheme, dwPage, nCmdShow, THEME_SHOW_PAGE_REASON_DEFAULT); +} + + +DAPI_(HRESULT) ThemeShowPageEx( + __in THEME* pTheme, + __in DWORD dwPage, + __in int nCmdShow, + __in THEME_SHOW_PAGE_REASON reason + ) +{ + HRESULT hr = S_OK; + BOOL fHide = SW_HIDE == nCmdShow; + BOOL fSaveEditboxes = FALSE; + THEME_SAVEDVARIABLE* pSavedVariable = NULL; + THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPage); + + if (pPage) + { + if (fHide) + { + switch (reason) + { + case THEME_SHOW_PAGE_REASON_DEFAULT: + // Set the variables in the loop below. + fSaveEditboxes = TRUE; + break; + case THEME_SHOW_PAGE_REASON_CANCEL: + if (pPage->cSavedVariables && pTheme->pfnSetStringVariable) + { + // Best effort to cancel any changes to the variables. + for (DWORD v = 0; v < pPage->cSavedVariables; ++v) + { + pSavedVariable = pPage->rgSavedVariables + v; + if (pSavedVariable->wzName) + { + pTheme->pfnSetStringVariable(pSavedVariable->wzName, pSavedVariable->sczValue, FALSE, pTheme->pvVariableContext); + } + } + } + break; + } + + if (THEME_SHOW_PAGE_REASON_REFRESH != reason) + { + pPage->cSavedVariables = 0; + if (pPage->rgSavedVariables) + { + SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables)); + } + } + + pTheme->dwCurrentPageId = 0; + } + else + { + if (THEME_SHOW_PAGE_REASON_REFRESH == reason) + { + fSaveEditboxes = TRUE; + } + else + { + hr = MemEnsureArraySize(reinterpret_cast(&pPage->rgSavedVariables), pPage->cControlIndices, sizeof(THEME_SAVEDVARIABLE), pPage->cControlIndices); + ThmExitOnFailure(hr, "Failed to allocate memory for saved variables."); + + SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables)); + pPage->cSavedVariables = pPage->cControlIndices; + + // Save the variables in the loop below. + } + + pTheme->dwCurrentPageId = dwPage; + } + } + + hr = ShowControls(pTheme, NULL, nCmdShow, fSaveEditboxes, reason, dwPage); + ThmExitOnFailure(hr, "Failed to show page controls."); + +LExit: + return hr; +} + + +DAPI_(BOOL) ThemeControlExists( + __in const THEME* pTheme, + __in DWORD dwControl + ) +{ + BOOL fExists = FALSE; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + if (hWnd) + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + fExists = (pControl && hWnd == pControl->hWnd); + } + + return fExists; +} + + +DAPI_(void) ThemeControlEnable( + __in THEME* pTheme, + __in DWORD dwControl, + __in BOOL fEnable + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl) + { + pControl->dwInternalStyle = fEnable ? (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_DISABLED) : (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_DISABLED); + ::EnableWindow(hWnd, fEnable); + + if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED) + { + ::ShowWindow(hWnd, fEnable ? SW_SHOW : SW_HIDE); + } + } +} + + +DAPI_(BOOL) ThemeControlEnabled( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + return pControl && !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED); +} + + +DAPI_(void) ThemeControlElevates( + __in THEME* pTheme, + __in DWORD dwControl, + __in BOOL fElevates + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + ::SendMessageW(hWnd, BCM_SETSHIELD, 0, fElevates); +} + + +DAPI_(void) ThemeShowControl( + __in THEME* pTheme, + __in DWORD dwControl, + __in int nCmdShow + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + ::ShowWindow(hWnd, nCmdShow); + + // Save the control's visible state. + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl) + { + pControl->dwInternalStyle = (SW_HIDE == nCmdShow) ? (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_HIDDEN) : (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_HIDDEN); + } +} + + +DAPI_(void) ThemeShowControlEx( + __in THEME* pTheme, + __in DWORD dwControl, + __in int nCmdShow + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl) + { + ShowControl(pTheme, pControl, nCmdShow, THEME_CONTROL_TYPE_EDITBOX == pControl->type, THEME_SHOW_PAGE_REASON_REFRESH, 0, NULL); + } +} + + +DAPI_(BOOL) ThemeControlVisible( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + return ::IsWindowVisible(hWnd); +} + + +DAPI_(BOOL) ThemePostControlMessage( + __in THEME* pTheme, + __in DWORD dwControl, + __in UINT Msg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (!::PostMessageW(hWnd, Msg, wParam, lParam)) + { + er = ::GetLastError(); + hr = HRESULT_FROM_WIN32(er); + } + + return SUCCEEDED(hr); +} + + +DAPI_(LRESULT) ThemeSendControlMessage( + __in const THEME* pTheme, + __in DWORD dwControl, + __in UINT Msg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + return ::SendMessageW(hWnd, Msg, wParam, lParam); +} + + +DAPI_(HRESULT) ThemeDrawBackground( + __in THEME* pTheme, + __in PAINTSTRUCT* pps + ) +{ + HRESULT hr = S_FALSE; + + if (pTheme->hImage && 0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY && pps->fErase) + { + HDC hdcMem = ::CreateCompatibleDC(pps->hdc); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pTheme->hImage)); + DWORD dwSourceWidth = pTheme->nDefaultDpiWidth; + DWORD dwSourceHeight = pTheme->nDefaultDpiHeight; + + ::StretchBlt(pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight, hdcMem, pTheme->nSourceX, pTheme->nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); + + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); + + hr = S_OK; + } + + return hr; +} + + +DAPI_(HRESULT) ThemeDrawControl( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis + ) +{ + HRESULT hr = S_OK; + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, pdis->hwndItem); + + AssertSz(pControl, "Expected control window from owner draw window."); + AssertSz(pControl->hWnd == pdis->hwndItem, "Expected control window to match owner draw window."); + AssertSz(pControl->nWidth < 1 || pControl->nWidth == pdis->rcItem.right - pdis->rcItem.left, "Expected control window width to match owner draw window width."); + AssertSz(pControl->nHeight < 1 || pControl->nHeight == pdis->rcItem.bottom - pdis->rcItem.top, "Expected control window height to match owner draw window height."); + + switch (pControl->type) + { + case THEME_CONTROL_TYPE_BUTTON: + hr = DrawButton(pTheme, pdis, pControl); + ThmExitOnFailure(hr, "Failed to draw button."); + break; + + case THEME_CONTROL_TYPE_HYPERLINK: + hr = DrawHyperlink(pTheme, pdis, pControl); + ThmExitOnFailure(hr, "Failed to draw hyperlink."); + break; + + case THEME_CONTROL_TYPE_IMAGE: + hr = DrawImage(pTheme, pdis, pControl); + ThmExitOnFailure(hr, "Failed to draw image."); + break; + + case THEME_CONTROL_TYPE_PROGRESSBAR: + hr = DrawProgressBar(pTheme, pdis, pControl); + ThmExitOnFailure(hr, "Failed to draw progress bar."); + break; + + default: + hr = E_UNEXPECTED; + ThmExitOnRootFailure(hr, "Did not specify an owner draw control to draw."); + } + +LExit: + return hr; +} + + +DAPI_(BOOL) ThemeHoverControl( + __in THEME* pTheme, + __in HWND hwndParent, + __in HWND hwndControl + ) +{ + BOOL fHovered = FALSE; + if (hwndControl != pTheme->hwndHover) + { + if (pTheme->hwndHover && pTheme->hwndHover != hwndParent) + { + DrawHoverControl(pTheme, FALSE); + } + + pTheme->hwndHover = hwndControl; + + if (pTheme->hwndHover && pTheme->hwndHover != hwndParent) + { + fHovered = DrawHoverControl(pTheme, TRUE); + } + } + + return fHovered; +} + + +DAPI_(BOOL) ThemeIsControlChecked( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + return BST_CHECKED == ::SendMessageW(hWnd, BM_GETCHECK, 0, 0); +} + + +DAPI_(BOOL) ThemeSetControlColor( + __in THEME* pTheme, + __in HDC hdc, + __in HWND hWnd, + __out HBRUSH* phBackgroundBrush + ) +{ + THEME_FONT* pFont = NULL; + BOOL fHasBackground = FALSE; + + *phBackgroundBrush = NULL; + + if (hWnd == pTheme->hwndParent) + { + pFont = (THEME_INVALID_ID == pTheme->dwFontId) ? NULL : pTheme->rgFonts + pTheme->dwFontId; + } + else + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + pFont = (!pControl || THEME_INVALID_ID == pControl->dwFontId) ? NULL : pTheme->rgFonts + pControl->dwFontId; + } + + if (pFont) + { + if (pFont->hForeground) + { + ::SetTextColor(hdc, pFont->crForeground); + } + + if (pFont->hBackground) + { + ::SetBkColor(hdc, pFont->crBackground); + + *phBackgroundBrush = pFont->hBackground; + fHasBackground = TRUE; + } + else + { + ::SetBkMode(hdc, TRANSPARENT); + *phBackgroundBrush = static_cast(::GetStockObject(NULL_BRUSH)); + fHasBackground = TRUE; + } + } + + return fHasBackground; +} + + +DAPI_(HRESULT) ThemeSetProgressControl( + __in THEME* pTheme, + __in DWORD dwControl, + __in DWORD dwProgressPercentage + ) +{ + HRESULT hr = E_NOTFOUND; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (hWnd) + { + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl && THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type) + { + DWORD dwCurrentProgress = LOWORD(pControl->dwData); + + if (dwCurrentProgress != dwProgressPercentage) + { + DWORD dwColor = HIWORD(pControl->dwData); + pControl->dwData = MAKEDWORD(dwProgressPercentage, dwColor); + + if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW) + { + if (!::InvalidateRect(hWnd, NULL, FALSE)) + { + ThmExitWithLastError(hr, "Failed to invalidate progress bar window."); + } + } + else + { + ::SendMessageW(hWnd, PBM_SETPOS, dwProgressPercentage, 0); + } + + hr = S_OK; + } + else + { + hr = S_FALSE; + } + } + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeSetProgressControlColor( + __in THEME* pTheme, + __in DWORD dwControl, + __in DWORD dwColorIndex + ) +{ + HRESULT hr = S_FALSE; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + if (hWnd) + { + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + + // Only set color on owner draw progress bars. + if (pControl && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW)) + { + DWORD dwCurrentColor = HIWORD(pControl->dwData); + + if (dwCurrentColor != dwColorIndex) + { + DWORD dwCurrentProgress = LOWORD(pControl->dwData); + pControl->dwData = MAKEDWORD(dwCurrentProgress, dwColorIndex); + + if (!::InvalidateRect(hWnd, NULL, FALSE)) + { + ThmExitWithLastError(hr, "Failed to invalidate progress bar window."); + } + + hr = S_OK; + } + } + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeSetTextControl( + __in const THEME* pTheme, + __in DWORD dwControl, + __in_z_opt LPCWSTR wzText + ) +{ + return ThemeSetTextControlEx(pTheme, dwControl, FALSE, wzText); +} + + +DAPI_(HRESULT) ThemeSetTextControlEx( + __in const THEME* pTheme, + __in DWORD dwControl, + __in BOOL fUpdate, + __in_z_opt LPCWSTR wzText + ) +{ + HRESULT hr = S_OK; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (hWnd) + { + if (fUpdate) + { + ::ShowWindow(hWnd, SW_HIDE); + } + + if (!::SetWindowTextW(hWnd, wzText)) + { + ThmExitWithLastError(hr, "Failed to set control text."); + } + + if (fUpdate) + { + ::ShowWindow(hWnd, SW_SHOW); + } + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeGetTextControl( + __in const THEME* pTheme, + __in DWORD dwControl, + __inout_z LPWSTR* psczText + ) +{ + HRESULT hr = S_OK; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + SIZE_T cbSize = 0; + DWORD cchText = 0; + DWORD cchTextRead = 0; + + // Ensure the string has room for at least one character. + hr = StrMaxLength(*psczText, &cbSize); + ThmExitOnFailure(hr, "Failed to get text buffer length."); + + cchText = (DWORD)min(DWORD_MAX, cbSize); + + if (!cchText) + { + cchText = GROW_WINDOW_TEXT; + + hr = StrAlloc(psczText, cchText); + ThmExitOnFailure(hr, "Failed to grow text buffer."); + } + + // Read (and keep growing buffer) until we finally read less than there + // is room in the buffer. + for (;;) + { + cchTextRead = ::GetWindowTextW(hWnd, *psczText, cchText); + if (cchTextRead + 1 < cchText) + { + break; + } + else + { + cchText = cchTextRead + GROW_WINDOW_TEXT; + + hr = StrAlloc(psczText, cchText); + ThmExitOnFailure(hr, "Failed to grow text buffer again."); + } + } + +LExit: + return hr; +} + + +DAPI_(HRESULT) ThemeUpdateCaption( + __in THEME* pTheme, + __in_z LPCWSTR wzCaption + ) +{ + HRESULT hr = S_OK; + + hr = StrAllocString(&pTheme->sczCaption, wzCaption, 0); + ThmExitOnFailure(hr, "Failed to update theme caption."); + +LExit: + return hr; +} + + +DAPI_(void) ThemeSetFocus( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HWND hwndFocus = ::GetDlgItem(pTheme->hwndParent, dwControl); + if (hwndFocus && !ThemeControlEnabled(pTheme, dwControl)) + { + hwndFocus = ::GetNextDlgTabItem(pTheme->hwndParent, hwndFocus, FALSE); + } + + if (hwndFocus) + { + ::SetFocus(hwndFocus); + } +} + + +DAPI_(void) ThemeShowChild( + __in THEME* pTheme, + __in THEME_CONTROL* pParentControl, + __in DWORD dwIndex + ) +{ + // show one child, hide the rest + for (DWORD i = 0; i < pParentControl->cControls; ++i) + { + THEME_CONTROL* pControl = pParentControl->rgControls + i; + ShowControl(pTheme, pControl, dwIndex == i ? SW_SHOW : SW_HIDE, FALSE, THEME_SHOW_PAGE_REASON_DEFAULT, 0, NULL); + } +} + + +// Internal functions. + +static HRESULT RegisterWindowClasses( + __in_opt HMODULE hModule + ) +{ + HRESULT hr = S_OK; + WNDCLASSW wcHyperlink = { }; + WNDCLASSW wcPanel = { }; + WNDCLASSW wcStaticOwnerDraw = { }; + WNDPROC pfnStaticOwnerDrawBaseWndProc = NULL; + + vhCursorHand = ::LoadCursorA(NULL, IDC_HAND); + + // Base the theme hyperlink class on a button but give it the "hand" icon. + if (!::GetClassInfoW(NULL, WC_BUTTONW, &wcHyperlink)) + { + ThmExitWithLastError(hr, "Failed to get button window class."); + } + + wcHyperlink.lpszClassName = THEME_WC_HYPERLINK; +#pragma prefast(push) +#pragma prefast(disable:25068) + wcHyperlink.hCursor = vhCursorHand; +#pragma prefast(pop) + + if (!::RegisterClassW(&wcHyperlink)) + { + ThmExitWithLastError(hr, "Failed to get button window class."); + } + vhHyperlinkRegisteredModule = hModule; + + // Panel is its own do-nothing class. + wcPanel.lpfnWndProc = PanelWndProc; + wcPanel.hInstance = hModule; + wcPanel.hCursor = ::LoadCursorW(NULL, (LPCWSTR) IDC_ARROW); + wcPanel.lpszClassName = THEME_WC_PANEL; + if (!::RegisterClassW(&wcPanel)) + { + ThmExitWithLastError(hr, "Failed to register window."); + } + vhPanelRegisteredModule = hModule; + + if (!::GetClassInfoW(NULL, WC_STATICW, &wcStaticOwnerDraw)) + { + ThmExitWithLastError(hr, "Failed to get static window class."); + } + + pfnStaticOwnerDrawBaseWndProc = wcStaticOwnerDraw.lpfnWndProc; + wcStaticOwnerDraw.lpfnWndProc = StaticOwnerDrawWndProc; + wcStaticOwnerDraw.hInstance = hModule; + wcStaticOwnerDraw.lpszClassName = THEME_WC_STATICOWNERDRAW; + if (!::RegisterClassW(&wcStaticOwnerDraw)) + { + ThmExitWithLastError(hr, "Failed to register OwnerDraw window class."); + } + vhStaticOwnerDrawRegisteredModule = hModule; + vpfnStaticOwnerDrawBaseWndProc = pfnStaticOwnerDrawBaseWndProc; + + +LExit: + return hr; +} + +static HRESULT ParseTheme( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMDocument* pixd, + __out THEME** ppTheme + ) +{ + static WORD wThemeId = 0; + + HRESULT hr = S_OK; + THEME* pTheme = NULL; + IXMLDOMElement *pThemeElement = NULL; + + hr = pixd->get_documentElement(&pThemeElement); + ThmExitOnFailure(hr, "Failed to get theme element."); + + pTheme = static_cast(MemAlloc(sizeof(THEME), TRUE)); + ThmExitOnNull(pTheme, hr, E_OUTOFMEMORY, "Failed to allocate memory for theme."); + + pTheme->wId = ++wThemeId; + pTheme->nDpi = USER_DEFAULT_SCREEN_DPI; + + // Parse the optional background resource image. + hr = ParseImage(hModule, wzRelativePath, pThemeElement, &pTheme->hImage); + ThmExitOnFailure(hr, "Failed while parsing theme image."); + + // Parse the fonts. + hr = ParseFonts(pThemeElement, pTheme); + ThmExitOnFailure(hr, "Failed to parse theme fonts."); + + // Parse the window element. + hr = ParseWindow(hModule, wzRelativePath, pThemeElement, pTheme); + ThmExitOnFailure(hr, "Failed to parse theme window element."); + + *ppTheme = pTheme; + pTheme = NULL; + +LExit: + ReleaseObject(pThemeElement); + + if (pTheme) + { + ThemeFree(pTheme); + } + + return hr; +} + +static HRESULT ParseImage( + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __out HBITMAP* phImage + ) +{ + HRESULT hr = S_OK; + BSTR bstr = NULL; + LPWSTR sczImageFile = NULL; + int iResourceId = 0; + Gdiplus::Bitmap* pBitmap = NULL; + *phImage = NULL; + + hr = XmlGetAttribute(pElement, L"ImageResource", &bstr); + ThmExitOnFailure(hr, "Failed to get image resource attribute."); + + if (S_OK == hr) + { + iResourceId = wcstol(bstr, NULL, 10); + + hr = GdipBitmapFromResource(hModule, MAKEINTRESOURCE(iResourceId), &pBitmap); + // Don't fail. + } + + ReleaseNullBSTR(bstr); + + // Parse the optional background image from a given file. + if (!pBitmap) + { + hr = XmlGetAttribute(pElement, L"ImageFile", &bstr); + ThmExitOnFailure(hr, "Failed to get image file attribute."); + + if (S_OK == hr) + { + if (wzRelativePath) + { + hr = PathConcat(wzRelativePath, bstr, &sczImageFile); + ThmExitOnFailure(hr, "Failed to combine image file path."); + } + else + { + hr = PathRelativeToModule(&sczImageFile, bstr, hModule); + ThmExitOnFailure(hr, "Failed to get image filename."); + } + + hr = GdipBitmapFromFile(sczImageFile, &pBitmap); + // Don't fail. + } + } + + // If there is an image, convert it into a bitmap handle. + if (pBitmap) + { + Gdiplus::Color black; + Gdiplus::Status gs = pBitmap->GetHBITMAP(black, phImage); + ThmExitOnGdipFailure(gs, hr, "Failed to convert GDI+ bitmap into HBITMAP."); + } + + hr = S_OK; + +LExit: + if (pBitmap) + { + delete pBitmap; + } + + ReleaseStr(sczImageFile); + ReleaseBSTR(bstr); + + return hr; +} + + +static HRESULT ParseIcon( + __in_opt HMODULE hModule, + __in_z_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __out HICON* phIcon + ) +{ + HRESULT hr = S_OK; + BSTR bstr = NULL; + LPWSTR sczImageFile = NULL; + int iResourceId = 0; + *phIcon = NULL; + + hr = XmlGetAttribute(pElement, L"IconResource", &bstr); + ThmExitOnFailure(hr, "Failed to get icon resource attribute."); + + if (S_OK == hr) + { + iResourceId = wcstol(bstr, NULL, 10); + + *phIcon = reinterpret_cast(::LoadImageW(hModule, MAKEINTRESOURCEW(iResourceId), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE)); + ThmExitOnNullWithLastError(*phIcon, hr, "Failed to load icon."); + } + else + { + ReleaseNullBSTR(bstr); + + hr = XmlGetAttribute(pElement, L"IconFile", &bstr); + ThmExitOnFailure(hr, "Failed to get icon file attribute."); + + if (S_OK == hr) + { + if (wzRelativePath) + { + hr = PathConcat(wzRelativePath, bstr, &sczImageFile); + ThmExitOnFailure(hr, "Failed to combine image file path."); + } + else + { + hr = PathRelativeToModule(&sczImageFile, bstr, hModule); + ThmExitOnFailure(hr, "Failed to get image filename."); + } + + *phIcon = reinterpret_cast(::LoadImageW(NULL, sczImageFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE)); + ThmExitOnNullWithLastError(*phIcon, hr, "Failed to load icon: %ls.", sczImageFile); + } + } + +LExit: + ReleaseStr(sczImageFile); + ReleaseBSTR(bstr); + + return hr; +} + + +static HRESULT ParseWindow( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMElement* pElement, + __in THEME* pTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + DWORD dwValue = 0; + BSTR bstr = NULL; + LPWSTR sczIconFile = NULL; + + hr = XmlSelectSingleNode(pElement, L"Window", &pixn); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find window element."); + + hr = XmlGetYesNoAttribute(pixn, L"AutoResize", &pTheme->fAutoResize); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to get window AutoResize attribute."); + + hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Failed to find window Width attribute."); + } + ThmExitOnFailure(hr, "Failed to get window Width attribute."); + + pTheme->nWidth = pTheme->nDefaultDpiWidth = pTheme->nWindowWidth = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Failed to find window Height attribute."); + } + ThmExitOnFailure(hr, "Failed to get window Height attribute."); + + pTheme->nHeight = pTheme->nDefaultDpiHeight = pTheme->nWindowHeight = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", &dwValue); + if (S_FALSE == hr) + { + dwValue = 0; + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to get window MinimumWidth attribute."); + + pTheme->nMinimumWidth = pTheme->nDefaultDpiMinimumWidth = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", &dwValue); + if (S_FALSE == hr) + { + dwValue = 0; + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to get window MinimumHeight attribute."); + + pTheme->nMinimumHeight = pTheme->nDefaultDpiMinimumHeight = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"FontId", &pTheme->dwFontId); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Failed to find window FontId attribute."); + } + ThmExitOnFailure(hr, "Failed to get window FontId attribute."); + + // Get the optional window icon from a resource. + hr = XmlGetAttribute(pixn, L"IconResource", &bstr); + ThmExitOnFailure(hr, "Failed to get window IconResource attribute."); + + if (S_OK == hr) + { + pTheme->hIcon = ::LoadIconW(hModule, bstr); + ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconResource."); + + ReleaseNullBSTR(bstr); + } + + // Get the optional window icon from a file. + hr = XmlGetAttribute(pixn, L"IconFile", &bstr); + ThmExitOnFailure(hr, "Failed to get window IconFile attribute."); + + if (S_OK == hr) + { + if (wzRelativePath) + { + hr = PathConcat(wzRelativePath, bstr, &sczIconFile); + ThmExitOnFailure(hr, "Failed to combine icon file path."); + } + else + { + hr = PathRelativeToModule(&sczIconFile, bstr, hModule); + ThmExitOnFailure(hr, "Failed to get icon filename."); + } + + pTheme->hIcon = ::LoadImageW(NULL, sczIconFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE); + ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconFile: %ls.", bstr); + + ReleaseNullBSTR(bstr); + } + + hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast(&pTheme->nSourceX)); + if (S_FALSE == hr) + { + pTheme->nSourceX = -1; + } + ThmExitOnFailure(hr, "Failed to get window SourceX attribute."); + + hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast(&pTheme->nSourceY)); + if (S_FALSE == hr) + { + pTheme->nSourceY = -1; + } + ThmExitOnFailure(hr, "Failed to get window SourceY attribute."); + + // Parse the optional window style. + hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pTheme->dwStyle); + ThmExitOnFailure(hr, "Failed to get theme window style (Window@HexStyle) attribute."); + + if (S_FALSE == hr) + { + pTheme->dwStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU; + pTheme->dwStyle |= (0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY) ? WS_POPUP : WS_OVERLAPPED; + } + + hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast(&pTheme->uStringId)); + ThmExitOnFailure(hr, "Failed to get window StringId attribute."); + + if (S_FALSE == hr) + { + pTheme->uStringId = UINT_MAX; + + hr = XmlGetAttribute(pixn, L"Caption", &bstr); + ThmExitOnFailure(hr, "Failed to get window Caption attribute."); + + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Window elements must contain the Caption or StringId attribute."); + } + + hr = StrAllocString(&pTheme->sczCaption, bstr, 0); + ThmExitOnFailure(hr, "Failed to copy window Caption attribute."); + } + + // Parse any image lists. + hr = ParseImageLists(hModule, wzRelativePath, pixn, pTheme); + ThmExitOnFailure(hr, "Failed to parse image lists."); + + // Parse the pages. + hr = ParsePages(hModule, wzRelativePath, pixn, pTheme); + ThmExitOnFailure(hr, "Failed to parse theme pages."); + + // Parse the non-paged controls. + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, NULL); + ThmExitOnFailure(hr, "Failed to parse theme controls."); + +LExit: + ReleaseStr(sczIconFile); + ReleaseBSTR(bstr); + ReleaseObject(pixn); + + return hr; +} + + +static HRESULT ParseFonts( + __in IXMLDOMElement* pElement, + __in THEME* pTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixn = NULL; + BSTR bstrName = NULL; + DWORD dwId = 0; + COLORREF crForeground = THEME_INVISIBLE_COLORREF; + COLORREF crBackground = THEME_INVISIBLE_COLORREF; + DWORD dwSystemForegroundColor = FALSE; + DWORD dwSystemBackgroundColor = FALSE; + + hr = XmlSelectNodes(pElement, L"Font", &pixnl); + ThmExitOnFailure(hr, "Failed to find font elements."); + + hr = pixnl->get_length(reinterpret_cast(&pTheme->cFonts)); + ThmExitOnFailure(hr, "Failed to count the number of theme fonts."); + + if (!pTheme->cFonts) + { + ExitFunction1(hr = S_OK); + } + + pTheme->rgFonts = static_cast(MemAlloc(sizeof(THEME_FONT) * pTheme->cFonts, TRUE)); + ThmExitOnNull(pTheme->rgFonts, hr, E_OUTOFMEMORY, "Failed to allocate theme fonts."); + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) + { + hr = XmlGetAttributeNumber(pixn, L"Id", &dwId); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find font id."); + + if (pTheme->cFonts <= dwId) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Invalid theme font id."); + } + + THEME_FONT* pFont = pTheme->rgFonts + dwId; + if (pFont->cFontInstances) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Theme font id duplicated."); + } + + pFont->lfQuality = CLEARTYPE_QUALITY; + + hr = XmlGetText(pixn, &bstrName); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to get font name."); + + hr = StrAllocString(&pFont->sczFaceName, bstrName, 0); + ThmExitOnFailure(hr, "Failed to copy font name."); + + hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pFont->lfHeight)); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find font height attribute."); + + hr = XmlGetAttributeNumber(pixn, L"Weight", reinterpret_cast(&pFont->lfWeight)); + if (S_FALSE == hr) + { + pFont->lfWeight = FW_DONTCARE; + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to find font weight attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"Underline", reinterpret_cast(&pFont->lfUnderline)); + if (E_NOTFOUND == hr) + { + pFont->lfUnderline = FALSE; + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to find font underline attribute."); + + hr = GetFontColor(pixn, L"Foreground", &crForeground, &dwSystemForegroundColor); + ThmExitOnFailure(hr, "Failed to find font foreground color."); + + hr = GetFontColor(pixn, L"Background", &crBackground, &dwSystemBackgroundColor); + ThmExitOnFailure(hr, "Failed to find font background color."); + + pFont->crForeground = crForeground; + if (THEME_INVISIBLE_COLORREF != pFont->crForeground) + { + pFont->hForeground = dwSystemForegroundColor ? ::GetSysColorBrush(dwSystemForegroundColor) : ::CreateSolidBrush(pFont->crForeground); + ThmExitOnNull(pFont->hForeground, hr, E_OUTOFMEMORY, "Failed to create text foreground brush."); + } + + pFont->crBackground = crBackground; + if (THEME_INVISIBLE_COLORREF != pFont->crBackground) + { + pFont->hBackground = dwSystemBackgroundColor ? ::GetSysColorBrush(dwSystemBackgroundColor) : ::CreateSolidBrush(pFont->crBackground); + ThmExitOnNull(pFont->hBackground, hr, E_OUTOFMEMORY, "Failed to create text background brush."); + } + + ReleaseNullBSTR(bstrName); + ReleaseNullObject(pixn); + } + ThmExitOnFailure(hr, "Failed to enumerate all fonts."); + + if (S_FALSE == hr) + { + hr = S_OK; + } + +LExit: + ReleaseBSTR(bstrName); + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + + +static HRESULT GetFontColor( + __in IXMLDOMNode* pixn, + __in_z LPCWSTR wzAttributeName, + __out COLORREF* pColorRef, + __out DWORD* pdwSystemColor + ) +{ + HRESULT hr = S_OK; + BSTR bstr = NULL; + + *pdwSystemColor = 0; + + hr = XmlGetAttribute(pixn, wzAttributeName, &bstr); + if (S_FALSE == hr) + { + *pColorRef = THEME_INVISIBLE_COLORREF; + ExitFunction1(hr = S_OK); + } + ThmExitOnFailure(hr, "Failed to find font %ls color.", wzAttributeName); + + if (pdwSystemColor) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btnface", -1)) + { + *pdwSystemColor = COLOR_BTNFACE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btntext", -1)) + { + *pdwSystemColor = COLOR_BTNTEXT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"graytext", -1)) + { + *pdwSystemColor = COLOR_GRAYTEXT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlight", -1)) + { + *pdwSystemColor = COLOR_HIGHLIGHT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlighttext", -1)) + { + *pdwSystemColor = COLOR_HIGHLIGHTTEXT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"hotlight", -1)) + { + *pdwSystemColor = COLOR_HOTLIGHT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"window", -1)) + { + *pdwSystemColor = COLOR_WINDOW; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"windowtext", -1)) + { + *pdwSystemColor = COLOR_WINDOWTEXT; + } + else + { + *pColorRef = wcstoul(bstr, NULL, 16); + } + + if (*pdwSystemColor) + { + *pColorRef = ::GetSysColor(*pdwSystemColor); + } + } + +LExit: + ReleaseBSTR(bstr); + + return hr; +} + +static HRESULT ParsePages( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixn = NULL; + BSTR bstrType = NULL; + THEME_PAGE* pPage = NULL; + DWORD iPage = 0; + + hr = XmlSelectNodes(pElement, L"Page", &pixnl); + ThmExitOnFailure(hr, "Failed to find page elements."); + + hr = pixnl->get_length(reinterpret_cast(&pTheme->cPages)); + ThmExitOnFailure(hr, "Failed to count the number of theme pages."); + + if (!pTheme->cPages) + { + ExitFunction1(hr = S_OK); + } + + pTheme->rgPages = static_cast(MemAlloc(sizeof(THEME_PAGE) * pTheme->cPages, TRUE)); + ThmExitOnNull(pTheme->rgPages, hr, E_OUTOFMEMORY, "Failed to allocate theme pages."); + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType))) + { + pPage = pTheme->rgPages + iPage; + + pPage->wId = static_cast(iPage + 1); + + hr = XmlGetAttributeEx(pixn, L"Name", &pPage->sczName); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying page Name."); + + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, pPage); + ThmExitOnFailure(hr, "Failed to parse page controls."); + + ++iPage; + + ReleaseNullBSTR(bstrType); + ReleaseNullObject(pixn); + } + ThmExitOnFailure(hr, "Failed to enumerate all pages."); + + if (S_FALSE == hr) + { + hr = S_OK; + } + +LExit: + ReleaseBSTR(bstrType); + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + + +static HRESULT ParseImageLists( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnlImageLists = NULL; + IXMLDOMNode* pixnImageList = NULL; + IXMLDOMNodeList* pixnlImages = NULL; + IXMLDOMNode* pixnImage = NULL; + DWORD dwImageListIndex = 0; + DWORD dwImageCount = 0; + HBITMAP hBitmap = NULL; + BITMAP bm = { }; + BSTR bstr = NULL; + DWORD i = 0; + int iRetVal = 0; + + hr = XmlSelectNodes(pElement, L"ImageList", &pixnlImageLists); + ThmExitOnFailure(hr, "Failed to find ImageList elements."); + + hr = pixnlImageLists->get_length(reinterpret_cast(&pTheme->cImageLists)); + ThmExitOnFailure(hr, "Failed to count the number of image lists."); + + if (!pTheme->cImageLists) + { + ExitFunction1(hr = S_OK); + } + + pTheme->rgImageLists = static_cast(MemAlloc(sizeof(THEME_IMAGELIST) * pTheme->cImageLists, TRUE)); + ThmExitOnNull(pTheme->rgImageLists, hr, E_OUTOFMEMORY, "Failed to allocate theme image lists."); + + while (S_OK == (hr = XmlNextElement(pixnlImageLists, &pixnImageList, NULL))) + { + hr = XmlGetAttribute(pixnImageList, L"Name", &bstr); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find ImageList/@Name attribute."); + + hr = StrAllocString(&pTheme->rgImageLists[dwImageListIndex].sczName, bstr, 0); + ThmExitOnFailure(hr, "Failed to make copy of ImageList name."); + + hr = XmlSelectNodes(pixnImageList, L"Image", &pixnlImages); + ThmExitOnFailure(hr, "Failed to select child Image nodes."); + + hr = pixnlImages->get_length(reinterpret_cast(&dwImageCount)); + ThmExitOnFailure(hr, "Failed to count the number of images in list."); + + if (0 < dwImageCount) + { + i = 0; + while (S_OK == (hr = XmlNextElement(pixnlImages, &pixnImage, NULL))) + { + if (hBitmap) + { + ::DeleteObject(hBitmap); + hBitmap = NULL; + } + hr = ParseImage(hModule, wzRelativePath, pixnImage, &hBitmap); + ThmExitOnFailure(hr, "Failed to parse image: %u", i); + + if (0 == i) + { + ::GetObjectW(hBitmap, sizeof(BITMAP), &bm); + + pTheme->rgImageLists[dwImageListIndex].hImageList = ImageList_Create(bm.bmWidth, bm.bmHeight, ILC_COLOR24, dwImageCount, 0); + ThmExitOnNullWithLastError(pTheme->rgImageLists[dwImageListIndex].hImageList, hr, "Failed to create image list."); + } + + iRetVal = ImageList_Add(pTheme->rgImageLists[dwImageListIndex].hImageList, hBitmap, NULL); + if (-1 == iRetVal) + { + ThmExitWithLastError(hr, "Failed to add image %u to image list.", i); + } + + ++i; + } + } + ++dwImageListIndex; + } + +LExit: + if (hBitmap) + { + ::DeleteObject(hBitmap); + } + ReleaseBSTR(bstr); + ReleaseObject(pixnlImageLists); + ReleaseObject(pixnImageList); + ReleaseObject(pixnlImages); + ReleaseObject(pixnImage); + + return hr; +} + +static void GetControls( + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __out DWORD** ppcControls, + __out THEME_CONTROL*** pprgControls + ) +{ + if (pParentControl) + { + *ppcControls = &pParentControl->cControls; + *pprgControls = &pParentControl->rgControls; + } + else + { + *ppcControls = &pTheme->cControls; + *pprgControls = &pTheme->rgControls; + } +} + +static void GetControls( + __in const THEME* pTheme, + __in_opt const THEME_CONTROL* pParentControl, + __out DWORD& cControls, + __out THEME_CONTROL*& rgControls + ) +{ + if (pParentControl) + { + cControls = pParentControl->cControls; + rgControls = pParentControl->rgControls; + } + else + { + cControls = pTheme->cControls; + rgControls = pTheme->rgControls; + } +} + +static HRESULT ParseControls( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pElement, + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in_opt THEME_PAGE* pPage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixn = NULL; + BSTR bstrType = NULL; + DWORD cNewControls = 0; + DWORD iControl = 0; + DWORD iPageControl = 0; + DWORD* pcControls = NULL; + THEME_CONTROL** prgControls = NULL; + + GetControls(pTheme, pParentControl, &pcControls, &prgControls); + + hr = ParseRadioButtons(hModule, wzRelativePath, pElement, pTheme, pParentControl, pPage); + ThmExitOnFailure(hr, "Failed to parse radio buttons."); + + hr = XmlSelectNodes(pElement, L"Billboard|Button|Checkbox|Combobox|CommandLink|Editbox|Hyperlink|Hypertext|ImageControl|Label|ListView|Panel|Progressbar|Richedit|Static|Tabs|TreeView", &pixnl); + ThmExitOnFailure(hr, "Failed to find control elements."); + + hr = pixnl->get_length(reinterpret_cast(&cNewControls)); + ThmExitOnFailure(hr, "Failed to count the number of theme controls."); + + if (!cNewControls) + { + ExitFunction1(hr = S_OK); + } + + hr = MemReAllocArray(reinterpret_cast(prgControls), *pcControls, sizeof(THEME_CONTROL), cNewControls); + ThmExitOnFailure(hr, "Failed to reallocate theme controls."); + + cNewControls += *pcControls; + + if (pPage) + { + iPageControl = pPage->cControlIndices; + pPage->cControlIndices += cNewControls; + } + + iControl = *pcControls; + *pcControls = cNewControls; + + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType))) + { + THEME_CONTROL_TYPE type = THEME_CONTROL_TYPE_UNKNOWN; + + if (!bstrType) + { + hr = E_UNEXPECTED; + ThmExitOnFailure(hr, "Null element encountered!"); + } + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Billboard", -1)) + { + type = THEME_CONTROL_TYPE_BILLBOARD; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Button", -1)) + { + type = THEME_CONTROL_TYPE_BUTTON; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Checkbox", -1)) + { + type = THEME_CONTROL_TYPE_CHECKBOX; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Combobox", -1)) + { + type = THEME_CONTROL_TYPE_COMBOBOX; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CommandLink", -1)) + { + type = THEME_CONTROL_TYPE_COMMANDLINK; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Editbox", -1)) + { + type = THEME_CONTROL_TYPE_EDITBOX; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hyperlink", -1)) + { + type = THEME_CONTROL_TYPE_HYPERLINK; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hypertext", -1)) + { + type = THEME_CONTROL_TYPE_HYPERTEXT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ImageControl", -1)) + { + type = THEME_CONTROL_TYPE_IMAGE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Label", -1)) + { + type = THEME_CONTROL_TYPE_LABEL; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ListView", -1)) + { + type = THEME_CONTROL_TYPE_LISTVIEW; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Panel", -1)) + { + type = THEME_CONTROL_TYPE_PANEL; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Progressbar", -1)) + { + type = THEME_CONTROL_TYPE_PROGRESSBAR; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Richedit", -1)) + { + type = THEME_CONTROL_TYPE_RICHEDIT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Static", -1)) + { + type = THEME_CONTROL_TYPE_STATIC; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Tabs", -1)) + { + type = THEME_CONTROL_TYPE_TAB; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"TreeView", -1)) + { + type = THEME_CONTROL_TYPE_TREEVIEW; + } + + if (THEME_CONTROL_TYPE_UNKNOWN != type) + { + THEME_CONTROL* pControl = *prgControls + iControl; + pControl->type = type; + + // billboard children are always the size of the billboard + BOOL fBillboardSizing = pParentControl && THEME_CONTROL_TYPE_BILLBOARD == pParentControl->type; + + hr = ParseControl(hModule, wzRelativePath, pixn, pTheme, pControl, fBillboardSizing, pPage); + ThmExitOnFailure(hr, "Failed to parse control."); + + if (fBillboardSizing) + { + pControl->nX = pControl->nDefaultDpiX = 0; + pControl->nY = pControl->nDefaultDpiY = 0; + pControl->nWidth = pControl->nDefaultDpiWidth = 0; + pControl->nHeight = pControl->nDefaultDpiHeight = 0; + } + + if (pPage) + { + pControl->wPageId = pPage->wId; + ++iPageControl; + } + + ++iControl; + } + + ReleaseNullBSTR(bstrType); + ReleaseNullObject(pixn); + } + ThmExitOnFailure(hr, "Failed to enumerate all controls."); + + if (S_FALSE == hr) + { + hr = S_OK; + } + + AssertSz(iControl == cNewControls, "The number of parsed controls didn't match the number of expected controls."); + +LExit: + ReleaseBSTR(bstrType); + ReleaseObject(pixn); + ReleaseObject(pixnl); + + return hr; +} + + +static HRESULT ParseControl( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pixn, + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in BOOL fSkipDimensions, + __in_opt THEME_PAGE* pPage + ) +{ + HRESULT hr = S_OK; + DWORD dwValue = 0; + BOOL fValue = FALSE; + BSTR bstrText = NULL; + BOOL fAnyTextChildren = FALSE; + BOOL fAnyNoteChildren = FALSE; + + hr = XmlGetAttributeEx(pixn, L"Name", &pControl->sczName); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying control Name attribute."); + + hr = XmlGetAttributeEx(pixn, L"EnableCondition", &pControl->sczEnableCondition); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying control EnableCondition attribute."); + + hr = XmlGetAttributeEx(pixn, L"VisibleCondition", &pControl->sczVisibleCondition); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying control VisibleCondition attribute."); + + if (!fSkipDimensions) + { + hr = XmlGetAttributeNumber(pixn, L"X", &dwValue); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find control X attribute."); + + pControl->nX = pControl->nDefaultDpiX = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"Y", &dwValue); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find control Y attribute."); + + pControl->nY = pControl->nDefaultDpiY = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find control Height attribute."); + + pControl->nHeight = pControl->nDefaultDpiHeight = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + ThmExitOnFailure(hr, "Failed to find control Width attribute."); + + pControl->nWidth = pControl->nDefaultDpiWidth = dwValue; + } + + // Parse the optional background resource image. + hr = ParseImage(hModule, wzRelativePath, pixn, &pControl->hImage); + ThmExitOnFailure(hr, "Failed while parsing control image."); + + hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast(&pControl->nSourceX)); + if (S_FALSE == hr) + { + pControl->nSourceX = -1; + } + ThmExitOnFailure(hr, "Failed when querying control SourceX attribute."); + + hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast(&pControl->nSourceY)); + if (S_FALSE == hr) + { + pControl->nSourceY = -1; + } + ThmExitOnFailure(hr, "Failed when querying control SourceY attribute."); + + hr = XmlGetAttributeNumber(pixn, L"FontId", &pControl->dwFontId); + if (S_FALSE == hr) + { + pControl->dwFontId = THEME_INVALID_ID; + } + ThmExitOnFailure(hr, "Failed when querying control FontId attribute."); + + // Parse the optional window style. + hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pControl->dwStyle); + ThmExitOnFailure(hr, "Failed when querying control HexStyle attribute."); + + // Parse the tabstop bit "shortcut nomenclature", this could have been set with the style above. + hr = XmlGetYesNoAttribute(pixn, L"TabStop", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ThmExitOnFailure(hr, "Failed when querying control TabStop attribute."); + + if (fValue) + { + pControl->dwStyle |= WS_TABSTOP; + } + } + + hr = XmlGetYesNoAttribute(pixn, L"Visible", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ThmExitOnFailure(hr, "Failed when querying control Visible attribute."); + + if (fValue) + { + pControl->dwStyle |= WS_VISIBLE; + } + } + + hr = XmlGetYesNoAttribute(pixn, L"HideWhenDisabled", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ThmExitOnFailure(hr, "Failed when querying control HideWhenDisabled attribute."); + + if (fValue) + { + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED; + } + } + + hr = XmlGetYesNoAttribute(pixn, L"DisableAutomaticBehavior", &pControl->fDisableVariableFunctionality); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ThmExitOnFailure(hr, "Failed when querying control DisableAutomaticBehavior attribute."); + } + + hr = ParseActions(pixn, pControl); + ThmExitOnFailure(hr, "Failed to parse action nodes of the control."); + + hr = ParseText(pixn, pControl, &fAnyTextChildren); + ThmExitOnFailure(hr, "Failed to parse text nodes of the control."); + + hr = ParseTooltips(pixn, pControl, &fAnyTextChildren); + ThmExitOnFailure(hr, "Failed to parse control Tooltip."); + + if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) + { + hr = ParseNotes(pixn, pControl, &fAnyNoteChildren); + ThmExitOnFailure(hr, "Failed to parse note text nodes of the control."); + } + + if (fAnyTextChildren || fAnyNoteChildren) + { + pControl->uStringId = UINT_MAX; + } + else + { + hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast(&pControl->uStringId)); + ThmExitOnFailure(hr, "Failed when querying control StringId attribute."); + + if (S_FALSE == hr) + { + pControl->uStringId = UINT_MAX; + + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type || THEME_CONTROL_TYPE_PANEL == pControl->type) + { + // Billboards and panels have child elements and we don't want to pick up child element text in the parents. + hr = S_OK; + } + else + { + hr = XmlGetText(pixn, &bstrText); + ThmExitOnFailure(hr, "Failed to get control inner text."); + + if (S_OK == hr) + { + hr = StrAllocString(&pControl->sczText, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy control text."); + + ReleaseNullBSTR(bstrText); + } + else if (S_FALSE == hr) + { + hr = S_OK; + } + } + } + } + + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type) + { + hr = XmlGetYesNoAttribute(pixn, L"Loop", &pControl->fBillboardLoops); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying Billboard/@Loop attribute."); + + pControl->wBillboardInterval = 5000; + hr = XmlGetAttributeNumber(pixn, L"Interval", &dwValue); + if (S_OK == hr && dwValue) + { + pControl->wBillboardInterval = static_cast(dwValue & 0xFFFF); + } + ThmExitOnFailure(hr, "Failed when querying Billboard/@Interval attribute."); + + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage); + ThmExitOnFailure(hr, "Failed to parse billboard children."); + } + else if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) + { + hr = ParseIcon(hModule, wzRelativePath, pixn, &pControl->hIcon); + ThmExitOnFailure(hr, "Failed while parsing control icon."); + } + else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type) + { + hr = XmlGetYesNoAttribute(pixn, L"FileSystemAutoComplete", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else + { + ThmExitOnFailure(hr, "Failed when querying Editbox/@FileSystemAutoComplete attribute."); + + if (fValue) + { + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE; + } + } + } + else if (THEME_CONTROL_TYPE_HYPERLINK == pControl->type || THEME_CONTROL_TYPE_BUTTON == pControl->type) + { + hr = XmlGetAttributeNumber(pixn, L"HoverFontId", &pControl->dwFontHoverId); + if (S_FALSE == hr) + { + pControl->dwFontHoverId = THEME_INVALID_ID; + } + ThmExitOnFailure(hr, "Failed when querying control HoverFontId attribute."); + + hr = XmlGetAttributeNumber(pixn, L"SelectedFontId", &pControl->dwFontSelectedId); + if (S_FALSE == hr) + { + pControl->dwFontSelectedId = THEME_INVALID_ID; + } + ThmExitOnFailure(hr, "Failed when querying control SelectedFontId attribute."); + } + else if (THEME_CONTROL_TYPE_LABEL == pControl->type) + { + hr = XmlGetYesNoAttribute(pixn, L"Center", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= SS_CENTER; + } + ThmExitOnFailure(hr, "Failed when querying Label/@Center attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"DisablePrefix", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= SS_NOPREFIX; + } + ThmExitOnFailure(hr, "Failed when querying Label/@DisablePrefix attribute."); + } + else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) + { + // Parse the optional extended window style. + hr = XmlGetAttributeNumberBase(pixn, L"HexExtendedStyle", 16, &pControl->dwExtendedStyle); + ThmExitOnFailure(hr, "Failed when querying ListView/@HexExtendedStyle attribute."); + + hr = XmlGetAttribute(pixn, L"ImageList", &bstrText); + if (S_FALSE != hr) + { + ThmExitOnFailure(hr, "Failed when querying ListView/@ImageList attribute."); + + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[0]); + ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageList for ListView.", bstrText); + } + + hr = XmlGetAttribute(pixn, L"ImageListSmall", &bstrText); + if (S_FALSE != hr) + { + ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListSmall attribute."); + + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[1]); + ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListSmall for ListView.", bstrText); + } + + hr = XmlGetAttribute(pixn, L"ImageListState", &bstrText); + if (S_FALSE != hr) + { + ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListState attribute."); + + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[2]); + ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListState for ListView.", bstrText); + } + + hr = XmlGetAttribute(pixn, L"ImageListGroupHeader", &bstrText); + if (S_FALSE != hr) + { + ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListGroupHeader attribute."); + + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[3]); + ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListGroupHeader for ListView.", bstrText); + } + + hr = ParseColumns(pixn, pControl); + ThmExitOnFailure(hr, "Failed to parse columns."); + } + else if (THEME_CONTROL_TYPE_PANEL == pControl->type) + { + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage); + ThmExitOnFailure(hr, "Failed to parse panel children."); + } + else if (THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type) + { + hr = XmlGetAttributeEx(pixn, L"Value", &pControl->sczValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying RadioButton/@Value attribute."); + } + else if (THEME_CONTROL_TYPE_TAB == pControl->type) + { + hr = ParseTabs(pixn, pControl); + ThmExitOnFailure(hr, "Failed to parse tabs"); + } + else if (THEME_CONTROL_TYPE_TREEVIEW == pControl->type) + { + pControl->dwStyle |= TVS_DISABLEDRAGDROP; + + hr = XmlGetYesNoAttribute(pixn, L"EnableDragDrop", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle &= ~TVS_DISABLEDRAGDROP; + } + ThmExitOnFailure(hr, "Failed when querying TreeView/@EnableDragDrop attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"FullRowSelect", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_FULLROWSELECT; + } + ThmExitOnFailure(hr, "Failed when querying TreeView/@FullRowSelect attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"HasButtons", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_HASBUTTONS; + } + ThmExitOnFailure(hr, "Failed when querying TreeView/@HasButtons attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"AlwaysShowSelect", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_SHOWSELALWAYS; + } + ThmExitOnFailure(hr, "Failed when querying TreeView/@AlwaysShowSelect attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"LinesAtRoot", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_LINESATROOT; + } + ThmExitOnFailure(hr, "Failed when querying TreeView/@LinesAtRoot attribute."); + + hr = XmlGetYesNoAttribute(pixn, L"HasLines", &fValue); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (fValue) + { + pControl->dwStyle |= TVS_HASLINES; + } + ThmExitOnFailure(hr, "Failed when querying TreeView/@HasLines attribute."); + } + +LExit: + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseActions( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrType = NULL; + + hr = XmlSelectNodes(pixn, L"BrowseDirectoryAction|ChangePageAction|CloseWindowAction", &pixnl); + ThmExitOnFailure(hr, "Failed to select child action nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cActions)); + ThmExitOnFailure(hr, "Failed to count the number of action nodes."); + + if (0 < pControl->cActions) + { + MemAllocArray(reinterpret_cast(&pControl->rgActions), sizeof(THEME_ACTION), pControl->cActions); + ThmExitOnNull(pControl->rgActions, hr, E_OUTOFMEMORY, "Failed to allocate THEME_ACTION structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, &bstrType))) + { + if (!bstrType) + { + hr = E_UNEXPECTED; + ThmExitOnFailure(hr, "Null element encountered!"); + } + + THEME_ACTION* pAction = pControl->rgActions + i; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"BrowseDirectoryAction", -1)) + { + pAction->type = THEME_ACTION_TYPE_BROWSE_DIRECTORY; + + hr = XmlGetAttributeEx(pixnChild, L"VariableName", &pAction->BrowseDirectory.sczVariableName); + ThmExitOnFailure(hr, "Failed when querying BrowseDirectoryAction/@VariableName attribute."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ChangePageAction", -1)) + { + pAction->type = THEME_ACTION_TYPE_CHANGE_PAGE; + + hr = XmlGetAttributeEx(pixnChild, L"Page", &pAction->ChangePage.sczPageName); + ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Page attribute."); + + hr = XmlGetYesNoAttribute(pixnChild, L"Cancel", &pAction->ChangePage.fCancel); + if (E_NOTFOUND != hr) + { + ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Cancel attribute."); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CloseWindowAction", -1)) + { + pAction->type = THEME_ACTION_TYPE_CLOSE_WINDOW; + } + else + { + hr = E_UNEXPECTED; + ThmExitOnFailure(hr, "Unexpected element encountered: %ls", bstrType); + } + + hr = XmlGetAttributeEx(pixnChild, L"Condition", &pAction->sczCondition); + if (E_NOTFOUND != hr) + { + ThmExitOnFailure(hr, "Failed when querying %ls/@Condition attribute.", bstrType); + } + + if (!pAction->sczCondition) + { + if (pControl->pDefaultAction) + { + hr = E_INVALIDDATA; + ThmExitOnFailure(hr, "Control '%ls' has multiple actions without a condition.", pControl->sczName); + } + + pControl->pDefaultAction = pAction; + } + + ++i; + ReleaseNullBSTR(bstrType); + ReleaseObject(pixnChild); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrType); + + return hr; +} + + +static HRESULT ParseColumns( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + DWORD dwValue = 0; + + hr = XmlSelectNodes(pixn, L"Column", &pixnl); + ThmExitOnFailure(hr, "Failed to select child column nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cColumns)); + ThmExitOnFailure(hr, "Failed to count the number of control columns."); + + if (0 < pControl->cColumns) + { + hr = MemAllocArray(reinterpret_cast(&pControl->ptcColumns), sizeof(THEME_COLUMN), pControl->cColumns); + ThmExitOnFailure(hr, "Failed to allocate column structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + THEME_COLUMN* pColumn = pControl->ptcColumns + i; + + hr = XmlGetText(pixnChild, &bstrText); + ThmExitOnFailure(hr, "Failed to get inner text of column element."); + + hr = XmlGetAttributeNumber(pixnChild, L"Width", &dwValue); + if (S_FALSE == hr) + { + dwValue = 100; + } + ThmExitOnFailure(hr, "Failed to get column width attribute."); + + pColumn->nBaseWidth = pColumn->nDefaultDpiBaseWidth = dwValue; + + hr = XmlGetYesNoAttribute(pixnChild, L"Expands", reinterpret_cast(&pColumn->fExpands)); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to get expands attribute."); + + hr = StrAllocString(&pColumn->pszName, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy column name."); + + ++i; + ReleaseNullBSTR(bstrText); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseRadioButtons( + __in_opt HMODULE hModule, + __in_opt LPCWSTR wzRelativePath, + __in IXMLDOMNode* pixn, + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in THEME_PAGE* pPage + ) +{ + HRESULT hr = S_OK; + DWORD cRadioButtons = 0; + DWORD iControl = 0; + DWORD iPageControl = 0; + IXMLDOMNodeList* pixnlRadioButtons = NULL; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnRadioButtons = NULL; + IXMLDOMNode* pixnChild = NULL; + LPWSTR sczName = NULL; + THEME_CONTROL* pControl = NULL; + BOOL fFirst = FALSE; + DWORD* pcControls = NULL; + THEME_CONTROL** prgControls = NULL; + + GetControls(pTheme, pParentControl, &pcControls, &prgControls); + + hr = XmlSelectNodes(pixn, L"RadioButtons", &pixnlRadioButtons); + ThmExitOnFailure(hr, "Failed to select RadioButtons nodes."); + + while (S_OK == (hr = XmlNextElement(pixnlRadioButtons, &pixnRadioButtons, NULL))) + { + hr = XmlGetAttributeEx(pixnRadioButtons, L"Name", &sczName); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying RadioButtons Name."); + + hr = XmlSelectNodes(pixnRadioButtons, L"RadioButton", &pixnl); + ThmExitOnFailure(hr, "Failed to select RadioButton nodes."); + + hr = pixnl->get_length(reinterpret_cast(&cRadioButtons)); + ThmExitOnFailure(hr, "Failed to count the number of RadioButton nodes."); + + if (cRadioButtons) + { + if (pPage) + { + iPageControl = pPage->cControlIndices; + pPage->cControlIndices += cRadioButtons; + } + + hr = MemReAllocArray(reinterpret_cast(prgControls), *pcControls, sizeof(THEME_CONTROL), cRadioButtons); + ThmExitOnFailure(hr, "Failed to reallocate theme controls."); + + iControl = *pcControls; + *pcControls += cRadioButtons; + + fFirst = TRUE; + + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + pControl = *prgControls + iControl; + pControl->type = THEME_CONTROL_TYPE_RADIOBUTTON; + + hr = ParseControl(hModule, wzRelativePath, pixnChild, pTheme, pControl, FALSE, pPage); + ThmExitOnFailure(hr, "Failed to parse control."); + + if (fFirst) + { + pControl->dwStyle |= WS_GROUP; + fFirst = FALSE; + } + + hr = StrAllocString(&pControl->sczVariable, sczName, 0); + ThmExitOnFailure(hr, "Failed to copy radio button variable."); + + if (pPage) + { + pControl->wPageId = pPage->wId; + ++iPageControl; + } + + ++iControl; + } + + if (!fFirst) + { + pControl->fLastRadioButton = TRUE; + } + } + } + +LExit: + ReleaseStr(sczName); + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseObject(pixnlRadioButtons); + ReleaseObject(pixnRadioButtons); + + return hr; +} + + +static HRESULT ParseTabs( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + + hr = XmlSelectNodes(pixn, L"Tab", &pixnl); + ThmExitOnFailure(hr, "Failed to select child tab nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cTabs)); + ThmExitOnFailure(hr, "Failed to count the number of tabs."); + + if (0 < pControl->cTabs) + { + hr = MemAllocArray(reinterpret_cast(&pControl->pttTabs), sizeof(THEME_TAB), pControl->cTabs); + ThmExitOnFailure(hr, "Failed to allocate tab structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + hr = XmlGetText(pixnChild, &bstrText); + ThmExitOnFailure(hr, "Failed to get inner text of tab element."); + + hr = StrAllocString(&(pControl->pttTabs[i].pszName), bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy tab name."); + + ++i; + ReleaseNullBSTR(bstrText); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseText( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __inout BOOL* pfAnyChildren + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + + hr = XmlSelectNodes(pixn, L"Text", &pixnl); + ThmExitOnFailure(hr, "Failed to select child Text nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cConditionalText)); + ThmExitOnFailure(hr, "Failed to count the number of Text nodes."); + + *pfAnyChildren |= 0 < pControl->cConditionalText; + + if (0 < pControl->cConditionalText) + { + MemAllocArray(reinterpret_cast(&pControl->rgConditionalText), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalText); + ThmExitOnNull(pControl->rgConditionalText, hr, E_OUTOFMEMORY, "Failed to allocate THEME_CONDITIONAL_TEXT structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + i; + + hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalText->sczCondition); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying Text/@Condition attribute."); + + hr = XmlGetText(pixnChild, &bstrText); + ThmExitOnFailure(hr, "Failed to get inner text of Text element."); + + if (S_OK == hr) + { + if (pConditionalText->sczCondition) + { + hr = StrAllocString(&pConditionalText->sczText, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy text to conditional text."); + + ++i; + } + else + { + if (pControl->sczText) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnFailure(hr, "Unconditional text for the '%ls' control is specified multiple times.", pControl->sczName); + } + + hr = StrAllocString(&pControl->sczText, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy text to control."); + + // Unconditional text entries aren't stored in the conditional text list. + --pControl->cConditionalText; + } + } + + ReleaseNullBSTR(bstrText); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseTooltips( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __inout BOOL* pfAnyChildren +) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + + hr = XmlSelectSingleNode(pixn, L"Tooltip", &pixnChild); + ThmExitOnFailure(hr, "Failed to select child Tooltip node."); + + if (S_OK == hr) + { + *pfAnyChildren |= TRUE; + + hr = XmlGetText(pixnChild, &bstrText); + ThmExitOnFailure(hr, "Failed to get inner text of Tooltip element."); + + if (S_OK == hr) + { + hr = StrAllocString(&pControl->sczTooltip, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy tooltip text to control."); + } + } + +LExit: + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT ParseNotes( + __in IXMLDOMNode* pixn, + __in THEME_CONTROL* pControl, + __out BOOL* pfAnyChildren + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + IXMLDOMNodeList* pixnl = NULL; + IXMLDOMNode* pixnChild = NULL; + BSTR bstrText = NULL; + + hr = XmlSelectNodes(pixn, L"Note", &pixnl); + ThmExitOnFailure(hr, "Failed to select child Note nodes."); + + hr = pixnl->get_length(reinterpret_cast(&pControl->cConditionalNotes)); + ThmExitOnFailure(hr, "Failed to count the number of Note nodes."); + + if (pfAnyChildren) + { + *pfAnyChildren = 0 < pControl->cConditionalNotes; + } + + if (0 < pControl->cConditionalNotes) + { + MemAllocArray(reinterpret_cast(&pControl->rgConditionalNotes), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalNotes); + ThmExitOnNull(pControl->rgConditionalNotes, hr, E_OUTOFMEMORY, "Failed to allocate note THEME_CONDITIONAL_TEXT structs."); + + i = 0; + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL))) + { + THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + i; + + hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalNote->sczCondition); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed when querying Note/@Condition attribute."); + + hr = XmlGetText(pixnChild, &bstrText); + ThmExitOnFailure(hr, "Failed to get inner text of Note element."); + + if (S_OK == hr) + { + if (pConditionalNote->sczCondition) + { + hr = StrAllocString(&pConditionalNote->sczText, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy text to conditional note text."); + + ++i; + } + else + { + if (pControl->sczNote) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnFailure(hr, "Unconditional note text for the '%ls' control is specified multiple times.", pControl->sczName); + } + + hr = StrAllocString(&pControl->sczNote, bstrText, 0); + ThmExitOnFailure(hr, "Failed to copy text to command link control."); + + // Unconditional note entries aren't stored in the conditional notes list. + --pControl->cConditionalNotes; + } + } + + ReleaseNullBSTR(bstrText); + } + } + +LExit: + ReleaseObject(pixnl); + ReleaseObject(pixnChild); + ReleaseBSTR(bstrText); + + return hr; +} + + +static HRESULT StartBillboard( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HRESULT hr = E_NOTFOUND; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (hWnd) + { + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hWnd)); + if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type) + { + // kick off + pControl->dwData = 0; + OnBillboardTimer(pTheme, pTheme->hwndParent, dwControl); + + if (!::SetTimer(pTheme->hwndParent, pControl->wId, pControl->wBillboardInterval, NULL)) + { + ThmExitWithLastError(hr, "Failed to start billboard."); + } + + hr = S_OK; + } + } + +LExit: + return hr; +} + + +static HRESULT StopBillboard( + __in THEME* pTheme, + __in DWORD dwControl + ) +{ + HRESULT hr = E_NOTFOUND; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + + if (hWnd) + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type) + { + ThemeControlEnable(pTheme, dwControl, FALSE); + + if (::KillTimer(pTheme->hwndParent, pControl->wId)) + { + hr = S_OK; + } + } + } + + return hr; +} + +static HRESULT EnsureFontInstance( + __in THEME* pTheme, + __in THEME_FONT* pFont, + __out THEME_FONT_INSTANCE** ppFontInstance + ) +{ + HRESULT hr = S_OK; + THEME_FONT_INSTANCE* pFontInstance = NULL; + LOGFONTW lf = { }; + + for (DWORD i = 0; i < pFont->cFontInstances; ++i) + { + pFontInstance = pFont->rgFontInstances + i; + if (pTheme->nDpi == pFontInstance->nDpi) + { + *ppFontInstance = pFontInstance; + ExitFunction(); + } + } + + hr = MemEnsureArraySize(reinterpret_cast(&pFont->rgFontInstances), pFont->cFontInstances, sizeof(THEME_FONT_INSTANCE), GROW_FONT_INSTANCES); + ThmExitOnFailure(hr, "Failed to allocate memory for font instances."); + + pFontInstance = pFont->rgFontInstances + pFont->cFontInstances; + pFontInstance->nDpi = pTheme->nDpi; + + lf.lfHeight = DpiuScaleValue(pFont->lfHeight, pFontInstance->nDpi); + lf.lfWeight = pFont->lfWeight; + lf.lfUnderline = pFont->lfUnderline; + lf.lfQuality = pFont->lfQuality; + + hr = ::StringCchCopyW(lf.lfFaceName, countof(lf.lfFaceName), pFont->sczFaceName); + ThmExitOnFailure(hr, "Failed to copy font name to create font."); + + pFontInstance->hFont = ::CreateFontIndirectW(&lf); + ThmExitOnNull(pFontInstance->hFont, hr, E_OUTOFMEMORY, "Failed to create DPI specific font."); + + ++pFont->cFontInstances; + *ppFontInstance = pFontInstance; + +LExit: + return hr; +} + + +static HRESULT FindImageList( + __in THEME* pTheme, + __in_z LPCWSTR wzImageListName, + __out HIMAGELIST *phImageList + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pTheme->cImageLists; ++i) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pTheme->rgImageLists[i].sczName, -1, wzImageListName, -1)) + { + *phImageList = pTheme->rgImageLists[i].hImageList; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + + +static HRESULT DrawButton( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ) +{ + int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; + int nSourceY = pControl->hImage ? 0 : pControl->nSourceY; + DWORD dwSourceWidth = pControl->nDefaultDpiWidth; + DWORD dwSourceHeight = pControl->nDefaultDpiHeight; + + HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); + + DWORD_PTR dwStyle = ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE); + // "clicked" gets priority + if (ODS_SELECTED & pdis->itemState) + { + nSourceY += pControl->nDefaultDpiHeight * 2; + } + // then hover + else if (pControl->dwData & THEME_CONTROL_DATA_HOVER) + { + nSourceY += pControl->nDefaultDpiHeight; + } + // then focused + else if (WS_TABSTOP & dwStyle && ODS_FOCUS & pdis->itemState) + { + nSourceY += pControl->nDefaultDpiHeight * 3; + } + + ::StretchBlt(pdis->hDC, 0, 0, pControl->nWidth, pControl->nHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); + + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); + + DrawControlText(pTheme, pdis, pControl, TRUE, FALSE); + + return S_OK; +} + + +static HRESULT DrawHyperlink( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ) +{ + DrawControlText(pTheme, pdis, pControl, FALSE, TRUE); + return S_OK; +} + + +static void DrawControlText( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl, + __in BOOL fCentered, + __in BOOL fDrawFocusRect + ) +{ + HRESULT hr = S_OK; + WCHAR wzText[256] = { }; + DWORD cchText = 0; + THEME_FONT* pFont = NULL; + THEME_FONT_INSTANCE* pFontInstance = NULL; + HFONT hfPrev = NULL; + + if (0 == (cchText = ::GetWindowTextW(pdis->hwndItem, wzText, countof(wzText)))) + { + // nothing to do + return; + } + + if (ODS_SELECTED & pdis->itemState) + { + pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontSelectedId ? pControl->dwFontSelectedId : pControl->dwFontId); + } + else if (pControl->dwData & THEME_CONTROL_DATA_HOVER) + { + pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontHoverId ? pControl->dwFontHoverId : pControl->dwFontId); + } + else + { + pFont = pTheme->rgFonts + pControl->dwFontId; + } + + hr = EnsureFontInstance(pTheme, pFont, &pFontInstance); + if (SUCCEEDED(hr)) + { + hfPrev = SelectFont(pdis->hDC, pFontInstance->hFont); + } + + ::DrawTextExW(pdis->hDC, wzText, cchText, &pdis->rcItem, DT_SINGLELINE | (fCentered ? (DT_CENTER | DT_VCENTER) : 0), NULL); + + if (fDrawFocusRect && (WS_TABSTOP & ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE)) && (ODS_FOCUS & pdis->itemState)) + { + ::DrawFocusRect(pdis->hDC, &pdis->rcItem); + } + + if (hfPrev) + { + SelectFont(pdis->hDC, hfPrev); + } +} + + +static HRESULT DrawImage( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ) +{ + DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top; + DWORD dwWidth = pdis->rcItem.right - pdis->rcItem.left; + int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; + int nSourceY = pControl->hImage ? 0 : pControl->nSourceY; + DWORD dwSourceHeight = pControl->nDefaultDpiHeight; + DWORD dwSourceWidth = pControl->nDefaultDpiWidth; + + BLENDFUNCTION bf = { }; + bf.BlendOp = AC_SRC_OVER; + bf.SourceConstantAlpha = 255; + bf.AlphaFormat = AC_SRC_ALPHA; + + HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); + + // Try to draw the image with transparency and if that fails (usually because the image has no + // alpha channel) then draw the image as is. + if (!::AlphaBlend(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, bf)) + { + ::StretchBlt(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY); + } + + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); + return S_OK; +} + + +static HRESULT DrawProgressBar( + __in THEME* pTheme, + __in DRAWITEMSTRUCT* pdis, + __in const THEME_CONTROL* pControl + ) +{ + DWORD dwProgressColor = HIWORD(pControl->dwData); + DWORD dwProgressPercentage = LOWORD(pControl->dwData); + DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top; + DWORD dwCenter = (pdis->rcItem.right - 2) * dwProgressPercentage / 100; + DWORD dwSourceHeight = pControl->nDefaultDpiHeight; + int nSourceX = pControl->hImage ? 0 : pControl->nSourceX; + int nSourceY = (pControl->hImage ? 0 : pControl->nSourceY) + (dwProgressColor * dwSourceHeight); + + HDC hdcMem = ::CreateCompatibleDC(pdis->hDC); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage)); + + // Draw the left side of the progress bar. + ::StretchBlt(pdis->hDC, 0, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwSourceHeight, SRCCOPY); + + // Draw the filled side of the progress bar, if there is any. + if (0 < dwCenter) + { + ::StretchBlt(pdis->hDC, 1, 0, dwCenter, dwHeight, hdcMem, nSourceX + 1, nSourceY, 1, dwSourceHeight, SRCCOPY); + } + + // Draw the unfilled side of the progress bar, if there is any. + if (dwCenter < static_cast(pdis->rcItem.right - 2)) + { + ::StretchBlt(pdis->hDC, 1 + dwCenter, 0, pdis->rcItem.right - dwCenter - 1, dwHeight, hdcMem, nSourceX + 2, nSourceY, 1, dwSourceHeight, SRCCOPY); + } + + // Draw the right side of the progress bar. + ::StretchBlt(pdis->hDC, pdis->rcItem.right - 1, 0, 1, dwHeight, hdcMem, nSourceX + 3, nSourceY, 1, dwSourceHeight, SRCCOPY); + + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); + return S_OK; +} + + +static BOOL DrawHoverControl( + __in THEME* pTheme, + __in BOOL fHover + ) +{ + BOOL fChangedHover = FALSE; + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, pTheme->hwndHover)); + + // Only hyperlinks and owner-drawn buttons have hover states. + if (pControl && (THEME_CONTROL_TYPE_HYPERLINK == pControl->type || + (THEME_CONTROL_TYPE_BUTTON == pControl->type && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW)))) + { + if (fHover) + { + pControl->dwData |= THEME_CONTROL_DATA_HOVER; + } + else + { + pControl->dwData &= ~THEME_CONTROL_DATA_HOVER; + } + + ::InvalidateRect(pControl->hWnd, NULL, FALSE); + fChangedHover = TRUE; + } + + return fChangedHover; +} + + +static void FreePage( + __in THEME_PAGE* pPage + ) +{ + if (pPage) + { + ReleaseStr(pPage->sczName); + + if (pPage->cSavedVariables) + { + for (DWORD i = 0; i < pPage->cSavedVariables; ++i) + { + ReleaseStr(pPage->rgSavedVariables[i].sczValue); + } + } + + ReleaseMem(pPage->rgSavedVariables); + } +} + + +static void FreeImageList( + __in THEME_IMAGELIST* pImageList + ) +{ + if (pImageList) + { + ReleaseStr(pImageList->sczName); + ImageList_Destroy(pImageList->hImageList); + } +} + +static void FreeControl( + __in THEME_CONTROL* pControl + ) +{ + if (pControl) + { + if (::IsWindow(pControl->hWnd)) + { + ::CloseWindow(pControl->hWnd); + pControl->hWnd = NULL; + } + + ReleaseStr(pControl->sczName); + ReleaseStr(pControl->sczText); + ReleaseStr(pControl->sczTooltip); + ReleaseStr(pControl->sczNote); + ReleaseStr(pControl->sczEnableCondition); + ReleaseStr(pControl->sczVisibleCondition); + ReleaseStr(pControl->sczValue); + ReleaseStr(pControl->sczVariable); + + if (pControl->hImage) + { + ::DeleteBitmap(pControl->hImage); + } + + for (DWORD i = 0; i < pControl->cControls; ++i) + { + FreeControl(pControl->rgControls + i); + } + + for (DWORD i = 0; i < pControl->cActions; ++i) + { + FreeAction(&(pControl->rgActions[i])); + } + + for (DWORD i = 0; i < pControl->cColumns; ++i) + { + FreeColumn(&(pControl->ptcColumns[i])); + } + + for (DWORD i = 0; i < pControl->cConditionalText; ++i) + { + FreeConditionalText(&(pControl->rgConditionalText[i])); + } + + for (DWORD i = 0; i < pControl->cConditionalNotes; ++i) + { + FreeConditionalText(&(pControl->rgConditionalNotes[i])); + } + + for (DWORD i = 0; i < pControl->cTabs; ++i) + { + FreeTab(&(pControl->pttTabs[i])); + } + + ReleaseMem(pControl->rgActions) + ReleaseMem(pControl->ptcColumns); + ReleaseMem(pControl->rgConditionalText); + ReleaseMem(pControl->rgConditionalNotes); + ReleaseMem(pControl->pttTabs); + } +} + + +static void FreeAction( + __in THEME_ACTION* pAction + ) +{ + switch (pAction->type) + { + case THEME_ACTION_TYPE_BROWSE_DIRECTORY: + ReleaseStr(pAction->BrowseDirectory.sczVariableName); + break; + case THEME_ACTION_TYPE_CHANGE_PAGE: + ReleaseStr(pAction->ChangePage.sczPageName); + break; + } + + ReleaseStr(pAction->sczCondition); +} + + +static void FreeColumn( + __in THEME_COLUMN* pColumn + ) +{ + ReleaseStr(pColumn->pszName); +} + + +static void FreeConditionalText( + __in THEME_CONDITIONAL_TEXT* pConditionalText + ) +{ + ReleaseStr(pConditionalText->sczCondition); + ReleaseStr(pConditionalText->sczText); +} + + +static void FreeTab( + __in THEME_TAB* pTab + ) +{ + ReleaseStr(pTab->pszName); +} + + +static void FreeFontInstance( + __in THEME_FONT_INSTANCE* pFontInstance + ) +{ + if (pFontInstance->hFont) + { + ::DeleteObject(pFontInstance->hFont); + pFontInstance->hFont = NULL; + } +} + + +static void FreeFont( + __in THEME_FONT* pFont + ) +{ + if (pFont) + { + if (pFont->hBackground) + { + ::DeleteObject(pFont->hBackground); + pFont->hBackground = NULL; + } + + if (pFont->hForeground) + { + ::DeleteObject(pFont->hForeground); + pFont->hForeground = NULL; + } + + for (DWORD i = 0; i < pFont->cFontInstances; ++i) + { + FreeFontInstance(&(pFont->rgFontInstances[i])); + } + + ReleaseMem(pFont->rgFontInstances); + ReleaseStr(pFont->sczFaceName); + } +} + + +static DWORD CALLBACK RichEditStreamFromFileHandleCallback( + __in DWORD_PTR dwCookie, + __in_bcount(cb) LPBYTE pbBuff, + __in LONG cb, + __in LONG* pcb + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = reinterpret_cast(dwCookie); + + if (!::ReadFile(hFile, pbBuff, cb, reinterpret_cast(pcb), NULL)) + { + ThmExitWithLastError(hr, "Failed to read file"); + } + +LExit: + return hr; +} + + +static DWORD CALLBACK RichEditStreamFromMemoryCallback( + __in DWORD_PTR dwCookie, + __in_bcount(cb) LPBYTE pbBuff, + __in LONG cb, + __in LONG* pcb + ) +{ + HRESULT hr = S_OK; + MEMBUFFER_FOR_RICHEDIT* pBuffer = reinterpret_cast(dwCookie); + DWORD cbCopy = 0; + + if (pBuffer->iData < pBuffer->cbData) + { + cbCopy = min(static_cast(cb), pBuffer->cbData - pBuffer->iData); + memcpy(pbBuff, pBuffer->rgbData + pBuffer->iData, cbCopy); + + pBuffer->iData += cbCopy; + Assert(pBuffer->iData <= pBuffer->cbData); + } + + *pcb = cbCopy; + return hr; +} + + +static void CALLBACK OnBillboardTimer( + __in THEME* pTheme, + __in HWND hwnd, + __in UINT_PTR idEvent + ) +{ + HWND hwndControl = ::GetDlgItem(hwnd, static_cast(idEvent)); + if (hwndControl) + { + THEME_CONTROL* pControl = const_cast(FindControlFromHWnd(pTheme, hwndControl)); + AssertSz(pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type, "Only billboard controls should get billboard timer messages."); + + if (pControl) + { + if (pControl->dwData < pControl->cControls) + { + ThemeShowChild(pTheme, pControl, pControl->dwData); + } + else if (pControl->fBillboardLoops) + { + pControl->dwData = 0; + ThemeShowChild(pTheme, pControl, pControl->dwData); + } + else // no more looping + { + ::KillTimer(hwnd, idEvent); + } + + ++pControl->dwData; + } + } +} + +static void OnBrowseDirectory( + __in THEME* pTheme, + __in HWND hWnd, + __in const THEME_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + WCHAR wzPath[MAX_PATH] = { }; + BROWSEINFOW browseInfo = { }; + PIDLIST_ABSOLUTE pidl = NULL; + + browseInfo.hwndOwner = hWnd; + browseInfo.pszDisplayName = wzPath; + browseInfo.lpszTitle = pTheme->sczCaption; + browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI; + pidl = ::SHBrowseForFolderW(&browseInfo); + if (pidl && ::SHGetPathFromIDListW(pidl, wzPath)) + { + // Since editbox changes aren't immediately saved off, we have to treat them differently. + THEME_CONTROL* pTargetControl = NULL; + + for (DWORD i = 0; i < pTheme->cControls; ++i) + { + THEME_CONTROL* pControl = pTheme->rgControls + i; + + if ((!pControl->wPageId || pControl->wPageId == pTheme->dwCurrentPageId) && + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pControl->sczName, -1, pAction->BrowseDirectory.sczVariableName, -1)) + { + pTargetControl = pControl; + break; + } + } + + if (pTargetControl && THEME_CONTROL_TYPE_EDITBOX == pTargetControl->type && !pTargetControl->fDisableVariableFunctionality) + { + hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath); + ThmExitOnFailure(hr, "Failed to set text on editbox: %ls", pTargetControl->sczName); + } + else if (pTheme->pfnSetStringVariable) + { + hr = pTheme->pfnSetStringVariable(pAction->BrowseDirectory.sczVariableName, wzPath, FALSE, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to set variable: %ls", pAction->BrowseDirectory.sczVariableName); + } + else if (pTargetControl) + { + hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath); + ThmExitOnFailure(hr, "Failed to set text on control: %ls", pTargetControl->sczName); + } + + ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH); + } + +LExit: + if (pidl) + { + ::CoTaskMemFree(pidl); + } +} + +static BOOL OnButtonClicked( + __in THEME* pTheme, + __in HWND hWnd, + __in const THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + BOOL fHandled = FALSE; + + if (THEME_CONTROL_TYPE_BUTTON == pControl->type || THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) + { + if (pControl->cActions) + { + fHandled = TRUE; + THEME_ACTION* pChosenAction = pControl->pDefaultAction; + + if (pTheme->pfnEvaluateCondition) + { + // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined. + // This is the current implementation and can change at any time. + for (DWORD j = 0; j < pControl->cActions; ++j) + { + THEME_ACTION* pAction = pControl->rgActions + j; + + if (pAction->sczCondition) + { + BOOL fCondition = FALSE; + + hr = pTheme->pfnEvaluateCondition(pAction->sczCondition, &fCondition, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate condition: %ls", pAction->sczCondition); + + if (fCondition) + { + pChosenAction = pAction; + break; + } + } + } + } + + if (pChosenAction) + { + switch (pChosenAction->type) + { + case THEME_ACTION_TYPE_BROWSE_DIRECTORY: + OnBrowseDirectory(pTheme, hWnd, pChosenAction); + break; + + case THEME_ACTION_TYPE_CLOSE_WINDOW: + ::SendMessageW(hWnd, WM_CLOSE, 0, 0); + break; + + case THEME_ACTION_TYPE_CHANGE_PAGE: + DWORD dwPageId = 0; + LPCWSTR pPageNames = pChosenAction->ChangePage.sczPageName; + ThemeGetPageIds(pTheme, &pPageNames, &dwPageId, 1); + + if (!dwPageId) + { + ThmExitOnFailure(E_INVALIDDATA, "Unknown page: %ls", pChosenAction->ChangePage.sczPageName); + } + + ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_HIDE, pChosenAction->ChangePage.fCancel ? THEME_SHOW_PAGE_REASON_CANCEL : THEME_SHOW_PAGE_REASON_DEFAULT); + ThemeShowPage(pTheme, dwPageId, SW_SHOW); + break; + } + } + } + } + else if (!pControl->fDisableVariableFunctionality && (pTheme->pfnSetNumericVariable || pTheme->pfnSetStringVariable)) + { + BOOL fRefresh = FALSE; + + switch (pControl->type) + { + case THEME_CONTROL_TYPE_CHECKBOX: + if (pTheme->pfnSetNumericVariable && pControl->sczName && *pControl->sczName) + { + BOOL fChecked = ThemeIsControlChecked(pTheme, pControl->wId); + pTheme->pfnSetNumericVariable(pControl->sczName, fChecked ? 1 : 0, pTheme->pvVariableContext); + fRefresh = TRUE; + } + break; + case THEME_CONTROL_TYPE_RADIOBUTTON: + if (pTheme->pfnSetStringVariable && pControl->sczVariable && *pControl->sczVariable && ThemeIsControlChecked(pTheme, pControl->wId)) + { + pTheme->pfnSetStringVariable(pControl->sczVariable, pControl->sczValue, FALSE, pTheme->pvVariableContext); + fRefresh = TRUE; + } + break; + } + + if (fRefresh) + { + ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH); + fHandled = TRUE; + } + } + +LExit: + return fHandled; +} + +static BOOL OnDpiChanged( + __in THEME* pTheme, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + UINT nDpi = HIWORD(wParam); + RECT* pRect = reinterpret_cast(lParam); + BOOL fIgnored = pTheme->nDpi == nDpi; + + if (fIgnored) + { + ExitFunction(); + } + + + pTheme->fForceResize = !pTheme->fAutoResize; + ScaleThemeFromWindow(pTheme, nDpi, pRect->left, pRect->top); + +LExit: + return !fIgnored; +} + +static void OnNcCreate( + __in THEME* pTheme, + __in HWND hWnd, + __in LPARAM lParam + ) +{ + DPIU_WINDOW_CONTEXT windowContext = { }; + CREATESTRUCTW* pCreateStruct = reinterpret_cast(lParam); + + pTheme->hwndParent = hWnd; + + DpiuGetWindowContext(pTheme->hwndParent, &windowContext); + + if (windowContext.nDpi != pTheme->nDpi) + { + ScaleTheme(pTheme, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y, pCreateStruct->style, NULL != pCreateStruct->hMenu, pCreateStruct->dwExStyle); + } +} + +static HRESULT OnRichEditEnLink( + __in LPARAM lParam, + __in HWND hWndRichEdit, + __in HWND hWnd + ) +{ + HRESULT hr = S_OK; + LPWSTR sczLink = NULL; + ENLINK* link = reinterpret_cast(lParam); + + switch (link->msg) + { + case WM_LBUTTONDOWN: + { + hr = StrAlloc(&sczLink, link->chrg.cpMax - link->chrg.cpMin + 2); + ThmExitOnFailure(hr, "Failed to allocate string for link."); + + TEXTRANGEW tr; + tr.chrg.cpMin = link->chrg.cpMin; + tr.chrg.cpMax = link->chrg.cpMax; + tr.lpstrText = sczLink; + + if (0 < ::SendMessageW(hWndRichEdit, EM_GETTEXTRANGE, 0, reinterpret_cast(&tr))) + { + hr = ShelExec(sczLink, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL); + ThmExitOnFailure(hr, "Failed to launch link: %ls", sczLink); + } + + break; + } + + case WM_SETCURSOR: + ::SetCursor(vhCursorHand); + break; + } + +LExit: + ReleaseStr(sczLink); + + return hr; +} + +static BOOL ControlIsType( + __in const THEME* pTheme, + __in DWORD dwControl, + __in const THEME_CONTROL_TYPE type + ) +{ + BOOL fIsType = FALSE; + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl); + if (hWnd) + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd); + fIsType = (pControl && type == pControl->type); + } + + return fIsType; +} + +static const THEME_CONTROL* FindControlFromHWnd( + __in const THEME* pTheme, + __in HWND hWnd, + __in_opt const THEME_CONTROL* pParentControl + ) +{ + DWORD cControls = 0; + THEME_CONTROL* rgControls = NULL; + + GetControls(pTheme, pParentControl, cControls, rgControls); + + // As we can't use GWLP_USERDATA (SysLink controls on Windows XP uses it too)... + for (DWORD i = 0; i < cControls; ++i) + { + if (hWnd == rgControls[i].hWnd) + { + return rgControls + i; + } + else if (0 < rgControls[i].cControls) + { + const THEME_CONTROL* pChildControl = FindControlFromHWnd(pTheme, hWnd, rgControls + i); + if (pChildControl) + { + return pChildControl; + } + } + } + + return NULL; +} + +static void GetControlDimensions( + __in const THEME_CONTROL* pControl, + __in const RECT* prcParent, + __out int* piWidth, + __out int* piHeight, + __out int* piX, + __out int* piY + ) +{ + *piWidth = pControl->nWidth + (0 < pControl->nWidth ? 0 : prcParent->right - max(0, pControl->nX)); + *piHeight = pControl->nHeight + (0 < pControl->nHeight ? 0 : prcParent->bottom - max(0, pControl->nY)); + *piX = pControl->nX + (-1 < pControl->nX ? 0 : prcParent->right - *piWidth); + *piY = pControl->nY + (-1 < pControl->nY ? 0 : prcParent->bottom - *piHeight); +} + +static HRESULT SizeListViewColumns( + __inout THEME_CONTROL* pControl + ) +{ + HRESULT hr = S_OK; + RECT rcParent = { }; + int cNumExpandingColumns = 0; + int iExtraAvailableSize; + + if (!::GetWindowRect(pControl->hWnd, &rcParent)) + { + ThmExitWithLastError(hr, "Failed to get window rect of listview control."); + } + + iExtraAvailableSize = rcParent.right - rcParent.left; + + for (DWORD i = 0; i < pControl->cColumns; ++i) + { + if (pControl->ptcColumns[i].fExpands) + { + ++cNumExpandingColumns; + } + + iExtraAvailableSize -= pControl->ptcColumns[i].nBaseWidth; + } + + // Leave room for a vertical scroll bar just in case. + iExtraAvailableSize -= ::GetSystemMetrics(SM_CXVSCROLL); + + for (DWORD i = 0; i < pControl->cColumns; ++i) + { + if (pControl->ptcColumns[i].fExpands) + { + pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth + (iExtraAvailableSize / cNumExpandingColumns); + // In case there is any remainder, use it up the first chance we get. + pControl->ptcColumns[i].nWidth += iExtraAvailableSize % cNumExpandingColumns; + iExtraAvailableSize -= iExtraAvailableSize % cNumExpandingColumns; + } + else + { + pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth; + } + } + +LExit: + return hr; +} + + +static HRESULT ShowControl( + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in int nCmdShow, + __in BOOL fSaveEditboxes, + __in THEME_SHOW_PAGE_REASON reason, + __in DWORD dwPageId, + __out_opt HWND* phwndFocus + ) +{ + HRESULT hr = S_OK; + DWORD iPageControl = 0; + HWND hwndFocus = NULL; + LPWSTR sczFormatString = NULL; + LPWSTR sczText = NULL; + THEME_SAVEDVARIABLE* pSavedVariable = NULL; + BOOL fHide = SW_HIDE == nCmdShow; + THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPageId); + + // Save the editbox value if necessary (other control types save their values immediately). + if (pTheme->pfnSetStringVariable && !pControl->fDisableVariableFunctionality && + fSaveEditboxes && THEME_CONTROL_TYPE_EDITBOX == pControl->type && pControl->sczName && *pControl->sczName) + { + hr = ThemeGetTextControl(pTheme, pControl->wId, &sczText); + ThmExitOnFailure(hr, "Failed to get the text for control: %ls", pControl->sczName); + + hr = pTheme->pfnSetStringVariable(pControl->sczName, sczText, FALSE, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to set the variable '%ls' to '%ls'", pControl->sczName, sczText); + } + + HWND hWnd = pControl->hWnd; + + if (fHide && pControl->wPageId) + { + ::ShowWindow(hWnd, SW_HIDE); + + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type) + { + StopBillboard(pTheme, pControl->wId); + } + + ExitFunction(); + } + + BOOL fEnabled = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED); + BOOL fVisible = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDDEN); + + if (!pControl->fDisableVariableFunctionality) + { + if (pTheme->pfnEvaluateCondition) + { + // If the control has a VisibleCondition, check if it's true. + if (pControl->sczVisibleCondition) + { + hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); + } + + // If the control has an EnableCondition, check if it's true. + if (pControl->sczEnableCondition) + { + hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnabled, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); + } + } + + // Try to format each control's text based on context, except for editboxes since their text comes from the user. + if (pTheme->pfnFormatString && ((pControl->sczText && *pControl->sczText) || pControl->cConditionalText) && THEME_CONTROL_TYPE_EDITBOX != pControl->type) + { + LPWSTR wzText = pControl->sczText; + LPWSTR wzNote = pControl->sczNote; + + if (pTheme->pfnEvaluateCondition) + { + // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined. + // This is the current implementation and can change at any time. + for (DWORD j = 0; j < pControl->cConditionalText; ++j) + { + THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + j; + wzText = pConditionalText->sczText; + + if (pConditionalText->sczCondition) + { + BOOL fCondition = FALSE; + + hr = pTheme->pfnEvaluateCondition(pConditionalText->sczCondition, &fCondition, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate condition: %ls", pConditionalText->sczCondition); + + if (fCondition) + { + wzText = pConditionalText->sczText; + break; + } + } + } + + for (DWORD j = 0; j < pControl->cConditionalNotes; ++j) + { + THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + j; + wzNote = pConditionalNote->sczText; + + if (pConditionalNote->sczCondition) + { + BOOL fCondition = FALSE; + + hr = pTheme->pfnEvaluateCondition(pConditionalNote->sczCondition, &fCondition, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate note condition: %ls", pConditionalNote->sczCondition); + + if (fCondition) + { + wzNote = pConditionalNote->sczText; + break; + } + } + } + } + + if (wzText && *wzText) + { + hr = pTheme->pfnFormatString(wzText, &sczText, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to format string: %ls", wzText); + } + else + { + ReleaseNullStr(sczText); + } + + ThemeSetTextControl(pTheme, pControl->wId, sczText); + + if (wzNote && *wzNote) + { + hr = pTheme->pfnFormatString(wzNote, &sczText, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to format note: %ls", wzNote); + } + else + { + ReleaseNullStr(sczText); + } + + ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast(sczText)); + } + + // If this is a named control, do variable magic. + if (pControl->sczName && *pControl->sczName) + { + // If this is a checkbox control, + // try to set its default state to the state of a matching named variable. + if (pTheme->pfnGetNumericVariable && THEME_CONTROL_TYPE_CHECKBOX == pControl->type) + { + LONGLONG llValue = 0; + hr = pTheme->pfnGetNumericVariable(pControl->sczName, &llValue, pTheme->pvVariableContext); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ThmExitOnFailure(hr, "Failed to get numeric variable: %ls", pControl->sczName); + + if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId) + { + pSavedVariable = pPage->rgSavedVariables + iPageControl; + pSavedVariable->wzName = pControl->sczName; + + if (SUCCEEDED(hr)) + { + hr = StrAllocFormattedSecure(&pSavedVariable->sczValue, L"%lld", llValue); + ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); + } + + ++iPageControl; + } + + ThemeSendControlMessage(pTheme, pControl->wId, BM_SETCHECK, SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED, 0); + } + + // If this is an editbox control, + // try to set its default state to the state of a matching named variable. + if (pTheme->pfnFormatString && THEME_CONTROL_TYPE_EDITBOX == pControl->type) + { + hr = StrAllocFormatted(&sczFormatString, L"[%ls]", pControl->sczName); + ThmExitOnFailure(hr, "Failed to create format string: '%ls'", pControl->sczName); + + hr = pTheme->pfnFormatString(sczFormatString, &sczText, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to format string: '%ls'", sczFormatString); + + if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId) + { + pSavedVariable = pPage->rgSavedVariables + iPageControl; + pSavedVariable->wzName = pControl->sczName; + + if (SUCCEEDED(hr)) + { + hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0); + ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName); + } + + ++iPageControl; + } + + ThemeSetTextControl(pTheme, pControl->wId, sczText); + } + } + + // If this is a radio button associated with a variable, + // try to set its default state to the state of the variable. + if (pTheme->pfnGetStringVariable && THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type && pControl->sczVariable && *pControl->sczVariable) + { + hr = pTheme->pfnGetStringVariable(pControl->sczVariable, &sczText, pTheme->pvVariableContext); + if (E_NOTFOUND == hr) + { + ReleaseNullStr(sczText); + } + else + { + ThmExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczVariable); + } + + if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId && pControl->fLastRadioButton) + { + pSavedVariable = pPage->rgSavedVariables + iPageControl; + pSavedVariable->wzName = pControl->sczVariable; + + if (SUCCEEDED(hr)) + { + hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0); + ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczVariable); + } + + ++iPageControl; + } + + hr = S_OK; + + Button_SetCheck(hWnd, (!sczText && !pControl->sczValue) || (sczText && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczText, -1, pControl->sczValue, -1))); + } + } + + if (!fVisible || (!fEnabled && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED))) + { + ::ShowWindow(hWnd, SW_HIDE); + } + else + { + ::EnableWindow(hWnd, !fHide && fEnabled); + + if (!hwndFocus && pControl->wPageId && (pControl->dwStyle & WS_TABSTOP)) + { + hwndFocus = hWnd; + } + + ::ShowWindow(hWnd, nCmdShow); + } + + if (0 < pControl->cControls) + { + ShowControls(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId); + } + + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type && pControl->wPageId) + { + if (fEnabled) + { + StartBillboard(pTheme, pControl->wId); + } + else + { + StopBillboard(pTheme, pControl->wId); + } + } + + if (phwndFocus) + { + *phwndFocus = hwndFocus; + } + +LExit: + ReleaseStr(sczFormatString); + ReleaseStr(sczText); + + return hr; +} + +static HRESULT ShowControls( + __in THEME* pTheme, + __in_opt const THEME_CONTROL* pParentControl, + __in int nCmdShow, + __in BOOL fSaveEditboxes, + __in THEME_SHOW_PAGE_REASON reason, + __in DWORD dwPageId + ) +{ + HRESULT hr = S_OK; + HWND hwndFocus = NULL; + DWORD cControls = 0; + THEME_CONTROL* rgControls = NULL; + + GetControls(pTheme, pParentControl, cControls, rgControls); + + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + + // Only look at non-page controls and the specified page's controls. + if (!pControl->wPageId || pControl->wPageId == dwPageId) + { + hr = ShowControl(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId, &hwndFocus); + ThmExitOnFailure(hr, "Failed to show control '%ls' at index %d.", pControl->sczName, i); + } + } + + if (hwndFocus) + { + ::SetFocus(hwndFocus); + } + +LExit: + return hr; +} + + +static LRESULT CALLBACK ControlGroupDefWindowProc( + __in_opt THEME* pTheme, + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + if (pTheme) + { + switch (uMsg) + { + case WM_DRAWITEM: + ThemeDrawControl(pTheme, reinterpret_cast(lParam)); + return TRUE; + + case WM_CTLCOLORBTN: __fallthrough; + case WM_CTLCOLORSTATIC: + { + HBRUSH hBrush = NULL; + if (ThemeSetControlColor(pTheme, reinterpret_cast(wParam), reinterpret_cast(lParam), &hBrush)) + { + return reinterpret_cast(hBrush); + } + } + break; + + case WM_SETCURSOR: + if (ThemeHoverControl(pTheme, hWnd, reinterpret_cast(wParam))) + { + return TRUE; + } + break; + + case WM_PAINT: + if (::GetUpdateRect(hWnd, NULL, FALSE)) + { + PAINTSTRUCT ps; + ::BeginPaint(hWnd, &ps); + if (hWnd == pTheme->hwndParent) + { + ThemeDrawBackground(pTheme, &ps); + } + ::EndPaint(hWnd, &ps); + } + return 0; + + case WM_TIMER: + OnBillboardTimer(pTheme, hWnd, wParam); + break; + + case WM_NOTIFY: + if (lParam) + { + LPNMHDR pnmhdr = reinterpret_cast(lParam); + switch (pnmhdr->code) + { + // Tab/Shift+Tab support for rich-edit control. + case EN_MSGFILTER: + { + MSGFILTER* msgFilter = reinterpret_cast(lParam); + if (WM_KEYDOWN == msgFilter->msg && VK_TAB == msgFilter->wParam) + { + BOOL fShift = 0x8000 & ::GetKeyState(VK_SHIFT); + HWND hwndFocus = ::GetNextDlgTabItem(hWnd, msgFilter->nmhdr.hwndFrom, fShift); + ::SetFocus(hwndFocus); + return 1; + } + break; + } + + // Hyperlink clicks from rich-edit control. + case EN_LINK: + return SUCCEEDED(OnRichEditEnLink(lParam, pnmhdr->hwndFrom, hWnd)); + + // Clicks on a hypertext/syslink control. + case NM_CLICK: __fallthrough; + case NM_RETURN: + if (ControlIsType(pTheme, static_cast(pnmhdr->idFrom), THEME_CONTROL_TYPE_HYPERTEXT)) + { + PNMLINK pnmlink = reinterpret_cast(lParam); + LITEM litem = pnmlink->item; + ShelExec(litem.szUrl, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL); + return 1; + } + + return 0; + } + } + break; + + case WM_COMMAND: + switch (HIWORD(wParam)) + { + case BN_CLICKED: + if (lParam) + { + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, (HWND)lParam); + if (pControl && OnButtonClicked(pTheme, hWnd, pControl)) + { + return 0; + } + } + break; + } + break; + } + } + + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + + +static LRESULT CALLBACK PanelWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + LRESULT lres = 0; + THEME* pTheme = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); + + switch (uMsg) + { + case WM_NCCREATE: + { + LPCREATESTRUCTW lpcs = reinterpret_cast(lParam); + pTheme = reinterpret_cast(lpcs->lpCreateParams); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pTheme)); + } + break; + + case WM_NCDESTROY: + lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + return lres; + + case WM_NCHITTEST: + return HTCLIENT; + break; + } + + return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); +} + +static LRESULT CALLBACK StaticOwnerDrawWndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_UPDATEUISTATE: + return ::DefWindowProc(hWnd, uMsg, wParam, lParam); + default: + return (*vpfnStaticOwnerDrawBaseWndProc)(hWnd, uMsg, wParam, lParam); + } +} + +static HRESULT LoadControls( + __in THEME* pTheme, + __in_opt THEME_CONTROL* pParentControl, + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds, + __in DWORD cAssignControlIds + ) +{ + HRESULT hr = S_OK; + RECT rcParent = { }; + LPWSTR sczText = NULL; + BOOL fStartNewGroup = FALSE; + DWORD cControls = 0; + THEME_CONTROL* rgControls = NULL; + HWND hwndParent = pParentControl ? pParentControl->hWnd : pTheme->hwndParent; + int w = 0; + int h = 0; + int x = 0; + int y = 0; + + GetControls(pTheme, pParentControl, cControls, rgControls); + ::GetClientRect(hwndParent, &rcParent); + + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL; + THEME_FONT_INSTANCE* pControlFontInstance = NULL; + LPCWSTR wzWindowClass = NULL; + DWORD dwWindowBits = WS_CHILD; + DWORD dwWindowExBits = 0; + + if (fStartNewGroup) + { + dwWindowBits |= WS_GROUP; + fStartNewGroup = FALSE; + } + + switch (pControl->type) + { + case THEME_CONTROL_TYPE_BILLBOARD: + __fallthrough; + case THEME_CONTROL_TYPE_PANEL: + wzWindowClass = THEME_WC_PANEL; + dwWindowBits |= WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + dwWindowExBits |= WS_EX_TRANSPARENT | WS_EX_CONTROLPARENT; +#ifdef DEBUG + StrAllocFormatted(&pControl->sczText, L"Panel '%ls', id: %d", pControl->sczName, pControl->wId); +#endif + break; + + case THEME_CONTROL_TYPE_CHECKBOX: + dwWindowBits |= BS_AUTOCHECKBOX | BS_MULTILINE; // checkboxes are basically buttons with an extra bit tossed in. + __fallthrough; + case THEME_CONTROL_TYPE_BUTTON: + wzWindowClass = WC_BUTTONW; + if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) + { + dwWindowBits |= BS_OWNERDRAW; + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; + } + break; + + case THEME_CONTROL_TYPE_COMBOBOX: + wzWindowClass = WC_COMBOBOXW; + dwWindowBits |= CBS_DROPDOWNLIST | CBS_HASSTRINGS; + break; + + case THEME_CONTROL_TYPE_COMMANDLINK: + wzWindowClass = WC_BUTTONW; + dwWindowBits |= BS_COMMANDLINK; + break; + + case THEME_CONTROL_TYPE_EDITBOX: + wzWindowClass = WC_EDITW; + dwWindowBits |= ES_LEFT | ES_AUTOHSCROLL; + dwWindowExBits = WS_EX_CLIENTEDGE; + break; + + case THEME_CONTROL_TYPE_HYPERLINK: // hyperlinks are basically just owner drawn buttons. + wzWindowClass = THEME_WC_HYPERLINK; + dwWindowBits |= BS_OWNERDRAW | BTNS_NOPREFIX; + break; + + case THEME_CONTROL_TYPE_HYPERTEXT: + wzWindowClass = WC_LINK; + dwWindowBits |= LWS_NOPREFIX; + break; + + case THEME_CONTROL_TYPE_IMAGE: // images are basically just owner drawn static controls (so we can draw .jpgs and .pngs instead of just bitmaps). + if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) + { + wzWindowClass = THEME_WC_STATICOWNERDRAW; + dwWindowBits |= SS_OWNERDRAW; + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ThmExitOnRootFailure(hr, "Invalid image or image list coordinates."); + } + break; + + case THEME_CONTROL_TYPE_LABEL: + wzWindowClass = WC_STATICW; + break; + + case THEME_CONTROL_TYPE_LISTVIEW: + // If thmutil is handling the image list for this listview, tell Windows not to free it when the control is destroyed. + if (pControl->rghImageList[0] || pControl->rghImageList[1] || pControl->rghImageList[2] || pControl->rghImageList[3]) + { + pControl->dwStyle |= LVS_SHAREIMAGELISTS; + } + wzWindowClass = WC_LISTVIEWW; + break; + + case THEME_CONTROL_TYPE_PROGRESSBAR: + if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY)) + { + wzWindowClass = THEME_WC_STATICOWNERDRAW; // no such thing as an owner drawn progress bar so we'll make our own out of a static control. + dwWindowBits |= SS_OWNERDRAW; + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW; + } + else + { + wzWindowClass = PROGRESS_CLASSW; + } + break; + + case THEME_CONTROL_TYPE_RADIOBUTTON: + dwWindowBits |= BS_AUTORADIOBUTTON | BS_MULTILINE; + wzWindowClass = WC_BUTTONW; + + if (pControl->fLastRadioButton) + { + fStartNewGroup = TRUE; + } + break; + + case THEME_CONTROL_TYPE_RICHEDIT: + if (!vhModuleMsftEdit && !vhModuleRichEd) + { + hr = LoadSystemLibrary(L"Msftedit.dll", &vhModuleMsftEdit); + if (FAILED(hr)) + { + hr = LoadSystemLibrary(L"Riched20.dll", &vhModuleRichEd); + ThmExitOnFailure(hr, "Failed to load Rich Edit control library."); + } + } + + wzWindowClass = vhModuleMsftEdit ? MSFTEDIT_CLASS : RICHEDIT_CLASSW; + dwWindowBits |= ES_AUTOVSCROLL | ES_MULTILINE | WS_VSCROLL | ES_READONLY; + break; + + case THEME_CONTROL_TYPE_STATIC: + wzWindowClass = WC_STATICW; + dwWindowBits |= SS_ETCHEDHORZ; + break; + + case THEME_CONTROL_TYPE_TAB: + wzWindowClass = WC_TABCONTROLW; + break; + + case THEME_CONTROL_TYPE_TREEVIEW: + wzWindowClass = WC_TREEVIEWW; + break; + } + ThmExitOnNull(wzWindowClass, hr, E_INVALIDDATA, "Failed to configure control %u because of unknown type: %u", i, pControl->type); + + // Default control ids to the theme id and its index in the control array, unless there + // is a specific id to assign to a named control. + WORD wControlId = MAKEWORD(i, pTheme->wId); + for (DWORD iAssignControl = 0; pControl->sczName && iAssignControl < cAssignControlIds; ++iAssignControl) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pControl->sczName, -1, rgAssignControlIds[iAssignControl].wzName, -1)) + { + wControlId = rgAssignControlIds[iAssignControl].wId; + break; + } + } + + pControl->wId = wControlId; + + GetControlDimensions(pControl, &rcParent, &w, &h, &x, &y); + + BOOL fVisible = pControl->dwStyle & WS_VISIBLE; + BOOL fDisabled = pControl->dwStyle & WS_DISABLED; + + // If the control is supposed to be initially visible and it has a VisibleCondition, check if it's true. + if (fVisible && pControl->sczVisibleCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality) + { + hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition); + + if (!fVisible) + { + pControl->dwStyle &= ~WS_VISIBLE; + } + } + + // Disable controls that aren't visible so their shortcut keys don't trigger. + if (!fVisible) + { + dwWindowBits |= WS_DISABLED; + fDisabled = TRUE; + } + + // If the control is supposed to be initially enabled and it has an EnableCondition, check if it's true. + if (!fDisabled && pControl->sczEnableCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality) + { + BOOL fEnable = TRUE; + + hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnable, pTheme->pvVariableContext); + ThmExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition); + + fDisabled = !fEnable; + dwWindowBits |= fDisabled ? WS_DISABLED : 0; + } + + // Honor the HideWhenDisabled option. + if ((pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED) && fVisible && fDisabled) + { + fVisible = FALSE; + pControl->dwStyle &= ~WS_VISIBLE; + } + + pControl->hWnd = ::CreateWindowExW(dwWindowExBits, wzWindowClass, pControl->sczText, pControl->dwStyle | dwWindowBits, x, y, w, h, hwndParent, reinterpret_cast(wControlId), NULL, pTheme); + ThmExitOnNullWithLastError(pControl->hWnd, hr, "Failed to create window."); + + if (pControl->sczTooltip) + { + if (!pTheme->hwndTooltip) + { + pTheme->hwndTooltip = ::CreateWindowExW(WS_EX_TOOLWINDOW, TOOLTIPS_CLASSW, NULL, WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON | TTS_NOPREFIX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndParent, NULL, NULL, NULL); + } + + if (pTheme->hwndTooltip) + { + TOOLINFOW toolinfo = {}; + toolinfo.cbSize = sizeof(toolinfo); + toolinfo.hwnd = hwndParent; + toolinfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + toolinfo.uId = reinterpret_cast(pControl->hWnd); + toolinfo.lpszText = pControl->sczTooltip; + ::SendMessageW(pTheme->hwndTooltip, TTM_ADDTOOLW, 0, reinterpret_cast(&toolinfo)); + } + } + + if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type) + { + if (pControl->sczNote) + { + ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast(pControl->sczNote)); + } + + if (pControl->hImage) + { + ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_BITMAP, reinterpret_cast(pControl->hImage)); + } + else if (pControl->hIcon) + { + ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_ICON, reinterpret_cast(pControl->hIcon)); + } + } + else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type) + { + if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE) + { + hr = ::SHAutoComplete(pControl->hWnd, SHACF_FILESYS_ONLY); + } + } + else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) + { + ::SendMessageW(pControl->hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, pControl->dwExtendedStyle); + + hr = SizeListViewColumns(pControl); + ThmExitOnFailure(hr, "Failed to get size of list view columns."); + + for (DWORD j = 0; j < pControl->cColumns; ++j) + { + LVCOLUMNW lvc = { }; + lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; + lvc.cx = pControl->ptcColumns[j].nWidth; + lvc.iSubItem = j; + lvc.pszText = pControl->ptcColumns[j].pszName; + lvc.fmt = LVCFMT_LEFT; + lvc.cchTextMax = 4; + + if (-1 == ::SendMessageW(pControl->hWnd, LVM_INSERTCOLUMNW, (WPARAM) (int) (j), (LPARAM) (const LV_COLUMNW *) (&lvc))) + { + ThmExitWithLastError(hr, "Failed to insert listview column %u into tab control.", j); + } + + // Return value tells us the old image list, we don't care. + if (pControl->rghImageList[0]) + { + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_NORMAL), reinterpret_cast(pControl->rghImageList[0])); + } + else if (pControl->rghImageList[1]) + { + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_SMALL), reinterpret_cast(pControl->rghImageList[1])); + } + else if (pControl->rghImageList[2]) + { + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_STATE), reinterpret_cast(pControl->rghImageList[2])); + } + else if (pControl->rghImageList[3]) + { + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast(LVSIL_GROUPHEADER), reinterpret_cast(pControl->rghImageList[3])); + } + } + } + else if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type) + { + ::SendMessageW(pControl->hWnd, EM_AUTOURLDETECT, static_cast(TRUE), 0); + ::SendMessageW(pControl->hWnd, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_LINK); + } + else if (THEME_CONTROL_TYPE_TAB == pControl->type) + { + ULONG_PTR hbrBackground = 0; + if (THEME_INVALID_ID != pControl->dwFontId) + { + hbrBackground = reinterpret_cast(pTheme->rgFonts[pControl->dwFontId].hBackground); + } + else + { + hbrBackground = ::GetClassLongPtr(pTheme->hwndParent, GCLP_HBRBACKGROUND); + } + ::SetClassLongPtr(pControl->hWnd, GCLP_HBRBACKGROUND, hbrBackground); + + for (DWORD j = 0; j < pControl->cTabs; ++j) + { + TCITEMW tci = { }; + tci.mask = TCIF_TEXT | TCIF_IMAGE; + tci.iImage = -1; + tci.pszText = pControl->pttTabs[j].pszName; + + if (-1 == ::SendMessageW(pControl->hWnd, TCM_INSERTITEMW, (WPARAM) (int) (j), (LPARAM) (const TC_ITEMW *) (&tci))) + { + ThmExitWithLastError(hr, "Failed to insert tab %u into tab control.", j); + } + } + } + + if (pControlFont) + { + hr = EnsureFontInstance(pTheme, pControlFont, &pControlFontInstance); + ThmExitOnFailure(hr, "Failed to get DPI specific font."); + + ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM) pControlFontInstance->hFont, FALSE); + } + + // Initialize the text on all "application" (non-page) controls, best effort only. + if (pTheme->pfnFormatString && !pControl->wPageId && pControl->sczText && *pControl->sczText) + { + HRESULT hrFormat = pTheme->pfnFormatString(pControl->sczText, &sczText, pTheme->pvVariableContext); + if (SUCCEEDED(hrFormat)) + { + ThemeSetTextControl(pTheme, pControl->wId, sczText); + } + } + + if (pControl->cControls) + { + hr = LoadControls(pTheme, pControl, rgAssignControlIds, cAssignControlIds); + ThmExitOnFailure(hr, "Failed to load child controls."); + } + } + +LExit: + ReleaseStr(sczText); + + return hr; +} + +static HRESULT LocalizeControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in const WIX_LOCALIZATION *pWixLoc + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + hr = LocalizeControl(pControl, pWixLoc); + ThmExitOnFailure(hr, "Failed to localize control: %ls", pControl->sczName); + } + +LExit: + return hr; +} + +static HRESULT LocalizeControl( + __in THEME_CONTROL* pControl, + __in const WIX_LOCALIZATION *pWixLoc + ) +{ + HRESULT hr = S_OK; + LOC_CONTROL* pLocControl = NULL; + LPWSTR sczLocStringId = NULL; + + if (pControl->sczText && *pControl->sczText) + { + hr = LocLocalizeString(pWixLoc, &pControl->sczText); + ThmExitOnFailure(hr, "Failed to localize control text."); + } + else if (pControl->sczName) + { + LOC_STRING* plocString = NULL; + + hr = StrAllocFormatted(&sczLocStringId, L"#(loc.%ls)", pControl->sczName); + ThmExitOnFailure(hr, "Failed to format loc string id: %ls", pControl->sczName); + + hr = LocGetString(pWixLoc, sczLocStringId, &plocString); + if (E_NOTFOUND != hr) + { + ThmExitOnFailure(hr, "Failed to get loc string: %ls", pControl->sczName); + + hr = StrAllocString(&pControl->sczText, plocString->wzText, 0); + ThmExitOnFailure(hr, "Failed to copy loc string to control: %ls", plocString->wzText); + } + } + + if (pControl->sczTooltip && *pControl->sczTooltip) + { + hr = LocLocalizeString(pWixLoc, &pControl->sczTooltip); + ThmExitOnFailure(hr, "Failed to localize control tooltip text."); + } + + if (pControl->sczNote && *pControl->sczNote) + { + hr = LocLocalizeString(pWixLoc, &pControl->sczNote); + ThmExitOnFailure(hr, "Failed to localize control note text."); + } + + for (DWORD j = 0; j < pControl->cConditionalText; ++j) + { + hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalText[j].sczText); + ThmExitOnFailure(hr, "Failed to localize conditional text."); + } + + for (DWORD j = 0; j < pControl->cConditionalNotes; ++j) + { + hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalNotes[j].sczText); + ThmExitOnFailure(hr, "Failed to localize conditional note."); + } + + for (DWORD j = 0; j < pControl->cColumns; ++j) + { + hr = LocLocalizeString(pWixLoc, &pControl->ptcColumns[j].pszName); + ThmExitOnFailure(hr, "Failed to localize column text."); + } + + for (DWORD j = 0; j < pControl->cTabs; ++j) + { + hr = LocLocalizeString(pWixLoc, &pControl->pttTabs[j].pszName); + ThmExitOnFailure(hr, "Failed to localize tab text."); + } + + // Localize control's size, location, and text. + if (pControl->sczName) + { + hr = LocGetControl(pWixLoc, pControl->sczName, &pLocControl); + if (E_NOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + ThmExitOnFailure(hr, "Failed to localize control."); + + if (LOC_CONTROL_NOT_SET != pLocControl->nX) + { + pControl->nDefaultDpiX = pLocControl->nX; + } + + if (LOC_CONTROL_NOT_SET != pLocControl->nY) + { + pControl->nDefaultDpiY = pLocControl->nY; + } + + if (LOC_CONTROL_NOT_SET != pLocControl->nWidth) + { + pControl->nDefaultDpiWidth = pLocControl->nWidth; + } + + if (LOC_CONTROL_NOT_SET != pLocControl->nHeight) + { + pControl->nDefaultDpiHeight = pLocControl->nHeight; + } + + if (pLocControl->wzText && *pLocControl->wzText) + { + hr = StrAllocString(&pControl->sczText, pLocControl->wzText, 0); + ThmExitOnFailure(hr, "Failed to localize control text."); + } + } + + hr = LocalizeControls(pControl->cControls, pControl->rgControls, pWixLoc); + +LExit: + ReleaseStr(sczLocStringId); + + return hr; +} + +static HRESULT LoadControlsString( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in HMODULE hResModule + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + hr = LoadControlString(pControl, hResModule); + ThmExitOnFailure(hr, "Failed to load string for control: %ls", pControl->sczName); + } + +LExit: + return hr; +} + +static HRESULT LoadControlString( + __in THEME_CONTROL* pControl, + __in HMODULE hResModule + ) +{ + HRESULT hr = S_OK; + if (UINT_MAX != pControl->uStringId) + { + hr = ResReadString(hResModule, pControl->uStringId, &pControl->sczText); + ThmExitOnFailure(hr, "Failed to load control text."); + + for (DWORD j = 0; j < pControl->cColumns; ++j) + { + if (UINT_MAX != pControl->ptcColumns[j].uStringId) + { + hr = ResReadString(hResModule, pControl->ptcColumns[j].uStringId, &pControl->ptcColumns[j].pszName); + ThmExitOnFailure(hr, "Failed to load column text."); + } + } + + for (DWORD j = 0; j < pControl->cTabs; ++j) + { + if (UINT_MAX != pControl->pttTabs[j].uStringId) + { + hr = ResReadString(hResModule, pControl->pttTabs[j].uStringId, &pControl->pttTabs[j].pszName); + ThmExitOnFailure(hr, "Failed to load tab text."); + } + } + } + + hr = LoadControlsString(pControl->cControls, pControl->rgControls, hResModule); + +LExit: + return hr; +} + +static void ResizeControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in const RECT* prcParent + ) +{ + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + ResizeControl(pControl, prcParent); + } +} + +static void ResizeControl( + __in THEME_CONTROL* pControl, + __in const RECT* prcParent + ) +{ + int w = 0; + int h = 0; + int x = 0; + int y = 0; + RECT rcControl = { }; + + GetControlDimensions(pControl, prcParent, &w, &h, &x, &y); + ::SetWindowPos(pControl->hWnd, NULL, x, y, w, h, SWP_NOACTIVATE | SWP_NOZORDER); + +#ifdef DEBUG + if (THEME_CONTROL_TYPE_BUTTON == pControl->type) + { + Trace(REPORT_STANDARD, "Resizing button (%ls/%ls) to (%d,%d)+(%d,%d) for parent (%d,%d)-(%d,%d)", + pControl->sczName, pControl->sczText, x, y, w, h, prcParent->left, prcParent->top, prcParent->right, prcParent->bottom); + } +#endif + + if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) + { + SizeListViewColumns(pControl); + + for (DWORD j = 0; j < pControl->cColumns; ++j) + { + if (-1 == ::SendMessageW(pControl->hWnd, LVM_SETCOLUMNWIDTH, (WPARAM) (int) (j), (LPARAM) (pControl->ptcColumns[j].nWidth))) + { + Trace(REPORT_DEBUG, "Failed to resize listview column %u with error %u", j, ::GetLastError()); + return; + } + } + } + + if (pControl->cControls) + { + ::GetClientRect(pControl->hWnd, &rcControl); + ResizeControls(pControl->cControls, pControl->rgControls, &rcControl); + } +} + +static void ScaleThemeFromWindow( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y + ) +{ + DWORD dwStyle = GetWindowStyle(pTheme->hwndParent); + BOOL fMenu = NULL != ::GetMenu(pTheme->hwndParent); + DWORD dwExStyle = GetWindowExStyle(pTheme->hwndParent); + + ScaleTheme(pTheme, nDpi, x, y, dwStyle, fMenu, dwExStyle); +} + +static void ScaleTheme( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y, + __in DWORD dwStyle, + __in BOOL fMenu, + __in DWORD dwExStyle + ) +{ + RECT rect = { }; + + pTheme->nDpi = nDpi; + + pTheme->nHeight = DpiuScaleValue(pTheme->nDefaultDpiHeight, pTheme->nDpi); + pTheme->nWidth = DpiuScaleValue(pTheme->nDefaultDpiWidth, pTheme->nDpi); + pTheme->nMinimumHeight = DpiuScaleValue(pTheme->nDefaultDpiMinimumHeight, pTheme->nDpi); + pTheme->nMinimumWidth = DpiuScaleValue(pTheme->nDefaultDpiMinimumWidth, pTheme->nDpi); + + rect.left = x; + rect.top = y; + rect.right = x + pTheme->nWidth; + rect.bottom = y + pTheme->nHeight; + DpiuAdjustWindowRect(&rect, dwStyle, fMenu, dwExStyle, pTheme->nDpi); + pTheme->nWindowWidth = rect.right - rect.left; + pTheme->nWindowHeight = rect.bottom - rect.top; + + ScaleControls(pTheme, pTheme->cControls, pTheme->rgControls, pTheme->nDpi); + + if (pTheme->hwndParent) + { + ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWindowWidth, pTheme->nWindowHeight, SWP_NOACTIVATE | SWP_NOZORDER); + } +} + +static void ScaleControls( + __in THEME* pTheme, + __in DWORD cControls, + __in THEME_CONTROL* rgControls, + __in UINT nDpi + ) +{ + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + ScaleControl(pTheme, pControl, nDpi); + } +} + +static void ScaleControl( + __in THEME* pTheme, + __in THEME_CONTROL* pControl, + __in UINT nDpi + ) +{ + HRESULT hr = S_OK; + THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL; + THEME_FONT_INSTANCE* pControlFontInstance = NULL; + + if (pControlFont) + { + hr = EnsureFontInstance(pTheme, pControlFont, &pControlFontInstance); + if (SUCCEEDED(hr) && pControl->hWnd) + { + ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM)pControlFontInstance->hFont, FALSE); + } + } + + if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type) + { + for (DWORD i = 0; i < pControl->cColumns; ++i) + { + THEME_COLUMN* pColumn = pControl->ptcColumns + i; + + pColumn->nBaseWidth = DpiuScaleValue(pColumn->nDefaultDpiBaseWidth, nDpi); + } + } + + pControl->nWidth = DpiuScaleValue(pControl->nDefaultDpiWidth, nDpi); + pControl->nHeight = DpiuScaleValue(pControl->nDefaultDpiHeight, nDpi); + pControl->nX = DpiuScaleValue(pControl->nDefaultDpiX, nDpi); + pControl->nY = DpiuScaleValue(pControl->nDefaultDpiY, nDpi); + + if (pControl->cControls) + { + ScaleControls(pTheme, pControl->cControls, pControl->rgControls, nDpi); + } +} + +static void UnloadControls( + __in DWORD cControls, + __in THEME_CONTROL* rgControls + ) +{ + for (DWORD i = 0; i < cControls; ++i) + { + THEME_CONTROL* pControl = rgControls + i; + pControl->hWnd = NULL; + + UnloadControls(pControl->cControls, pControl->rgControls); + } +} + diff --git a/src/libs/dutil/WixToolset.DUtil/timeutil.cpp b/src/libs/dutil/WixToolset.DUtil/timeutil.cpp new file mode 100644 index 00000000..b7953c94 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/timeutil.cpp @@ -0,0 +1,385 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define TimeExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__) +#define TimeExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_TIMEUTIL, p, x, e, s, __VA_ARGS__) +#define TimeExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, p, x, s, __VA_ARGS__) +#define TimeExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, p, x, e, s, __VA_ARGS__) +#define TimeExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, p, x, s, __VA_ARGS__) +#define TimeExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_TIMEUTIL, e, x, s, __VA_ARGS__) +#define TimeExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_TIMEUTIL, g, x, s, __VA_ARGS__) + +const LPCWSTR DAY_OF_WEEK[] = { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" }; +const LPCWSTR MONTH_OF_YEAR[] = { L"None", L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec" }; +enum TIME_PARSER { DayOfWeek, DayOfMonth, MonthOfYear, Year, Hours, Minutes, Seconds, TimeZone }; +enum TIME_PARSERRFC3339 { RFC3339_Year, RFC3339_Month, RFC3339_Day, RFC3339_Hours, RFC3339_Minutes, RFC3339_Seconds, RFC3339_TimeZone }; + +// prototypes +static HRESULT DayFromString( + __in_z LPCWSTR wzDay, + __out WORD* pwDayOfWeek + ); +static HRESULT MonthFromString( + __in_z LPCWSTR wzMonth, + __out WORD* pwMonthOfYear + ); + + +/******************************************************************** + TimeFromString - converts string to FILETIME + +*******************************************************************/ +extern "C" HRESULT DAPI TimeFromString( + __in_z LPCWSTR wzTime, + __out FILETIME* pFileTime + ) +{ + Assert(wzTime && pFileTime); + + HRESULT hr = S_OK; + LPWSTR pwzTime = NULL; + + SYSTEMTIME sysTime = { }; + TIME_PARSER timeParser = DayOfWeek; + + LPCWSTR pwzStart = NULL; + LPWSTR pwzEnd = NULL; + + hr = StrAllocString(&pwzTime, wzTime, 0); + TimeExitOnFailure(hr, "Failed to copy time."); + + pwzStart = pwzEnd = pwzTime; + while (pwzEnd && *pwzEnd) + { + if (L',' == *pwzEnd || L' ' == *pwzEnd || L':' == *pwzEnd) + { + *pwzEnd = L'\0'; // null terminate + ++pwzEnd; + + while (L' ' == *pwzEnd) + { + ++pwzEnd; // and skip past the blank space + } + + switch (timeParser) + { + case DayOfWeek: + hr = DayFromString(pwzStart, &sysTime.wDayOfWeek); + TimeExitOnFailure(hr, "Failed to convert string to day: %ls", pwzStart); + break; + + case DayOfMonth: + sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case MonthOfYear: + hr = MonthFromString(pwzStart, &sysTime.wMonth); + TimeExitOnFailure(hr, "Failed to convert to month: %ls", pwzStart); + break; + + case Year: + sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case Hours: + sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case Minutes: + sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case Seconds: + sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case TimeZone: + // TODO: do something with this in the future, but this should only hit outside of the while loop. + break; + + default: + break; + } + + pwzStart = pwzEnd; + timeParser = (TIME_PARSER)((int)timeParser + 1); + } + + ++pwzEnd; + } + + + if (!::SystemTimeToFileTime(&sysTime, pFileTime)) + { + TimeExitWithLastError(hr, "Failed to convert system time to file time."); + } + +LExit: + ReleaseStr(pwzTime); + + return hr; +} + +/******************************************************************** + TimeFromString3339 - converts string formated in accorance with RFC3339 to FILETIME + http://tools.ietf.org/html/rfc3339 +*******************************************************************/ +extern "C" HRESULT DAPI TimeFromString3339( + __in_z LPCWSTR wzTime, + __out FILETIME* pFileTime + ) +{ + Assert(wzTime && pFileTime); + + HRESULT hr = S_OK; + LPWSTR pwzTime = NULL; + + SYSTEMTIME sysTime = { }; + TIME_PARSERRFC3339 timeParser = RFC3339_Year; + + LPCWSTR pwzStart = NULL; + LPWSTR pwzEnd = NULL; + + hr = StrAllocString(&pwzTime, wzTime, 0); + TimeExitOnFailure(hr, "Failed to copy time."); + + pwzStart = pwzEnd = pwzTime; + while (pwzEnd && *pwzEnd) + { + if (L'T' == *pwzEnd || L':' == *pwzEnd || L'-' == *pwzEnd) + { + *pwzEnd = L'\0'; // null terminate + ++pwzEnd; + + switch (timeParser) + { + case RFC3339_Year: + sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Month: + sysTime.wMonth = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Day: + sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Hours: + sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Minutes: + sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_Seconds: + sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10); + break; + + case RFC3339_TimeZone: + // TODO: do something with this in the future, but this should only hit outside of the while loop. + break; + + default: + break; + } + + pwzStart = pwzEnd; + timeParser = (TIME_PARSERRFC3339)((int)timeParser + 1); + } + + ++pwzEnd; + } + + + if (!::SystemTimeToFileTime(&sysTime, pFileTime)) + { + TimeExitWithLastError(hr, "Failed to convert system time to file time."); + } + +LExit: + ReleaseStr(pwzTime); + + return hr; +} +/**************************************************************************** +TimeCurrentTime - gets the current time in string format + +****************************************************************************/ +extern "C" HRESULT DAPI TimeCurrentTime( + __deref_out_z LPWSTR* ppwz, + __in BOOL fGMT + ) +{ + SYSTEMTIME st; + + if (fGMT) + { + ::GetSystemTime(&st); + } + else + { + SYSTEMTIME stGMT; + TIME_ZONE_INFORMATION tzi; + + ::GetTimeZoneInformation(&tzi); + ::GetSystemTime(&stGMT); + ::SystemTimeToTzSpecificLocalTime(&tzi, &stGMT, &st); + } + + return StrAllocFormatted(ppwz, L"%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond); +} + + +/**************************************************************************** +TimeCurrentDateTime - gets the current date and time in string format, + per format described in RFC 3339 +****************************************************************************/ +extern "C" HRESULT DAPI TimeCurrentDateTime( + __deref_out_z LPWSTR* ppwz, + __in BOOL fGMT + ) +{ + SYSTEMTIME st; + + ::GetSystemTime(&st); + + return TimeSystemDateTime(ppwz, &st, fGMT); +} + + +/**************************************************************************** +TimeSystemDateTime - converts the provided system time struct to string format, + per format described in RFC 3339 +****************************************************************************/ +extern "C" HRESULT DAPI TimeSystemDateTime( + __deref_out_z LPWSTR* ppwz, + __in const SYSTEMTIME *pst, + __in BOOL fGMT + ) +{ + DWORD dwAbsBias = 0; + + if (fGMT) + { + return StrAllocFormatted(ppwz, L"%04hu-%02hu-%02huT%02hu:%02hu:%02huZ", pst->wYear, pst->wMonth, pst->wDay, pst->wHour, pst->wMinute, pst->wSecond); + } + else + { + SYSTEMTIME st; + TIME_ZONE_INFORMATION tzi; + + ::GetTimeZoneInformation(&tzi); + ::SystemTimeToTzSpecificLocalTime(&tzi, pst, &st); + dwAbsBias = abs(tzi.Bias); + + return StrAllocFormatted(ppwz, L"%04hu-%02hu-%02huT%02hu:%02hu:%02hu%c%02u:%02u", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, 0 >= tzi.Bias ? L'+' : L'-', dwAbsBias / 60, dwAbsBias % 60); + } +} + + +/**************************************************************************** +TimeSystemToDateTimeString - converts the provided system time struct to + string format representing date and time for the specified locale +****************************************************************************/ +HRESULT DAPI TimeSystemToDateTimeString( + __deref_out_z LPWSTR* ppwz, + __in const SYSTEMTIME* pst, + __in LCID locale + ) +{ + HRESULT hr = S_OK; + const WCHAR * DATE_FORMAT = L"MMM dd',' yyyy',' "; + const WCHAR * TIME_FORMAT = L"hh':'mm':'ss tt"; + int iLenDate = 0; + int iLenTime = 0; + + iLenDate = ::GetDateFormatW(locale, 0, pst, DATE_FORMAT, NULL, 0); + if (0 >= iLenDate) + { + TimeExitWithLastError(hr, "Failed to get date format with NULL"); + } + + iLenTime = ::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, NULL, 0); + if (0 >= iLenTime) + { + TimeExitWithLastError(hr, "Failed to get time format with NULL"); + } + + // Between both lengths we account for 2 null terminators, and only need one, so we subtract one + hr = StrAlloc(ppwz, iLenDate + iLenTime - 1); + TimeExitOnFailure(hr, "Failed to allocate string"); + + if (!::GetDateFormatW(locale, 0, pst, DATE_FORMAT, *ppwz, iLenDate)) + { + TimeExitWithLastError(hr, "Failed to get date format with buffer"); + } + // Space to separate them + (*ppwz)[iLenDate - 1] = ' '; + + if (!::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, (*ppwz) + iLenDate - 1, iLenTime)) + { + TimeExitWithLastError(hr, "Failed to get time format with buffer"); + } + +LExit: + return hr; +} + +/******************************************************************** + DayFromString - converts string to day + +*******************************************************************/ +static HRESULT DayFromString( + __in_z LPCWSTR wzDay, + __out WORD* pwDayOfWeek + ) +{ + HRESULT hr = E_INVALIDARG; // assume we won't find a matching name + + for (WORD i = 0; i < countof(DAY_OF_WEEK); ++i) + { + if (0 == lstrcmpW(wzDay, DAY_OF_WEEK[i])) + { + *pwDayOfWeek = i; + hr = S_OK; + break; + } + } + + return hr; +} + + +/******************************************************************** + MonthFromString - converts string to month + +*******************************************************************/ +static HRESULT MonthFromString( + __in_z LPCWSTR wzMonth, + __out WORD* pwMonthOfYear + ) +{ + HRESULT hr = E_INVALIDARG; // assume we won't find a matching name + + for (WORD i = 0; i < countof(MONTH_OF_YEAR); ++i) + { + if (0 == lstrcmpW(wzMonth, MONTH_OF_YEAR[i])) + { + *pwMonthOfYear = i; + hr = S_OK; + break; + } + } + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/uncutil.cpp b/src/libs/dutil/WixToolset.DUtil/uncutil.cpp new file mode 100644 index 00000000..415ea198 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/uncutil.cpp @@ -0,0 +1,69 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define UncExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__) +#define UncExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_UNCUTIL, p, x, e, s, __VA_ARGS__) +#define UncExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, p, x, s, __VA_ARGS__) +#define UncExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_UNCUTIL, p, x, e, s, __VA_ARGS__) +#define UncExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, p, x, s, __VA_ARGS__) +#define UncExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_UNCUTIL, e, x, s, __VA_ARGS__) +#define UncExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_UNCUTIL, g, x, s, __VA_ARGS__) + +DAPI_(HRESULT) UncConvertFromMountedDrive( + __inout LPWSTR *psczUNCPath, + __in LPCWSTR sczMountedDrivePath + ) +{ + HRESULT hr = S_OK; + DWORD dwLength = 0; + DWORD er = ERROR_SUCCESS; + LPWSTR sczDrive = NULL; + + // Only copy drive letter and colon + hr = StrAllocString(&sczDrive, sczMountedDrivePath, 2); + UncExitOnFailure(hr, "Failed to copy drive"); + + // ERROR_NOT_CONNECTED means it's not a mapped drive + er = ::WNetGetConnectionW(sczDrive, NULL, &dwLength); + if (ERROR_MORE_DATA == er) + { + er = ERROR_SUCCESS; + + hr = StrAlloc(psczUNCPath, dwLength); + UncExitOnFailure(hr, "Failed to allocate string to get raw UNC path of length %u", dwLength); + + er = ::WNetGetConnectionW(sczDrive, *psczUNCPath, &dwLength); + if (ERROR_CONNECTION_UNAVAIL == er) + { + // This means the drive is remembered but not currently connected, this can mean the location is accessible via UNC path but not via mounted drive path + er = ERROR_SUCCESS; + } + UncExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed with buffer provided on drive %ls", sczDrive); + + // Skip drive letter and colon + hr = StrAllocConcat(psczUNCPath, sczMountedDrivePath + 2, 0); + UncExitOnFailure(hr, "Failed to copy rest of database path"); + } + else + { + if (ERROR_SUCCESS == er) + { + er = ERROR_NO_DATA; + } + + UncExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed on drive %ls", sczDrive); + } + +LExit: + ReleaseStr(sczDrive); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/uriutil.cpp b/src/libs/dutil/WixToolset.DUtil/uriutil.cpp new file mode 100644 index 00000000..7913c22e --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/uriutil.cpp @@ -0,0 +1,553 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define UriExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__) +#define UriExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__) +#define UriExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__) +#define UriExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__) +#define UriExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__) +#define UriExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_URIUTIL, e, x, s, __VA_ARGS__) +#define UriExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_URIUTIL, g, x, s, __VA_ARGS__) + + +// +// UriCanonicalize - canonicalizes a URI. +// +extern "C" HRESULT DAPI UriCanonicalize( + __inout_z LPWSTR* psczUri + ) +{ + HRESULT hr = S_OK; + WCHAR wz[INTERNET_MAX_URL_LENGTH] = { }; + DWORD cch = countof(wz); + + if (!::InternetCanonicalizeUrlW(*psczUri, wz, &cch, ICU_DECODE)) + { + UriExitWithLastError(hr, "Failed to canonicalize URI."); + } + + hr = StrAllocString(psczUri, wz, cch); + UriExitOnFailure(hr, "Failed copy canonicalized URI."); + +LExit: + return hr; +} + + +// +// UriCrack - cracks a URI into constituent parts. +// +extern "C" HRESULT DAPI UriCrack( + __in_z LPCWSTR wzUri, + __out_opt INTERNET_SCHEME* pScheme, + __deref_opt_out_z LPWSTR* psczHostName, + __out_opt INTERNET_PORT* pPort, + __deref_opt_out_z LPWSTR* psczUser, + __deref_opt_out_z LPWSTR* psczPassword, + __deref_opt_out_z LPWSTR* psczPath, + __deref_opt_out_z LPWSTR* psczQueryString + ) +{ + HRESULT hr = S_OK; + URL_COMPONENTSW components = { }; + WCHAR wzHostName[INTERNET_MAX_HOST_NAME_LENGTH + 1]; + WCHAR wzUserName[INTERNET_MAX_USER_NAME_LENGTH + 1]; + WCHAR wzPassword[INTERNET_MAX_PASSWORD_LENGTH + 1]; + WCHAR wzPath[INTERNET_MAX_PATH_LENGTH + 1]; + WCHAR wzQueryString[INTERNET_MAX_PATH_LENGTH + 1]; + + components.dwStructSize = sizeof(URL_COMPONENTSW); + + if (psczHostName) + { + components.lpszHostName = wzHostName; + components.dwHostNameLength = countof(wzHostName); + } + + if (psczUser) + { + components.lpszUserName = wzUserName; + components.dwUserNameLength = countof(wzUserName); + } + + if (psczPassword) + { + components.lpszPassword = wzPassword; + components.dwPasswordLength = countof(wzPassword); + } + + if (psczPath) + { + components.lpszUrlPath = wzPath; + components.dwUrlPathLength = countof(wzPath); + } + + if (psczQueryString) + { + components.lpszExtraInfo = wzQueryString; + components.dwExtraInfoLength = countof(wzQueryString); + } + + if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &components)) + { + UriExitWithLastError(hr, "Failed to crack URI."); + } + + if (pScheme) + { + *pScheme = components.nScheme; + } + + if (psczHostName) + { + hr = StrAllocString(psczHostName, components.lpszHostName, components.dwHostNameLength); + UriExitOnFailure(hr, "Failed to copy host name."); + } + + if (pPort) + { + *pPort = components.nPort; + } + + if (psczUser) + { + hr = StrAllocString(psczUser, components.lpszUserName, components.dwUserNameLength); + UriExitOnFailure(hr, "Failed to copy user name."); + } + + if (psczPassword) + { + hr = StrAllocString(psczPassword, components.lpszPassword, components.dwPasswordLength); + UriExitOnFailure(hr, "Failed to copy password."); + } + + if (psczPath) + { + hr = StrAllocString(psczPath, components.lpszUrlPath, components.dwUrlPathLength); + UriExitOnFailure(hr, "Failed to copy path."); + } + + if (psczQueryString) + { + hr = StrAllocString(psczQueryString, components.lpszExtraInfo, components.dwExtraInfoLength); + UriExitOnFailure(hr, "Failed to copy query string."); + } + +LExit: + return hr; +} + + +// +// UriCrackEx - cracks a URI into URI_INFO. +// +extern "C" HRESULT DAPI UriCrackEx( + __in_z LPCWSTR wzUri, + __in URI_INFO* pUriInfo + ) +{ + HRESULT hr = S_OK; + + hr = UriCrack(wzUri, &pUriInfo->scheme, &pUriInfo->sczHostName, &pUriInfo->port, &pUriInfo->sczUser, &pUriInfo->sczPassword, &pUriInfo->sczPath, &pUriInfo->sczQueryString); + UriExitOnFailure(hr, "Failed to crack URI."); + +LExit: + return hr; +} + + +// +// UriInfoUninitialize - frees the memory in a URI_INFO struct. +// +extern "C" void DAPI UriInfoUninitialize( + __in URI_INFO* pUriInfo + ) +{ + ReleaseStr(pUriInfo->sczHostName); + ReleaseStr(pUriInfo->sczUser); + ReleaseStr(pUriInfo->sczPassword); + ReleaseStr(pUriInfo->sczPath); + ReleaseStr(pUriInfo->sczQueryString); + memset(pUriInfo, 0, sizeof(URI_INFO)); +} + + +// +// UriCreate - creates a URI from constituent parts. +// +extern "C" HRESULT DAPI UriCreate( + __inout_z LPWSTR* psczUri, + __in INTERNET_SCHEME scheme, + __in_z_opt LPWSTR wzHostName, + __in INTERNET_PORT port, + __in_z_opt LPWSTR wzUser, + __in_z_opt LPWSTR wzPassword, + __in_z_opt LPWSTR wzPath, + __in_z_opt LPWSTR wzQueryString + ) +{ + HRESULT hr = S_OK; + WCHAR wz[INTERNET_MAX_URL_LENGTH] = { }; + DWORD cch = countof(wz); + URL_COMPONENTSW components = { }; + + components.dwStructSize = sizeof(URL_COMPONENTSW); + components.nScheme = scheme; + components.lpszHostName = wzHostName; + components.nPort = port; + components.lpszUserName = wzUser; + components.lpszPassword = wzPassword; + components.lpszUrlPath = wzPath; + components.lpszExtraInfo = wzQueryString; + + if (!::InternetCreateUrlW(&components, ICU_ESCAPE, wz, &cch)) + { + UriExitWithLastError(hr, "Failed to create URI."); + } + + hr = StrAllocString(psczUri, wz, cch); + UriExitOnFailure(hr, "Failed copy created URI."); + +LExit: + return hr; +} + + +// +// UriGetServerAndResource - gets the server and resource as independent strings from a URI. +// +// NOTE: This function is useful for the InternetConnect/HttpRequest APIs. +// +extern "C" HRESULT DAPI UriGetServerAndResource( + __in_z LPCWSTR wzUri, + __out_z LPWSTR* psczServer, + __out_z LPWSTR* psczResource + ) +{ + HRESULT hr = S_OK; + INTERNET_SCHEME scheme = INTERNET_SCHEME_UNKNOWN; + LPWSTR sczHostName = NULL; + INTERNET_PORT port = INTERNET_INVALID_PORT_NUMBER; + LPWSTR sczUser = NULL; + LPWSTR sczPassword = NULL; + LPWSTR sczPath = NULL; + LPWSTR sczQueryString = NULL; + + hr = UriCrack(wzUri, &scheme, &sczHostName, &port, &sczUser, &sczPassword, &sczPath, &sczQueryString); + UriExitOnFailure(hr, "Failed to crack URI."); + + hr = UriCreate(psczServer, scheme, sczHostName, port, sczUser, sczPassword, NULL, NULL); + UriExitOnFailure(hr, "Failed to allocate server URI."); + + hr = UriCreate(psczResource, INTERNET_SCHEME_UNKNOWN, NULL, INTERNET_INVALID_PORT_NUMBER, NULL, NULL, sczPath, sczQueryString); + UriExitOnFailure(hr, "Failed to allocate resource URI."); + +LExit: + ReleaseStr(sczQueryString); + ReleaseStr(sczPath); + ReleaseStr(sczPassword); + ReleaseStr(sczUser); + ReleaseStr(sczHostName); + + return hr; +} + + +// +// UriFile - returns the file part of the URI. +// +extern "C" HRESULT DAPI UriFile( + __deref_out_z LPWSTR* psczFile, + __in_z LPCWSTR wzUri + ) +{ + HRESULT hr = S_OK; + WCHAR wz[MAX_PATH + 1]; + DWORD cch = countof(wz); + URL_COMPONENTSW uc = { }; + + uc.dwStructSize = sizeof(uc); + uc.lpszUrlPath = wz; + uc.dwUrlPathLength = cch; + + if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &uc)) + { + UriExitWithLastError(hr, "Failed to crack URI."); + } + + // Copy only the file name. Fortunately, PathFile() understands that + // forward slashes can be directory separators like backslashes. + hr = StrAllocString(psczFile, PathFile(wz), 0); + UriExitOnFailure(hr, "Failed to copy file name"); + +LExit: + return hr; +} + + +/******************************************************************* + UriProtocol - determines the protocol of a URI. + +********************************************************************/ +extern "C" HRESULT DAPI UriProtocol( + __in_z LPCWSTR wzUri, + __out URI_PROTOCOL* pProtocol + ) +{ + Assert(wzUri && *wzUri); + Assert(pProtocol); + + HRESULT hr = S_OK; + + if ((L'h' == wzUri[0] || L'H' == wzUri[0]) && + (L't' == wzUri[1] || L'T' == wzUri[1]) && + (L't' == wzUri[2] || L'T' == wzUri[2]) && + (L'p' == wzUri[3] || L'P' == wzUri[3]) && + (L's' == wzUri[4] || L'S' == wzUri[4]) && + L':' == wzUri[5] && + L'/' == wzUri[6] && + L'/' == wzUri[7]) + { + *pProtocol = URI_PROTOCOL_HTTPS; + } + else if ((L'h' == wzUri[0] || L'H' == wzUri[0]) && + (L't' == wzUri[1] || L'T' == wzUri[1]) && + (L't' == wzUri[2] || L'T' == wzUri[2]) && + (L'p' == wzUri[3] || L'P' == wzUri[3]) && + L':' == wzUri[4] && + L'/' == wzUri[5] && + L'/' == wzUri[6]) + { + *pProtocol = URI_PROTOCOL_HTTP; + } + else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) && + (L't' == wzUri[1] || L'T' == wzUri[1]) && + (L'p' == wzUri[2] || L'P' == wzUri[2]) && + L':' == wzUri[3] && + L'/' == wzUri[4] && + L'/' == wzUri[5]) + { + *pProtocol = URI_PROTOCOL_FTP; + } + else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) && + (L'i' == wzUri[1] || L'I' == wzUri[1]) && + (L'l' == wzUri[2] || L'L' == wzUri[2]) && + (L'e' == wzUri[3] || L'E' == wzUri[3]) && + L':' == wzUri[4] && + L'/' == wzUri[5] && + L'/' == wzUri[6]) + { + *pProtocol = URI_PROTOCOL_FILE; + } + else + { + *pProtocol = URI_PROTOCOL_UNKNOWN; + } + + return hr; +} + + +/******************************************************************* + UriRoot - returns the root of the path specified in the URI. + + examples: + file:///C:\path\path -> C:\ + file://server/share/path/path -> \\server\share + http://www.example.com/path/path -> http://www.example.com/ + ftp://ftp.example.com/path/path -> ftp://www.example.com/ + + NOTE: This function should only be used on cannonicalized URIs. + It does not cannonicalize itself. +********************************************************************/ +extern "C" HRESULT DAPI UriRoot( + __in_z LPCWSTR wzUri, + __out LPWSTR* ppwzRoot, + __out_opt URI_PROTOCOL* pProtocol + ) +{ + Assert(wzUri && *wzUri); + Assert(ppwzRoot); + + HRESULT hr = S_OK; + URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; + LPCWSTR pwcSlash = NULL; + + hr = UriProtocol(wzUri, &protocol); + UriExitOnFailure(hr, "Invalid URI."); + + switch (protocol) + { + case URI_PROTOCOL_FILE: + if (L'/' == wzUri[7]) // file path + { + if (((L'a' <= wzUri[8] && L'z' >= wzUri[8]) || (L'A' <= wzUri[8] && L'Z' >= wzUri[8])) && L':' == wzUri[9]) + { + hr = StrAlloc(ppwzRoot, 4); + UriExitOnFailure(hr, "Failed to allocate string for root of URI."); + *ppwzRoot[0] = wzUri[8]; + *ppwzRoot[1] = L':'; + *ppwzRoot[2] = L'\\'; + *ppwzRoot[3] = L'\0'; + } + else + { + hr = E_INVALIDARG; + UriExitOnFailure(hr, "Invalid file path in URI."); + } + } + else // UNC share + { + pwcSlash = wcschr(wzUri + 8, L'/'); + if (!pwcSlash) + { + hr = E_INVALIDARG; + UriExitOnFailure(hr, "Invalid server name in URI."); + } + else + { + hr = StrAllocString(ppwzRoot, L"\\\\", 64); + UriExitOnFailure(hr, "Failed to allocate string for root of URI."); + + pwcSlash = wcschr(pwcSlash + 1, L'/'); + if (pwcSlash) + { + hr = StrAllocConcat(ppwzRoot, wzUri + 8, pwcSlash - wzUri - 8); + UriExitOnFailure(hr, "Failed to add server/share to root of URI."); + } + else + { + hr = StrAllocConcat(ppwzRoot, wzUri + 8, 0); + UriExitOnFailure(hr, "Failed to add server/share to root of URI."); + } + + // replace all slashes with backslashes to be truly UNC. + for (LPWSTR pwc = *ppwzRoot; pwc && *pwc; ++pwc) + { + if (L'/' == *pwc) + { + *pwc = L'\\'; + } + } + } + } + break; + + case URI_PROTOCOL_FTP: + pwcSlash = wcschr(wzUri + 6, L'/'); + if (pwcSlash) + { + hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); + UriExitOnFailure(hr, "Failed allocate root from URI."); + } + else + { + hr = StrAllocString(ppwzRoot, wzUri, 0); + UriExitOnFailure(hr, "Failed allocate root from URI."); + } + break; + + case URI_PROTOCOL_HTTP: + pwcSlash = wcschr(wzUri + 7, L'/'); + if (pwcSlash) + { + hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri); + UriExitOnFailure(hr, "Failed allocate root from URI."); + } + else + { + hr = StrAllocString(ppwzRoot, wzUri, 0); + UriExitOnFailure(hr, "Failed allocate root from URI."); + } + break; + + default: + hr = E_INVALIDARG; + UriExitOnFailure(hr, "Unknown URI protocol."); + } + + if (pProtocol) + { + *pProtocol = protocol; + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI UriResolve( + __in_z LPCWSTR wzUri, + __in_opt LPCWSTR wzBaseUri, + __out LPWSTR* ppwzResolvedUri, + __out_opt URI_PROTOCOL* pResolvedProtocol + ) +{ + UNREFERENCED_PARAMETER(wzUri); + UNREFERENCED_PARAMETER(wzBaseUri); + UNREFERENCED_PARAMETER(ppwzResolvedUri); + UNREFERENCED_PARAMETER(pResolvedProtocol); + + HRESULT hr = E_NOTIMPL; +#if 0 + URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; + + hr = UriProtocol(wzUri, &protocol); + UriExitOnFailure(hr, "Failed to determine protocol for URL: %ls", wzUri); + + UriExitOnNull(ppwzResolvedUri, hr, E_INVALIDARG, "Failed to resolve URI, because no method of output was provided"); + + if (URI_PROTOCOL_UNKNOWN == protocol) + { + UriExitOnNull(wzBaseUri, hr, E_INVALIDARG, "Failed to resolve URI - base URI provided was NULL"); + + if (L'/' == *wzUri || L'\\' == *wzUri) + { + hr = UriRoot(wzBaseUri, ppwzResolvedUri, &protocol); + UriExitOnFailure(hr, "Failed to get root from URI: %ls", wzBaseUri); + + hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); + UriExitOnFailure(hr, "Failed to concat file to base URI."); + } + else + { + hr = UriProtocol(wzBaseUri, &protocol); + UriExitOnFailure(hr, "Failed to get protocol of base URI: %ls", wzBaseUri); + + LPCWSTR pwcFile = const_cast (UriFile(wzBaseUri)); + if (!pwcFile) + { + hr = E_INVALIDARG; + UriExitOnFailure(hr, "Failed to get file from base URI: %ls", wzBaseUri); + } + + hr = StrAllocString(ppwzResolvedUri, wzBaseUri, pwcFile - wzBaseUri); + UriExitOnFailure(hr, "Failed to allocate string for resolved URI."); + + hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0); + UriExitOnFailure(hr, "Failed to concat file to resolved URI."); + } + } + else + { + hr = StrAllocString(ppwzResolvedUri, wzUri, 0); + UriExitOnFailure(hr, "Failed to copy resolved URI."); + } + + if (pResolvedProtocol) + { + *pResolvedProtocol = protocol; + } + +LExit: +#endif + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/userutil.cpp b/src/libs/dutil/WixToolset.DUtil/userutil.cpp new file mode 100644 index 00000000..ca6d5480 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/userutil.cpp @@ -0,0 +1,300 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// UserExit macros +#define UserExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__) +#define UserExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__) +#define UserExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__) +#define UserExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__) +#define UserExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__) +#define UserExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_USERUTIL, e, x, s, __VA_ARGS__) +#define UserExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_USERUTIL, g, x, s, __VA_ARGS__) + +static BOOL CheckIsMemberHelper( + __in_z LPCWSTR pwzGroupUserDomain, + __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData, + __in DWORD cguiGroupData + ); + +/******************************************************************* + UserBuildDomainUserName - builds a DOMAIN\USERNAME string + +********************************************************************/ +extern "C" HRESULT DAPI UserBuildDomainUserName( + __out_ecount_z(cchDest) LPWSTR wzDest, + __in int cchDest, + __in_z LPCWSTR pwzName, + __in_z LPCWSTR pwzDomain + ) +{ + HRESULT hr = S_OK; + DWORD cchLeft = cchDest; + WCHAR* pwz = wzDest; + DWORD cchWz = cchDest; + DWORD cch; + + cch = lstrlenW(pwzDomain); + if (cch >= cchLeft) + { + hr = ERROR_MORE_DATA; + UserExitOnFailure(hr, "Buffer size is not big enough to hold domain name: %ls", pwzDomain); + } + else if (cch > 0) + { + // handle the domain case + + hr = ::StringCchCopyNW(pwz, cchWz, pwzDomain, cchLeft - 1); // last parameter does not include '\0' + UserExitOnFailure(hr, "Failed to copy Domain onto string."); + + cchLeft -= cch; + pwz += cch; + cchWz -= cch; + + if (1 >= cchLeft) + { + hr = ERROR_MORE_DATA; + UserExitOnFailure(hr, "Insufficient buffer size while building domain user name"); + } + + hr = ::StringCchCopyNW(pwz, cchWz, L"\\", cchLeft - 1); // last parameter does not include '\0' + UserExitOnFailure(hr, "Failed to copy backslash onto string."); + + --cchLeft; + ++pwz; + --cchWz; + } + + cch = lstrlenW(pwzName); + if (cch >= cchLeft) + { + hr = ERROR_MORE_DATA; + UserExitOnFailure(hr, "Buffer size is not big enough to hold user name: %ls", pwzName); + } + + hr = ::StringCchCopyNW(pwz, cchWz, pwzName, cchLeft - 1); // last parameter does not include '\0' + UserExitOnFailure(hr, "Failed to copy User name onto string."); + +LExit: + return hr; +} + + +/******************************************************************* + Checks whether a user is a member of a group - outputs the result via lpfMember +********************************************************************/ +extern "C" HRESULT DAPI UserCheckIsMember( + __in_z LPCWSTR pwzName, + __in_z LPCWSTR pwzDomain, + __in_z LPCWSTR pwzGroupName, + __in_z LPCWSTR pwzGroupDomain, + __out LPBOOL lpfMember + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + DWORD dwRead = 0; + DWORD dwTotal = 0; + LPCWSTR wz = NULL; + GROUP_USERS_INFO_0 *pguiGroupData = NULL; + WCHAR wzGroupUserDomain[MAX_DARWIN_COLUMN + 1]; // GROUPDOMAIN\GROUPNAME + WCHAR wzUserDomain[MAX_DARWIN_COLUMN + 1]; // USERDOMAIN\USERNAME + BSTR bstrUser = NULL; + BSTR bstrGroup = NULL; + + IADsGroup *pGroup = NULL; + VARIANT_BOOL vtBoolResult = VARIANT_FALSE; + + hr = UserBuildDomainUserName(wzGroupUserDomain, countof(wzGroupUserDomain), pwzGroupName, pwzGroupDomain); + UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); + + hr = UserBuildDomainUserName(wzUserDomain, countof(wzUserDomain), pwzName, pwzDomain); + UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName); + + if (pwzDomain && *pwzDomain) + { + wz = pwzDomain; + } + + er = ::NetUserGetGroups(wz, pwzName, 0, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal); + // Ignore these errors, and just go to the fallback checks + if (ERROR_BAD_NETPATH == er || ERROR_INVALID_NAME == er || NERR_UserNotFound == er) + { + Trace(REPORT_VERBOSE, "failed to get groups for user %ls from domain %ls with error code 0x%x - continuing", pwzName, (wz != NULL) ? wz : L"", HRESULT_FROM_WIN32(er)); + er = ERROR_SUCCESS; + } + UserExitOnWin32Error(er, hr, "Failed to get list of global groups for user while checking group membership information for user: %ls", pwzName); + + if (dwRead != dwTotal) + { + hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); + UserExitOnRootFailure(hr, "Failed to get entire list of groups (global) for user while checking group membership information for user: %ls", pwzName); + } + + if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead)) + { + *lpfMember = TRUE; + ExitFunction1(hr = S_OK); + } + + if (NULL != pguiGroupData) + { + ::NetApiBufferFree(pguiGroupData); + pguiGroupData = NULL; + } + + // If we fail with the global groups, try again with the local groups + er = ::NetUserGetLocalGroups(NULL, wzUserDomain, 0, LG_INCLUDE_INDIRECT, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal); + // Ignore these errors, and just go to the fallback checks + if (NERR_UserNotFound == er || NERR_DCNotFound == er || RPC_S_SERVER_UNAVAILABLE == er) + { + Trace(REPORT_VERBOSE, "failed to get local groups for user %ls from domain %ls with error code 0x%x - continuing", pwzName, (wz != NULL) ? wz : L"", HRESULT_FROM_WIN32(er)); + er = ERROR_SUCCESS; + } + UserExitOnWin32Error(er, hr, "Failed to get list of groups for user while checking group membership information for user: %ls", pwzName); + + if (dwRead != dwTotal) + { + hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); + UserExitOnRootFailure(hr, "Failed to get entire list of groups (local) for user while checking group membership information for user: %ls", pwzName); + } + + if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead)) + { + *lpfMember = TRUE; + ExitFunction1(hr = S_OK); + } + + // If the above methods failed, let's try active directory + hr = UserCreateADsPath(pwzDomain, pwzName, &bstrUser); + UserExitOnFailure(hr, "failed to create user ADsPath in order to check group membership for group: %ls domain: %ls", pwzName, pwzDomain); + + hr = UserCreateADsPath(pwzGroupDomain, pwzGroupName, &bstrGroup); + UserExitOnFailure(hr, "failed to create group ADsPath in order to check group membership for group: %ls domain: %ls", pwzGroupName, pwzGroupDomain); + + if (lstrlenW(pwzGroupDomain) > 0) + { + hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); + UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); + + hr = pGroup->IsMember(bstrUser, &vtBoolResult); + UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); + } + + if (vtBoolResult) + { + *lpfMember = TRUE; + ExitFunction1(hr = S_OK); + } + + hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast(&pGroup)); + UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast(bstrGroup) ); + + hr = pGroup->IsMember(bstrUser, &vtBoolResult); + UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast(bstrUser), reinterpret_cast(bstrGroup) ); + + if (vtBoolResult) + { + *lpfMember = TRUE; + ExitFunction1(hr = S_OK); + } + +LExit: + ReleaseObject(pGroup); + ReleaseBSTR(bstrUser); + ReleaseBSTR(bstrGroup); + + if (NULL != pguiGroupData) + { + ::NetApiBufferFree(pguiGroupData); + } + + return hr; +} + + +/******************************************************************* + Takes a domain and name, and allocates a BSTR which represents + DOMAIN\NAME's active directory path. The BSTR this function returns + should be released manually using the ReleaseBSTR() macro. +********************************************************************/ +extern "C" HRESULT DAPI UserCreateADsPath( + __in_z LPCWSTR wzObjectDomain, + __in_z LPCWSTR wzObjectName, + __out BSTR *pbstrAdsPath + ) +{ + Assert(wzObjectDomain && wzObjectName && *wzObjectName); + + HRESULT hr = S_OK; + LPWSTR pwzAdsPath = NULL; + + hr = StrAllocString(&pwzAdsPath, L"WinNT://", 0); + UserExitOnFailure(hr, "failed to allocate AdsPath string"); + + if (*wzObjectDomain) + { + hr = StrAllocFormatted(&pwzAdsPath, L"%s/%s", wzObjectDomain, wzObjectName); + UserExitOnFailure(hr, "failed to allocate AdsPath string"); + } + else if (NULL != wcsstr(wzObjectName, L"\\") || NULL != wcsstr(wzObjectName, L"/")) + { + hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0); + UserExitOnFailure(hr, "failed to concat objectname: %ls", wzObjectName); + } + else + { + hr = StrAllocConcat(&pwzAdsPath, L"Localhost/", 0); + UserExitOnFailure(hr, "failed to concat LocalHost/"); + + hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0); + UserExitOnFailure(hr, "failed to concat object name: %ls", wzObjectName); + } + + *pbstrAdsPath = ::SysAllocString(pwzAdsPath); + if (NULL == *pbstrAdsPath) + { + hr = E_OUTOFMEMORY; + } + +LExit: + ReleaseStr(pwzAdsPath); + + return hr; +} + + +/******************************************************************* + Helper function to check if pwzGroupUserDomain (in form of "domain\username" is + a member of a given LOCALGROUP_USERS_INFO_0 structure. Useful to pass in the + output from both NetUserGetGroups() and NetUserGetLocalGroups() +********************************************************************/ +static BOOL CheckIsMemberHelper( + __in_z LPCWSTR pwzGroupUserDomain, + __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData, + __in DWORD cguiGroupData + ) +{ + if (NULL == pguiGroupData) + { + return FALSE; + } + + for (DWORD dwCounter = 0; dwCounter < cguiGroupData; ++dwCounter) + { + // If the user is a member of the group, set the output flag to true + if (0 == lstrcmpiW(pwzGroupUserDomain, pguiGroupData[dwCounter].grui0_name)) + { + return TRUE; + } + } + + return FALSE; +} diff --git a/src/libs/dutil/WixToolset.DUtil/verutil.cpp b/src/libs/dutil/WixToolset.DUtil/verutil.cpp new file mode 100644 index 00000000..21626f94 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/verutil.cpp @@ -0,0 +1,647 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// Exit macros +#define VerExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__) +#define VerExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__) +#define VerExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__) +#define VerExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__) +#define VerExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__) +#define VerExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_VERUTIL, e, x, s, __VA_ARGS__) + +// constants +const DWORD GROW_RELEASE_LABELS = 3; + +// Forward declarations. +static int CompareDword( + __in const DWORD& dw1, + __in const DWORD& dw2 + ); +static HRESULT CompareReleaseLabel( + __in const VERUTIL_VERSION_RELEASE_LABEL* p1, + __in LPCWSTR wzVersion1, + __in const VERUTIL_VERSION_RELEASE_LABEL* p2, + __in LPCWSTR wzVersion2, + __out int* pnResult + ); +static HRESULT CompareVersionSubstring( + __in LPCWSTR wzString1, + __in int cchCount1, + __in LPCWSTR wzString2, + __in int cchCount2, + __out int* pnResult + ); + + +DAPI_(HRESULT) VerCompareParsedVersions( + __in_opt VERUTIL_VERSION* pVersion1, + __in_opt VERUTIL_VERSION* pVersion2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + int nResult = 0; + DWORD cMaxReleaseLabels = 0; + BOOL fCompareMetadata = FALSE; + + if (pVersion1 && !pVersion1->sczVersion || + pVersion2 && !pVersion2->sczVersion) + { + ExitFunction1(hr = E_INVALIDARG); + } + + if (pVersion1 == pVersion2) + { + ExitFunction1(nResult = 0); + } + else if (pVersion1 && !pVersion2) + { + ExitFunction1(nResult = 1); + } + else if (!pVersion1 && pVersion2) + { + ExitFunction1(nResult = -1); + } + + nResult = CompareDword(pVersion1->dwMajor, pVersion2->dwMajor); + if (0 != nResult) + { + ExitFunction(); + } + + nResult = CompareDword(pVersion1->dwMinor, pVersion2->dwMinor); + if (0 != nResult) + { + ExitFunction(); + } + + nResult = CompareDword(pVersion1->dwPatch, pVersion2->dwPatch); + if (0 != nResult) + { + ExitFunction(); + } + + nResult = CompareDword(pVersion1->dwRevision, pVersion2->dwRevision); + if (0 != nResult) + { + ExitFunction(); + } + + if (pVersion1->cReleaseLabels) + { + if (pVersion2->cReleaseLabels) + { + cMaxReleaseLabels = max(pVersion1->cReleaseLabels, pVersion2->cReleaseLabels); + } + else + { + ExitFunction1(nResult = -1); + } + } + else if (pVersion2->cReleaseLabels) + { + ExitFunction1(nResult = 1); + } + + if (cMaxReleaseLabels) + { + for (DWORD i = 0; i < cMaxReleaseLabels; ++i) + { + VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel1 = pVersion1->cReleaseLabels > i ? pVersion1->rgReleaseLabels + i : NULL; + VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel2 = pVersion2->cReleaseLabels > i ? pVersion2->rgReleaseLabels + i : NULL; + + hr = CompareReleaseLabel(pReleaseLabel1, pVersion1->sczVersion, pReleaseLabel2, pVersion2->sczVersion, &nResult); + if (FAILED(hr) || 0 != nResult) + { + ExitFunction(); + } + } + } + + if (pVersion1->fInvalid) + { + if (!pVersion2->fInvalid) + { + ExitFunction1(nResult = -1); + } + else + { + fCompareMetadata = TRUE; + } + } + else if (pVersion2->fInvalid) + { + ExitFunction1(nResult = 1); + } + + if (fCompareMetadata) + { + hr = CompareVersionSubstring(pVersion1->sczVersion + pVersion1->cchMetadataOffset, -1, pVersion2->sczVersion + pVersion2->cchMetadataOffset, -1, &nResult); + } + +LExit: + *pnResult = nResult; + return hr; +} + +DAPI_(HRESULT) VerCompareStringVersions( + __in_z LPCWSTR wzVersion1, + __in_z LPCWSTR wzVersion2, + __in BOOL fStrict, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + int nResult = 0; + + hr = VerParseVersion(wzVersion1, 0, fStrict, &pVersion1); + VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, fStrict, &pVersion2); + VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion2); + + hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult); + VerExitOnFailure(hr, "Failed to compare parsed Verutil versions '%ls' and '%ls'.", wzVersion1, wzVersion2); + +LExit: + *pnResult = nResult; + + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + + return hr; +} + +DAPI_(HRESULT) VerCopyVersion( + __in VERUTIL_VERSION* pSource, + __out VERUTIL_VERSION** ppVersion + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pCopy = NULL; + + pCopy = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); + VerExitOnNull(pCopy, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version copy."); + + hr = StrAllocString(&pCopy->sczVersion, pSource->sczVersion, 0); + VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", pSource->sczVersion); + + pCopy->dwMajor = pSource->dwMajor; + pCopy->dwMinor = pSource->dwMinor; + pCopy->dwPatch = pSource->dwPatch; + pCopy->dwRevision = pSource->dwRevision; + + if (pSource->cReleaseLabels) + { + hr = MemEnsureArraySize(reinterpret_cast(&pCopy->rgReleaseLabels), 0, sizeof(VERUTIL_VERSION_RELEASE_LABEL), pSource->cReleaseLabels); + VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels copies."); + + pCopy->cReleaseLabels = pSource->cReleaseLabels; + + for (DWORD i = 0; i < pCopy->cReleaseLabels; ++i) + { + VERUTIL_VERSION_RELEASE_LABEL* pSourceLabel = pSource->rgReleaseLabels + i; + VERUTIL_VERSION_RELEASE_LABEL* pCopyLabel = pCopy->rgReleaseLabels + i; + + pCopyLabel->cchLabelOffset = pSourceLabel->cchLabelOffset; + pCopyLabel->cchLabel = pSourceLabel->cchLabel; + pCopyLabel->fNumeric = pSourceLabel->fNumeric; + pCopyLabel->dwValue = pSourceLabel->dwValue; + } + } + + pCopy->cchMetadataOffset = pSource->cchMetadataOffset; + pCopy->fInvalid = pSource->fInvalid; + + *ppVersion = pCopy; + pCopy = NULL; + +LExit: + ReleaseVerutilVersion(pCopy); + + return hr; +} + +DAPI_(void) VerFreeVersion( + __in VERUTIL_VERSION* pVersion + ) +{ + if (pVersion) + { + ReleaseStr(pVersion->sczVersion); + ReleaseMem(pVersion->rgReleaseLabels); + ReleaseMem(pVersion); + } +} + +DAPI_(HRESULT) VerParseVersion( + __in_z LPCWSTR wzVersion, + __in SIZE_T cchVersion, + __in BOOL fStrict, + __out VERUTIL_VERSION** ppVersion + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + LPCWSTR wzEnd = NULL; + LPCWSTR wzPartBegin = NULL; + LPCWSTR wzPartEnd = NULL; + BOOL fInvalid = FALSE; + BOOL fLastPart = FALSE; + BOOL fTrailingDot = FALSE; + BOOL fParsedVersionNumber = FALSE; + BOOL fExpectedReleaseLabels = FALSE; + DWORD iPart = 0; + + if (!wzVersion || !ppVersion) + { + ExitFunction1(hr = E_INVALIDARG); + } + + // Get string length if not provided. + if (!cchVersion) + { + hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast(&cchVersion)); + VerExitOnRootFailure(hr, "Failed to get length of version string: %ls", wzVersion); + } + else if (INT_MAX < cchVersion) + { + VerExitOnRootFailure(hr = E_INVALIDARG, "Version string is too long: %Iu", cchVersion); + } + + if (L'v' == *wzVersion || L'V' == *wzVersion) + { + ++wzVersion; + --cchVersion; + } + + pVersion = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); + VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version '%ls'.", wzVersion); + + hr = StrAllocString(&pVersion->sczVersion, wzVersion, cchVersion); + VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", wzVersion); + + wzVersion = wzPartBegin = wzPartEnd = pVersion->sczVersion; + + // Save end pointer. + wzEnd = wzVersion + cchVersion; + + // Parse version number + while (wzPartBegin < wzEnd) + { + fTrailingDot = FALSE; + + // Find end of part. + for (;;) + { + if (wzPartEnd >= wzEnd) + { + fLastPart = TRUE; + break; + } + + switch (*wzPartEnd) + { + case L'0': + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'8': + case L'9': + ++wzPartEnd; + continue; + case L'.': + fTrailingDot = TRUE; + break; + case L'-': + case L'+': + fLastPart = TRUE; + break; + default: + fInvalid = TRUE; + break; + } + + break; + } + + if (wzPartBegin == wzPartEnd) + { + fInvalid = TRUE; + } + + if (fInvalid) + { + break; + } + + DWORD cchPart = 0; + hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart); + if (FAILED(hr)) + { + fInvalid = TRUE; + break; + } + + // Parse version part. + UINT uPart = 0; + hr = StrStringToUInt32(wzPartBegin, cchPart, &uPart); + if (FAILED(hr)) + { + fInvalid = TRUE; + break; + } + + switch (iPart) + { + case 0: + pVersion->dwMajor = uPart; + break; + case 1: + pVersion->dwMinor = uPart; + break; + case 2: + pVersion->dwPatch = uPart; + break; + case 3: + pVersion->dwRevision = uPart; + break; + } + + if (fTrailingDot) + { + ++wzPartEnd; + } + wzPartBegin = wzPartEnd; + ++iPart; + + if (4 <= iPart || fLastPart) + { + fParsedVersionNumber = TRUE; + break; + } + } + + fInvalid |= !fParsedVersionNumber || fTrailingDot; + + if (!fInvalid && wzPartBegin < wzEnd && *wzPartBegin == L'-') + { + wzPartBegin = wzPartEnd = wzPartBegin + 1; + fExpectedReleaseLabels = TRUE; + fLastPart = FALSE; + } + + while (fExpectedReleaseLabels && wzPartBegin < wzEnd) + { + fTrailingDot = FALSE; + + // Find end of part. + for (;;) + { + if (wzPartEnd >= wzEnd) + { + fLastPart = TRUE; + break; + } + + if (*wzPartEnd >= L'0' && *wzPartEnd <= L'9' || + *wzPartEnd >= L'A' && *wzPartEnd <= L'Z' || + *wzPartEnd >= L'a' && *wzPartEnd <= L'z' || + *wzPartEnd == L'-') + { + ++wzPartEnd; + continue; + } + else if (*wzPartEnd == L'+') + { + fLastPart = TRUE; + } + else if (*wzPartEnd == L'.') + { + fTrailingDot = TRUE; + } + else + { + fInvalid = TRUE; + } + + break; + } + + if (wzPartBegin == wzPartEnd) + { + fInvalid = TRUE; + } + + if (fInvalid) + { + break; + } + + int cchLabel = 0; + hr = ::PtrdiffTToInt32(wzPartEnd - wzPartBegin, &cchLabel); + if (FAILED(hr) || 0 > cchLabel) + { + fInvalid = TRUE; + break; + } + + hr = MemReAllocArray(reinterpret_cast(&pVersion->rgReleaseLabels), pVersion->cReleaseLabels, sizeof(VERUTIL_VERSION_RELEASE_LABEL), GROW_RELEASE_LABELS - (pVersion->cReleaseLabels % GROW_RELEASE_LABELS)); + VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels '%ls'", wzVersion); + + VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel = pVersion->rgReleaseLabels + pVersion->cReleaseLabels; + ++pVersion->cReleaseLabels; + + // Try to parse as number. + UINT uLabel = 0; + hr = StrStringToUInt32(wzPartBegin, cchLabel, &uLabel); + if (SUCCEEDED(hr)) + { + pReleaseLabel->fNumeric = TRUE; + pReleaseLabel->dwValue = uLabel; + } + + pReleaseLabel->cchLabelOffset = wzPartBegin - pVersion->sczVersion; + pReleaseLabel->cchLabel = cchLabel; + + if (fTrailingDot) + { + ++wzPartEnd; + } + wzPartBegin = wzPartEnd; + + if (fLastPart) + { + break; + } + } + + fInvalid |= fExpectedReleaseLabels && (!pVersion->cReleaseLabels || fTrailingDot); + + if (!fInvalid && wzPartBegin < wzEnd) + { + if (*wzPartBegin == L'+') + { + wzPartBegin = wzPartEnd = wzPartBegin + 1; + } + else + { + fInvalid = TRUE; + } + } + + if (fInvalid && fStrict) + { + ExitFunction1(hr = E_INVALIDARG); + } + + pVersion->cchMetadataOffset = min(wzPartBegin, wzEnd) - pVersion->sczVersion; + pVersion->fInvalid = fInvalid; + + *ppVersion = pVersion; + pVersion = NULL; + hr = S_OK; + +LExit: + ReleaseVerutilVersion(pVersion); + + return hr; +} + +DAPI_(HRESULT) VerVersionFromQword( + __in DWORD64 qwVersion, + __out VERUTIL_VERSION** ppVersion + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + + pVersion = reinterpret_cast(MemAlloc(sizeof(VERUTIL_VERSION), TRUE)); + VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version from QWORD."); + + pVersion->dwMajor = (WORD)(qwVersion >> 48 & 0xffff); + pVersion->dwMinor = (WORD)(qwVersion >> 32 & 0xffff); + pVersion->dwPatch = (WORD)(qwVersion >> 16 & 0xffff); + pVersion->dwRevision = (WORD)(qwVersion & 0xffff); + + hr = StrAllocFormatted(&pVersion->sczVersion, L"%lu.%lu.%lu.%lu", pVersion->dwMajor, pVersion->dwMinor, pVersion->dwPatch, pVersion->dwRevision); + ExitOnFailure(hr, "Failed to allocate and format the version string."); + + pVersion->cchMetadataOffset = lstrlenW(pVersion->sczVersion); + + *ppVersion = pVersion; + pVersion = NULL; + +LExit: + ReleaseVerutilVersion(pVersion); + + return hr; +} + + +static int CompareDword( + __in const DWORD& dw1, + __in const DWORD& dw2 + ) +{ + int nResult = 0; + + if (dw1 > dw2) + { + nResult = 1; + } + else if (dw1 < dw2) + { + nResult = -1; + } + + return nResult; +} + +static HRESULT CompareReleaseLabel( + __in const VERUTIL_VERSION_RELEASE_LABEL* p1, + __in LPCWSTR wzVersion1, + __in const VERUTIL_VERSION_RELEASE_LABEL* p2, + __in LPCWSTR wzVersion2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + int nResult = 0; + + if (p1 == p2) + { + ExitFunction(); + } + else if (p1 && !p2) + { + ExitFunction1(nResult = 1); + } + else if (!p1 && p2) + { + ExitFunction1(nResult = -1); + } + + if (p1->fNumeric) + { + if (p2->fNumeric) + { + nResult = CompareDword(p1->dwValue, p2->dwValue); + } + else + { + nResult = -1; + } + } + else + { + if (p2->fNumeric) + { + nResult = 1; + } + else + { + hr = CompareVersionSubstring(wzVersion1 + p1->cchLabelOffset, p1->cchLabel, wzVersion2 + p2->cchLabelOffset, p2->cchLabel, &nResult); + } + } + +LExit: + *pnResult = nResult; + + return hr; +} + +static HRESULT CompareVersionSubstring( + __in LPCWSTR wzString1, + __in int cchCount1, + __in LPCWSTR wzString2, + __in int cchCount2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + int nResult = 0; + + nResult = ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzString1, cchCount1, wzString2, cchCount2); + if (!nResult) + { + VerExitOnLastError(hr, "Failed to compare version substrings"); + } + +LExit: + *pnResult = nResult - 2; + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/wiutil.cpp b/src/libs/dutil/WixToolset.DUtil/wiutil.cpp new file mode 100644 index 00000000..7414ac42 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/wiutil.cpp @@ -0,0 +1,1629 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define WiuExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) +#define WiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__) +#define WiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__) +#define WiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__) +#define WiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__) +#define WiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_WIUTIL, e, x, s, __VA_ARGS__) +#define WiuExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_WIUTIL, g, x, s, __VA_ARGS__) + + +// constants + +const DWORD WIU_MSI_PROGRESS_INVALID = 0xFFFFFFFF; +const DWORD WIU_GOOD_ENOUGH_PROPERTY_LENGTH = 64; + + +// structs + + +static PFN_MSIENABLELOGW vpfnMsiEnableLogW = ::MsiEnableLogW; +static PFN_MSIGETPRODUCTINFOW vpfnMsiGetProductInfoW = ::MsiGetProductInfoW; +static PFN_MSIQUERYFEATURESTATEW vpfnMsiQueryFeatureStateW = ::MsiQueryFeatureStateW; +static PFN_MSIGETCOMPONENTPATHW vpfnMsiGetComponentPathW = ::MsiGetComponentPathW; +static PFN_MSILOCATECOMPONENTW vpfnMsiLocateComponentW = ::MsiLocateComponentW; +static PFN_MSIINSTALLPRODUCTW vpfnMsiInstallProductW = ::MsiInstallProductW; +static PFN_MSICONFIGUREPRODUCTEXW vpfnMsiConfigureProductExW = ::MsiConfigureProductExW; +static PFN_MSIREMOVEPATCHESW vpfnMsiRemovePatchesW = ::MsiRemovePatchesW; +static PFN_MSISETINTERNALUI vpfnMsiSetInternalUI = ::MsiSetInternalUI; +static PFN_MSISETEXTERNALUIW vpfnMsiSetExternalUIW = ::MsiSetExternalUIW; +static PFN_MSIENUMPRODUCTSW vpfnMsiEnumProductsW = ::MsiEnumProductsW; +static PFN_MSIENUMRELATEDPRODUCTSW vpfnMsiEnumRelatedProductsW = ::MsiEnumRelatedProductsW; + +// MSI 3.0+ +static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceW = NULL; +static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesW = NULL; +static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExW = NULL; +static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExW = NULL; +static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExW = NULL; +static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecord = NULL; +static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL; + +static HMODULE vhMsiDll = NULL; +static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; +static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; +static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExWFromLibrary = NULL; +static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExWFromLibrary = NULL; +static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExWFromLibrary = NULL; +static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecordFromLibrary = NULL; +static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExWFromLibrary = NULL; + +// MSI Transactions v4.5+ +static PFN_MSIBEGINTRANSACTIONW vpfnMsiBeginTransaction = NULL; +static PFN_MSIENDTRANSACTION vpfnMsiEndTransaction = NULL; + +static BOOL vfWiuInitialized = FALSE; + +// globals +static DWORD vdwMsiDllMajorMinor = 0; +static DWORD vdwMsiDllBuildRevision = 0; + + +// internal function declarations + +static DWORD CheckForRestartErrorCode( + __in DWORD dwErrorCode, + __out WIU_RESTART* pRestart + ); +static INT CALLBACK InstallEngineCallback( + __in LPVOID pvContext, + __in UINT uiMessage, + __in_z_opt LPCWSTR wzMessage + ); +static INT CALLBACK InstallEngineRecordCallback( + __in LPVOID pvContext, + __in UINT uiMessage, + __in_opt MSIHANDLE hRecord + ); +static INT HandleInstallMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ); +static INT HandleInstallProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in_z_opt LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ); +static INT SendMsiMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ); +static INT SendErrorMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ); +static INT SendFilesInUseMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in_opt MSIHANDLE hRecord, + __in BOOL fRestartManagerRequest + ); +static INT SendProgressUpdate( + __in WIU_MSI_EXECUTE_CONTEXT* pContext + ); +static void ResetProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext + ); +static DWORD CalculatePhaseProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in DWORD dwProgressIndex, + __in DWORD dwWeightPercentage + ); +void InitializeMessageData( + __in_opt MSIHANDLE hRecord, + __deref_out_ecount(*pcData) LPWSTR** prgsczData, + __out DWORD* pcData + ); +void UninitializeMessageData( + __in LPWSTR* rgsczData, + __in DWORD cData + ); + + +/******************************************************************** + WiuInitialize - initializes wiutil + +*********************************************************************/ +extern "C" HRESULT DAPI WiuInitialize( + ) +{ + HRESULT hr = S_OK; + LPWSTR sczMsiDllPath = NULL; + + hr = LoadSystemLibraryWithPath(L"Msi.dll", &vhMsiDll, &sczMsiDllPath); + WiuExitOnFailure(hr, "Failed to load Msi.DLL"); + + // Ignore failures + FileVersion(sczMsiDllPath, &vdwMsiDllMajorMinor, &vdwMsiDllBuildRevision); + + vpfnMsiDeterminePatchSequenceWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiDeterminePatchSequenceW")); + if (NULL == vpfnMsiDeterminePatchSequenceW) + { + vpfnMsiDeterminePatchSequenceW = vpfnMsiDeterminePatchSequenceWFromLibrary; + } + + vpfnMsiDetermineApplicablePatchesWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiDetermineApplicablePatchesW")); + if (NULL == vpfnMsiDetermineApplicablePatchesW) + { + vpfnMsiDetermineApplicablePatchesW = vpfnMsiDetermineApplicablePatchesWFromLibrary; + } + + vpfnMsiEnumProductsExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiEnumProductsExW")); + if (NULL == vpfnMsiEnumProductsExW) + { + vpfnMsiEnumProductsExW = vpfnMsiEnumProductsExWFromLibrary; + } + + vpfnMsiGetPatchInfoExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiGetPatchInfoExW")); + if (NULL == vpfnMsiGetPatchInfoExW) + { + vpfnMsiGetPatchInfoExW = vpfnMsiGetPatchInfoExWFromLibrary; + } + + vpfnMsiGetProductInfoExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiGetProductInfoExW")); + if (NULL == vpfnMsiGetProductInfoExW) + { + vpfnMsiGetProductInfoExW = vpfnMsiGetProductInfoExWFromLibrary; + } + + vpfnMsiSetExternalUIRecordFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiSetExternalUIRecord")); + if (NULL == vpfnMsiSetExternalUIRecord) + { + vpfnMsiSetExternalUIRecord = vpfnMsiSetExternalUIRecordFromLibrary; + } + + //static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL; + vpfnMsiSourceListAddSourceExWFromLibrary = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiSourceListAddSourceExW")); + if (NULL == vpfnMsiSourceListAddSourceExW) + { + vpfnMsiSourceListAddSourceExW = vpfnMsiSourceListAddSourceExWFromLibrary; + } + + // MSI Transaction functions + if (NULL == vpfnMsiBeginTransaction) + { + vpfnMsiBeginTransaction = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiBeginTransactionW")); + } + + if (NULL == vpfnMsiEndTransaction) + { + vpfnMsiEndTransaction = reinterpret_cast(::GetProcAddress(vhMsiDll, "MsiEndTransaction")); + } + + vfWiuInitialized = TRUE; + +LExit: + ReleaseStr(sczMsiDllPath); + return hr; +} + + +/******************************************************************** + WiuUninitialize - uninitializes wiutil + +*********************************************************************/ +extern "C" void DAPI WiuUninitialize( + ) +{ + if (vhMsiDll) + { + ::FreeLibrary(vhMsiDll); + vhMsiDll = NULL; + vpfnMsiSetExternalUIRecordFromLibrary = NULL; + vpfnMsiGetProductInfoExWFromLibrary = NULL; + vpfnMsiGetPatchInfoExWFromLibrary = NULL; + vpfnMsiEnumProductsExWFromLibrary = NULL; + vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; + vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; + vpfnMsiSourceListAddSourceExWFromLibrary = NULL; + vpfnMsiBeginTransaction = NULL; + vpfnMsiEndTransaction = NULL; + } + + vfWiuInitialized = FALSE; +} + + +/******************************************************************** + WiuFunctionOverride - overrides the Windows installer functions. Typically used + for unit testing. + +*********************************************************************/ +extern "C" void DAPI WiuFunctionOverride( + __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW, + __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW, + __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW, + __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW, + __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW, + __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW, + __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW, + __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW, + __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI, + __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW, + __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW, + __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord, + __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW + ) +{ + vpfnMsiEnableLogW = pfnMsiEnableLogW ? pfnMsiEnableLogW : ::MsiEnableLogW; + vpfnMsiGetComponentPathW = pfnMsiGetComponentPathW ? pfnMsiGetComponentPathW : ::MsiGetComponentPathW; + vpfnMsiLocateComponentW = pfnMsiLocateComponentW ? pfnMsiLocateComponentW : ::MsiLocateComponentW; + vpfnMsiQueryFeatureStateW = pfnMsiQueryFeatureStateW ? pfnMsiQueryFeatureStateW : ::MsiQueryFeatureStateW; + vpfnMsiGetProductInfoW = pfnMsiGetProductInfoW ? pfnMsiGetProductInfoW : vpfnMsiGetProductInfoW; + vpfnMsiInstallProductW = pfnMsiInstallProductW ? pfnMsiInstallProductW : ::MsiInstallProductW; + vpfnMsiConfigureProductExW = pfnMsiConfigureProductExW ? pfnMsiConfigureProductExW : ::MsiConfigureProductExW; + vpfnMsiSetInternalUI = pfnMsiSetInternalUI ? pfnMsiSetInternalUI : ::MsiSetInternalUI; + vpfnMsiSetExternalUIW = pfnMsiSetExternalUIW ? pfnMsiSetExternalUIW : ::MsiSetExternalUIW; + vpfnMsiEnumRelatedProductsW = pfnMsiEnumRelatedProductsW ? pfnMsiEnumRelatedProductsW : ::MsiEnumRelatedProductsW; + vpfnMsiGetProductInfoExW = pfnMsiGetProductInfoExW ? pfnMsiGetProductInfoExW : vpfnMsiGetProductInfoExWFromLibrary; + vpfnMsiSetExternalUIRecord = pfnMsiSetExternalUIRecord ? pfnMsiSetExternalUIRecord : vpfnMsiSetExternalUIRecordFromLibrary; + vpfnMsiSourceListAddSourceExW = pfnMsiSourceListAddSourceExW ? pfnMsiSourceListAddSourceExW : vpfnMsiSourceListAddSourceExWFromLibrary; +} + + +extern "C" HRESULT DAPI WiuGetComponentPath( + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzComponentId, + __out INSTALLSTATE* pInstallState, + __out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + DWORD cchCompare; + + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to allocate string for component path."); + + cchCompare = cch; + *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); + if (INSTALLSTATE_MOREDATA == *pInstallState) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for component path."); + + cchCompare = cch; + *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); + } + + if (INSTALLSTATE_INVALIDARG == *pInstallState) + { + hr = E_INVALIDARG; + WiuExitOnRootFailure(hr, "Invalid argument when getting component path."); + } + else if (INSTALLSTATE_UNKNOWN == *pInstallState) + { + ExitFunction(); + } + + // If the actual path length is greater than or equal to the original buffer + // allocate a larger buffer and get the path again, just in case we are + // missing any part of the path. + if (cchCompare <= cch) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for component path."); + + *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuLocateComponent( + __in_z LPCWSTR wzComponentId, + __out INSTALLSTATE* pInstallState, + __out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + DWORD cchCompare; + + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to allocate string for component path."); + + cchCompare = cch; + *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); + if (INSTALLSTATE_MOREDATA == *pInstallState) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for component path."); + + cchCompare = cch; + *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); + } + + if (INSTALLSTATE_INVALIDARG == *pInstallState) + { + hr = E_INVALIDARG; + WiuExitOnRootFailure(hr, "Invalid argument when locating component."); + } + else if (INSTALLSTATE_UNKNOWN == *pInstallState) + { + ExitFunction(); + } + + // If the actual path length is greater than or equal to the original buffer + // allocate a larger buffer and get the path again, just in case we are + // missing any part of the path. + if (cchCompare <= cch) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for component path."); + + *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuQueryFeatureState( + __in_z LPCWSTR wzProduct, + __in_z LPCWSTR wzFeature, + __out INSTALLSTATE* pInstallState + ) +{ + HRESULT hr = S_OK; + + *pInstallState = vpfnMsiQueryFeatureStateW(wzProduct, wzFeature); + if (INSTALLSTATE_INVALIDARG == *pInstallState) + { + hr = E_INVALIDARG; + WiuExitOnRootFailure(hr, "Failed to query state of feature: %ls in product: %ls", wzFeature, wzProduct); + } + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuGetProductInfo( + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to allocate string for product info."); + + er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); + if (ERROR_MORE_DATA == er) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for product info."); + + er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); + } + WiuExitOnWin32Error(er, hr, "Failed to get product info."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuGetProductInfoEx( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + + if (!vpfnMsiGetProductInfoExW) + { + hr = WiuGetProductInfo(wzProductCode, wzProperty, psczValue); + WiuExitOnFailure(hr, "Failed to get product info when extended info was not available."); + + ExitFunction(); + } + + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to allocate string for extended product info."); + + er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); + if (ERROR_MORE_DATA == er) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for extended product info."); + + er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); + } + WiuExitOnWin32Error(er, hr, "Failed to get extended product info."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuGetProductProperty( + __in MSIHANDLE hProduct, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to allocate string for product property."); + + er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); + if (ERROR_MORE_DATA == er) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for product property."); + + er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); + } + WiuExitOnWin32Error(er, hr, "Failed to get product property."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuGetPatchInfoEx( + __in_z LPCWSTR wzPatchCode, + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in_z LPCWSTR wzProperty, + __out LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; + + if (!vpfnMsiGetPatchInfoExW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to allocate string for extended patch info."); + + er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); + if (ERROR_MORE_DATA == er) + { + ++cch; + hr = StrAlloc(psczValue, cch); + WiuExitOnFailure(hr, "Failed to reallocate string for extended patch info."); + + er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); + } + WiuExitOnWin32Error(er, hr, "Failed to get extended patch info."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuDeterminePatchSequence( + __in_z LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT context, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo, + __in DWORD cPatchInfo + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!vpfnMsiDeterminePatchSequenceW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + er = vpfnMsiDeterminePatchSequenceW(wzProductCode, wzUserSid, context, cPatchInfo, pPatchInfo); + WiuExitOnWin32Error(er, hr, "Failed to determine patch sequence for product code."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuDetermineApplicablePatches( + __in_z LPCWSTR wzProductPackagePath, + __in PMSIPATCHSEQUENCEINFOW pPatchInfo, + __in DWORD cPatchInfo + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!vpfnMsiDetermineApplicablePatchesW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + er = vpfnMsiDetermineApplicablePatchesW(wzProductPackagePath, cPatchInfo, pPatchInfo); + WiuExitOnWin32Error(er, hr, "Failed to determine applicable patches for product package."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuEnumProducts( + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiEnumProductsW(iProductIndex, wzProductCode); + if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + WiuExitOnWin32Error(er, hr, "Failed to enumerate products."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuEnumProductsEx( + __in_z_opt LPCWSTR wzProductCode, + __in_z_opt LPCWSTR wzUserSid, + __in DWORD dwContext, + __in DWORD dwIndex, + __out_opt WCHAR wzInstalledProductCode[39], + __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, + __out_opt LPWSTR wzSid, + __inout_opt LPDWORD pcchSid + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!vpfnMsiEnumProductsExW) + { + ExitFunction1(hr = E_NOTIMPL); + } + + er = vpfnMsiEnumProductsExW(wzProductCode, wzUserSid, dwContext, dwIndex, wzInstalledProductCode, pdwInstalledContext, wzSid, pcchSid); + if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + WiuExitOnWin32Error(er, hr, "Failed to enumerate products."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuEnumRelatedProducts( + __in_z LPCWSTR wzUpgradeCode, + __in DWORD iProductIndex, + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiEnumRelatedProductsW(wzUpgradeCode, 0, iProductIndex, wzProductCode); + if (ERROR_NO_MORE_ITEMS == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + } + WiuExitOnWin32Error(er, hr, "Failed to enumerate related products for updgrade code: %ls", wzUpgradeCode); + +LExit: + return hr; +} + +/******************************************************************** + WiuEnumRelatedProductCodes - Returns an array of related products for a given upgrade code. + + Parameters: + wzUpgradeCode - The upgrade code that will be used to find the related products. + prgsczProductCodes - Pointer to the array that will contain the product codes. + pcRelatedProducts - Returns the count of the number of related products found. + fReturnHighestVersionOnly - When set to "TRUE", will only return the product code of the highest version found. +********************************************************************/ +extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( + __in_z LPCWSTR wzUpgradeCode, + __deref_out_ecount_opt(*pcRelatedProducts) LPWSTR** prgsczProductCodes, + __out DWORD* pcRelatedProducts, + __in BOOL fReturnHighestVersionOnly + ) +{ + HRESULT hr = S_OK; + WCHAR wzCurrentProductCode[MAX_GUID_CHARS + 1] = { }; + LPWSTR sczInstalledVersion = NULL; + VERUTIL_VERSION* pCurrentVersion = NULL; + VERUTIL_VERSION* pHighestVersion = NULL; + int nCompare = 0; + + // make sure we start at zero + *pcRelatedProducts = 0; + + for (DWORD i = 0; ; ++i) + { + hr = WiuEnumRelatedProducts(wzUpgradeCode, i, wzCurrentProductCode); + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + break; + } + WiuExitOnFailure(hr, "Failed to enumerate related products for upgrade code: %ls", wzUpgradeCode); + + if (fReturnHighestVersionOnly) + { + // try to get the version but if the product registration is broken + // (for whatever reason), skip this product + hr = WiuGetProductInfo(wzCurrentProductCode, L"VersionString", &sczInstalledVersion); + if (FAILED(hr)) + { + WiuExitTrace(hr, "Could not get product version for product code: %ls, skipping...", wzCurrentProductCode); + continue; + } + + hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pCurrentVersion); + WiuExitOnFailure(hr, "Failed to parse version: %ls for product code: %ls", sczInstalledVersion, wzCurrentProductCode); + + if (pCurrentVersion->fInvalid) + { + WiuExitTrace(E_INVALIDDATA, "Enumerated msi package with invalid version, product code: '%1!ls!', version: '%2!ls!'"); + } + + // if this is the first product found then it is the highest version (for now) + if (!pHighestVersion) + { + pHighestVersion = pCurrentVersion; + pCurrentVersion = NULL; + } + else + { + hr = VerCompareParsedVersions(pCurrentVersion, pHighestVersion, &nCompare); + WiuExitOnFailure(hr, "Failed to compare version '%ls' to highest version: '%ls'", pCurrentVersion->sczVersion, pHighestVersion->sczVersion); + + // if this is the highest version encountered so far then overwrite + // the first item in the array (there will never be more than one item) + if (nCompare > 0) + { + ReleaseVerutilVersion(pHighestVersion); + pHighestVersion = pCurrentVersion; + pCurrentVersion = NULL; + + hr = StrAllocString(prgsczProductCodes[0], wzCurrentProductCode, 0); + WiuExitOnFailure(hr, "Failed to update array with higher versioned product code."); + } + else + { + ReleaseVerutilVersion(pCurrentVersion); + } + + // continue here as we don't want anything else added to the list + continue; + } + } + + hr = StrArrayAllocString(prgsczProductCodes, (LPUINT)(pcRelatedProducts), wzCurrentProductCode, 0); + WiuExitOnFailure(hr, "Failed to add product code to array."); + } + +LExit: + ReleaseVerutilVersion(pCurrentVersion); + ReleaseVerutilVersion(pHighestVersion); + ReleaseStr(sczInstalledVersion); + return hr; +} + + +extern "C" HRESULT DAPI WiuEnableLog( + __in DWORD dwLogMode, + __in_z LPCWSTR wzLogFile, + __in DWORD dwLogAttributes + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiEnableLogW(dwLogMode, wzLogFile, dwLogAttributes); + WiuExitOnWin32Error(er, hr, "Failed to enable MSI internal logging."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuInitializeInternalUI( + __in INSTALLUILEVEL internalUILevel, + __in_opt HWND hwndParent, + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ) +{ + HRESULT hr = S_OK; + + memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); + + pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(internalUILevel, &hwndParent); + pExecuteContext->hwndPreviousParentWindow = hwndParent; + + if (INSTALLUILEVEL_NOCHANGE == pExecuteContext->previousInstallUILevel) + { + hr = E_INVALIDARG; + } + + return hr; +} + + +extern "C" HRESULT DAPI WiuInitializeExternalUI( + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in INSTALLUILEVEL internalUILevel, + __in_opt HWND hwndParent, + __in LPVOID pvContext, + __in BOOL fRollback, + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + DWORD dwMessageFilter = INSTALLLOGMODE_INITIALIZE | INSTALLLOGMODE_TERMINATE | + INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | + INSTALLLOGMODE_RESOLVESOURCE | INSTALLLOGMODE_OUTOFDISKSPACE | + INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA | + INSTALLLOGMODE_PROGRESS | INSTALLLOGMODE_FILESINUSE; + + if (MAKEDWORD(0, 4) <= vdwMsiDllMajorMinor) + { + dwMessageFilter |= INSTALLLOGMODE_RMFILESINUSE; + } + + // Wire the internal and external UI handler. + hr = WiuInitializeInternalUI(internalUILevel, hwndParent, pExecuteContext); + WiuExitOnFailure(hr, "Failed to set internal UI level and window."); + + pExecuteContext->fRollback = fRollback; + pExecuteContext->pfnMessageHandler = pfnMessageHandler; + pExecuteContext->pvContext = pvContext; + + // If the external UI record is available (MSI version >= 3.1) use it but fall back to the standard external + // UI handler if necesary. + if (vpfnMsiSetExternalUIRecord) + { + er = vpfnMsiSetExternalUIRecord(InstallEngineRecordCallback, dwMessageFilter, pExecuteContext, &pExecuteContext->pfnPreviousExternalUIRecord); + WiuExitOnWin32Error(er, hr, "Failed to wire up external UI record handler."); + pExecuteContext->fSetPreviousExternalUIRecord = TRUE; + } + else + { + pExecuteContext->pfnPreviousExternalUI = vpfnMsiSetExternalUIW(InstallEngineCallback, dwMessageFilter, pExecuteContext); + pExecuteContext->fSetPreviousExternalUI = TRUE; + } + +LExit: + return hr; +} + + +extern "C" void DAPI WiuUninitializeExternalUI( + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext + ) +{ + if (INSTALLUILEVEL_NOCHANGE != pExecuteContext->previousInstallUILevel) + { + pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(pExecuteContext->previousInstallUILevel, &pExecuteContext->hwndPreviousParentWindow); + } + + if (pExecuteContext->fSetPreviousExternalUI) // unset the UI handler + { + vpfnMsiSetExternalUIW(pExecuteContext->pfnPreviousExternalUI, 0, NULL); + } + + if (pExecuteContext->fSetPreviousExternalUIRecord) // unset the UI record handler + { + vpfnMsiSetExternalUIRecord(pExecuteContext->pfnPreviousExternalUIRecord, 0, NULL, NULL); + } + + memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); +} + + +extern "C" HRESULT DAPI WiuConfigureProductEx( + __in_z LPCWSTR wzProduct, + __in int iInstallLevel, + __in INSTALLSTATE eInstallState, + __in_z LPCWSTR wzCommandLine, + __out WIU_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiConfigureProductExW(wzProduct, iInstallLevel, eInstallState, wzCommandLine); + er = CheckForRestartErrorCode(er, pRestart); + WiuExitOnWin32Error(er, hr, "Failed to configure product: %ls", wzProduct); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuInstallProduct( + __in_z LPCWSTR wzPackagePath, + __in_z LPCWSTR wzCommandLine, + __out WIU_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiInstallProductW(wzPackagePath, wzCommandLine); + er = CheckForRestartErrorCode(er, pRestart); + WiuExitOnWin32Error(er, hr, "Failed to install product: %ls", wzPackagePath); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuRemovePatches( + __in_z LPCWSTR wzPatchList, + __in_z LPCWSTR wzProductCode, + __in_z LPCWSTR wzPropertyList, + __out WIU_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiRemovePatchesW(wzPatchList, wzProductCode, INSTALLTYPE_SINGLE_INSTANCE, wzPropertyList); + er = CheckForRestartErrorCode(er, pRestart); + WiuExitOnWin32Error(er, hr, "Failed to remove patches."); + +LExit: + return hr; +} + + +extern "C" HRESULT DAPI WiuSourceListAddSourceEx( + __in_z LPCWSTR wzProductCodeOrPatchCode, + __in_z_opt LPCWSTR wzUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in DWORD dwCode, + __in_z LPCWSTR wzSource, + __in_opt DWORD dwIndex + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + er = vpfnMsiSourceListAddSourceExW(wzProductCodeOrPatchCode, wzUserSid, dwContext, MSISOURCETYPE_NETWORK | dwCode, wzSource, dwIndex); + WiuExitOnWin32Error(er, hr, "Failed to add source."); + +LExit: + return hr; +} + +extern "C" BOOL DAPI WiuIsMsiTransactionSupported( + ) +{ + return vpfnMsiBeginTransaction && vpfnMsiEndTransaction; +} + +extern "C" HRESULT DAPI WiuBeginTransaction( + __in_z LPCWSTR szName, + __in DWORD dwTransactionAttributes, + __out MSIHANDLE * phTransactionHandle, + __out HANDLE * phChangeOfOwnerEvent, + __in DWORD dwLogMode, + __in_z LPCWSTR szLogPath + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!WiuIsMsiTransactionSupported()) + { + WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); + } + + hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); + WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction"); + + er = vpfnMsiBeginTransaction(szName, dwTransactionAttributes, phTransactionHandle, phChangeOfOwnerEvent); + WiuExitOnWin32Error(er, hr, "Failed to begin transaction."); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI WiuEndTransaction( + __in DWORD dwTransactionState, + __in DWORD dwLogMode, + __in_z LPCWSTR szLogPath + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + if (!WiuIsMsiTransactionSupported()) + { + WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); + } + + hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); + WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction"); + + er = vpfnMsiEndTransaction(dwTransactionState); + WiuExitOnWin32Error(er, hr, "Failed to end transaction."); + +LExit: + return hr; +} + + + +static DWORD CheckForRestartErrorCode( + __in DWORD dwErrorCode, + __out WIU_RESTART* pRestart + ) +{ + switch (dwErrorCode) + { + case ERROR_SUCCESS_REBOOT_REQUIRED: + case ERROR_SUCCESS_RESTART_REQUIRED: + *pRestart = WIU_RESTART_REQUIRED; + dwErrorCode = ERROR_SUCCESS; + break; + + case ERROR_SUCCESS_REBOOT_INITIATED: + case ERROR_INSTALL_SUSPEND: + *pRestart = WIU_RESTART_INITIATED; + dwErrorCode = ERROR_SUCCESS; + break; + } + + return dwErrorCode; +} + +static INT CALLBACK InstallEngineCallback( + __in LPVOID pvContext, + __in UINT uiMessage, + __in_z_opt LPCWSTR wzMessage + ) +{ + INT nResult = IDNOACTION; + WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext; + INSTALLMESSAGE mt = static_cast(0xFF000000 & uiMessage); + UINT uiFlags = 0x00FFFFFF & uiMessage; + + if (wzMessage) + { + if (INSTALLMESSAGE_PROGRESS == mt) + { + nResult = HandleInstallProgress(pContext, wzMessage, NULL); + } + else + { + nResult = HandleInstallMessage(pContext, mt, uiFlags, wzMessage, NULL); + } + } + + return nResult; +} + +static INT CALLBACK InstallEngineRecordCallback( + __in LPVOID pvContext, + __in UINT uiMessage, + __in_opt MSIHANDLE hRecord + ) +{ + INT nResult = IDNOACTION; + HRESULT hr = S_OK; + WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext; + + INSTALLMESSAGE mt = static_cast(0xFF000000 & uiMessage); + UINT uiFlags = 0x00FFFFFF & uiMessage; + LPWSTR sczMessage = NULL; + DWORD cchMessage = 0; + + if (hRecord) + { + if (INSTALLMESSAGE_PROGRESS == mt) + { + nResult = HandleInstallProgress(pContext, NULL, hRecord); + } + else + { + // create formated message string +#pragma prefast(push) +#pragma prefast(disable:6298) // docs explicitly say this is a valid option for getting the buffer size + DWORD er = ::MsiFormatRecordW(NULL, hRecord, L"", &cchMessage); +#pragma prefast(pop) + if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) + { + hr = StrAlloc(&sczMessage, ++cchMessage); + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + WiuExitOnFailure(hr, "Failed to allocate string for formated message."); + + er = ::MsiFormatRecordW(NULL, hRecord, sczMessage, &cchMessage); + WiuExitOnWin32Error(er, hr, "Failed to format message record."); + + // Pass to handler including both the formated message and the original record. + nResult = HandleInstallMessage(pContext, mt, uiFlags, sczMessage, hRecord); + } + } + +LExit: + ReleaseStr(sczMessage); + return nResult; +} + +static INT HandleInstallMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ) +{ + INT nResult = IDNOACTION; + +Trace(REPORT_STANDARD, "MSI install[%x]: %ls", pContext->dwCurrentProgressIndex, wzMessage); + + // Handle the message. + switch (mt) + { + case INSTALLMESSAGE_INITIALIZE: // this message is received prior to internal UI initialization, no string data + ResetProgress(pContext); + break; + + case INSTALLMESSAGE_TERMINATE: // sent after UI termination, no string data + break; + + case INSTALLMESSAGE_ACTIONSTART: + if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData) + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; + } + + nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); + break; + + case INSTALLMESSAGE_ACTIONDATA: + if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData) + { + if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward) + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep; + } + else // rollback. + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep; + } + + nResult = SendProgressUpdate(pContext); + } + else + { + nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); + } + break; + + case INSTALLMESSAGE_OUTOFDISKSPACE: __fallthrough; + case INSTALLMESSAGE_FATALEXIT: __fallthrough; + case INSTALLMESSAGE_ERROR: + nResult = SendErrorMessage(pContext, uiFlags, wzMessage, hRecord); + break; + + case INSTALLMESSAGE_FILESINUSE: + case INSTALLMESSAGE_RMFILESINUSE: + nResult = SendFilesInUseMessage(pContext, hRecord, INSTALLMESSAGE_RMFILESINUSE == mt); + break; + +/* +#if 0 + case INSTALLMESSAGE_COMMONDATA: + if (L'1' == wzMessage[0] && L':' == wzMessage[1] && L' ' == wzMessage[2]) + { + if (L'0' == wzMessage[3]) + { + // TODO: handle the language common data message. + lres = IDOK; + return lres; + } + else if (L'1' == wzMessage[3]) + { + // TODO: really handle sending the caption. + lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CAPTION, uiFlags, reinterpret_cast(wzMessage + 3)); + return lres; + } + else if (L'2' == wzMessage[3]) + { + // TODO: really handle sending the cancel button status. + lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CANCEL, uiFlags, reinterpret_cast(wzMessage + 3)); + return lres; + } + } + break; +#endif +*/ + + //case INSTALLMESSAGE_WARNING: + //case INSTALLMESSAGE_USER: + //case INSTALLMESSAGE_INFO: + //case INSTALLMESSAGE_SHOWDIALOG: // sent prior to display of authored dialog or wizard + default: + nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); + break; + } + + // Always return "no action" (0) for resolve source messages. + return (INSTALLMESSAGE_RESOLVESOURCE == mt) ? IDNOACTION : nResult; +} + +static INT HandleInstallProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in_z_opt LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ) +{ + HRESULT hr = S_OK; + INT nResult = IDNOACTION; + INT iFields[4] = { }; + INT cFields = 0; + LPCWSTR pwz = NULL; + DWORD cch = 0; + + // get field values + if (hRecord) + { + cFields = ::MsiRecordGetFieldCount(hRecord); + cFields = min(cFields, countof(iFields)); // avoid buffer overrun if there are more fields than our buffer can hold + for (INT i = 0; i < cFields; ++i) + { + iFields[i] = ::MsiRecordGetInteger(hRecord, i + 1); + } + } + else + { + Assert(wzMessage); + + // parse message string + pwz = wzMessage; + while (cFields < 4) + { + // check if we have the start of a valid part + if ((L'1' + cFields) != pwz[0] || L':' != pwz[1] || L' ' != pwz[2]) + { + break; + } + pwz += 3; + + // find character count of number + cch = 0; + while (pwz[cch] && L' ' != pwz[cch]) + { + ++cch; + } + + // parse number + hr = StrStringToInt32(pwz, cch, &iFields[cFields]); + WiuExitOnFailure(hr, "Failed to parse MSI message part."); + + // increment field count + ++cFields; + } + } + +#ifdef _DEBUG + WCHAR wz[256]; + ::StringCchPrintfW(wz, countof(wz), L"1: %d 2: %d 3: %d 4: %d", iFields[0], iFields[1], iFields[2], iFields[3]); + Trace(REPORT_STANDARD, "MSI progress[%x]: %ls", pContext->dwCurrentProgressIndex, wz); +#endif + + // Verify that we have the enough field values. + if (1 > cFields) + { + ExitFunction(); // unknown message, bail + } + + // Handle based on message type. + switch (iFields[0]) + { + case 0: // master progress reset + if (4 > cFields) + { + Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); + ExitFunction(); + } + //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - MASTER RESET - %d, %d, %d", iFields[1], iFields[2], iFields[3]); + + // Update the index into progress array. + if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex) + { + pContext->dwCurrentProgressIndex = 0; + } + else if (pContext->dwCurrentProgressIndex + 1 < countof(pContext->rgMsiProgress)) + { + ++pContext->dwCurrentProgressIndex; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + WiuExitOnRootFailure(hr, "Insufficient space to hold progress information."); + } + + // we only care about the first stage after script execution has started + //if (!pEngineInfo->fMsiProgressScriptInProgress && 1 != iFields[3]) + //{ + // pEngineInfo->fMsiProgressFinished = TRUE; + //} + + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal = iFields[1]; + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted = 0 == iFields[2] ? 0 : iFields[1]; // if forward start at 0, if backwards start at max + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward = (0 == iFields[2]); + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fScriptInProgress = (1 == iFields[3]); + + if (0 == pContext->dwCurrentProgressIndex) + { + // HACK!!! this is a hack courtesy of the Windows Installer team. It seems the script planning phase + // is always off by "about 50". So we'll toss an extra 50 ticks on so that the standard progress + // doesn't go over 100%. If there are any custom actions, they may blow the total so we'll call this + // "close" and deal with the rest. + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += 50; + } + break; + + case 1: // action info. + if (3 > cFields) + { + Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); + ExitFunction(); + } + //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - ACTION INFO - %d, %d, %d", iFields[1], iFields[2], iFields[3]); + + if (0 == iFields[2]) + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; + } + else + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = TRUE; + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep = iFields[1]; + } + break; + + case 2: // progress report. + if (2 > cFields) + { + Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); + break; + } + + //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - PROGRESS REPORT - %d, %d, %d", iFields[1], iFields[2], iFields[3]); + + if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex) + { + break; + } + else if (0 == pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal) + { + break; + } + + // Update progress. + if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward) + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += iFields[1]; + } + else // rollback. + { + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= iFields[1]; + } + break; + + case 3: // extend the progress bar. + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += iFields[1]; + break; + + default: + ExitFunction(); // unknown message, bail + } + + // If we have a valid progress index, send an update. + if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex) + { + nResult = SendProgressUpdate(pContext); + } + +LExit: + return nResult; +} + +static INT SendMsiMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ) +{ + INT nResult = IDNOACTION; + WIU_MSI_EXECUTE_MESSAGE message = { }; + LPWSTR* rgsczData = NULL; + DWORD cData = 0; + + InitializeMessageData(hRecord, &rgsczData, &cData); + + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE; + message.dwAllowedResults = uiFlags; + message.cData = cData; + message.rgwzData = (LPCWSTR*)rgsczData; + message.msiMessage.mt = mt; + message.msiMessage.wzMessage = wzMessage; + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); + + UninitializeMessageData(rgsczData, cData); + return nResult; +} + +static INT SendErrorMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in_opt MSIHANDLE hRecord + ) +{ + INT nResult = IDNOACTION; + WIU_MSI_EXECUTE_MESSAGE message = { }; + DWORD dwErrorCode = 0; + LPWSTR* rgsczData = NULL; + DWORD cData = 0; + + if (hRecord) + { + dwErrorCode = ::MsiRecordGetInteger(hRecord, 1); + + // Set the recommendation if it's a known error code. + switch (dwErrorCode) + { + case 1605: // continue with install even if there isn't enough room for rollback. + nResult = IDIGNORE; + break; + + case 1704: // rollback suspended installs so our install can continue. + nResult = IDOK; + break; + } + } + + InitializeMessageData(hRecord, &rgsczData, &cData); + + message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR; + message.dwAllowedResults = uiFlags; + message.nResultRecommendation = nResult; + message.cData = cData; + message.rgwzData = (LPCWSTR*)rgsczData; + message.error.dwErrorCode = dwErrorCode; + message.error.wzMessage = wzMessage; + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); + + UninitializeMessageData(rgsczData, cData); + return nResult; +} + +static INT SendFilesInUseMessage( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in_opt MSIHANDLE hRecord, + __in BOOL /*fRestartManagerRequest*/ + ) +{ + INT nResult = IDNOACTION; + WIU_MSI_EXECUTE_MESSAGE message = { }; + LPWSTR* rgsczData = NULL; + DWORD cData = 0; + + InitializeMessageData(hRecord, &rgsczData, &cData); + + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE; + message.dwAllowedResults = WIU_MB_OKIGNORECANCELRETRY; + message.cData = cData; + message.rgwzData = (LPCWSTR*)rgsczData; + message.msiFilesInUse.cFiles = message.cData; // point the files in use information to the message record information. + message.msiFilesInUse.rgwzFiles = message.rgwzData; + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); + + UninitializeMessageData(rgsczData, cData); + return nResult; +} + +static INT SendProgressUpdate( + __in WIU_MSI_EXECUTE_CONTEXT* pContext + ) +{ + int nResult = IDNOACTION; + DWORD dwPercentage = 0; // number representing 0 - 100% + WIU_MSI_EXECUTE_MESSAGE message = { }; + + //DWORD dwMsiProgressTotal = pEngineInfo->dwMsiProgressTotal; + //DWORD dwMsiProgressComplete = pEngineInfo->dwMsiProgressComplete; //min(dwMsiProgressTotal, pEngineInfo->dwMsiProgressComplete); + //double dProgressGauge = 0; + //double dProgressStageTotal = (double)pEngineInfo->qwProgressStageTotal; + + // Calculate progress for the phases of Windows Installer. + // TODO: handle upgrade progress which would add another phase. + dwPercentage += CalculatePhaseProgress(pContext, 0, 15); + dwPercentage += CalculatePhaseProgress(pContext, 1, 80); + dwPercentage += CalculatePhaseProgress(pContext, 2, 5); + dwPercentage = min(dwPercentage, 100); // ensure the percentage never goes over 100%. + + if (pContext->fRollback) + { + dwPercentage = 100 - dwPercentage; + } + + //if (qwTotal) // avoid "divide by zero" if the MSI range is blank. + //{ + // // calculate gauge. + // double dProgressGauge = static_cast(qwCompleted) / static_cast(qwTotal); + // dProgressGauge = (1.0 / (1.0 + exp(3.7 - dProgressGauge * 7.5)) - 0.024127021417669196) / 0.975872978582330804; + // qwCompleted = (DWORD)(dProgressGauge * qwTotal); + + // // calculate progress within range + // //qwProgressComplete = (DWORD64)(dwMsiProgressComplete * (dProgressStageTotal / dwMsiProgressTotal)); + // //qwProgressComplete = min(qwProgressComplete, pEngineInfo->qwProgressStageTotal); + //} + +#ifdef _DEBUG + DWORD64 qwCompleted = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted; + DWORD64 qwTotal = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal; + Trace(REPORT_STANDARD, "MSI progress: %I64u/%I64u (%u%%)", qwCompleted, qwTotal, dwPercentage); + //AssertSz(qwCompleted <= qwTotal, "Completed progress is larger than total progress."); +#endif + + message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = dwPercentage; + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); + + return nResult; +} + +static void ResetProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext + ) +{ + memset(pContext->rgMsiProgress, 0, sizeof(pContext->rgMsiProgress)); + pContext->dwCurrentProgressIndex = WIU_MSI_PROGRESS_INVALID; +} + +static DWORD CalculatePhaseProgress( + __in WIU_MSI_EXECUTE_CONTEXT* pContext, + __in DWORD dwProgressIndex, + __in DWORD dwWeightPercentage + ) +{ + DWORD dwPhasePercentage = 0; + + // If we've already passed this progress index, return the maximum percentage possible (the weight) + if (dwProgressIndex < pContext->dwCurrentProgressIndex) + { + dwPhasePercentage = dwWeightPercentage; + } + else if (dwProgressIndex == pContext->dwCurrentProgressIndex) // have to do the math for the current progress. + { + WIU_MSI_PROGRESS* pProgress = pContext->rgMsiProgress + dwProgressIndex; + if (pProgress->dwTotal) + { + DWORD64 dw64Completed = pProgress->dwCompleted; + dwPhasePercentage = static_cast(dw64Completed * dwWeightPercentage / pProgress->dwTotal); + } + } + // else we're not there yet so it has to be zero. + + return dwPhasePercentage; +} + +void InitializeMessageData( + __in_opt MSIHANDLE hRecord, + __deref_out_ecount(*pcData) LPWSTR** prgsczData, + __out DWORD* pcData + ) +{ + DWORD cData = 0; + LPWSTR* rgsczData = NULL; + + // If we have a record based message, try to get the extra data. + if (hRecord) + { + cData = ::MsiRecordGetFieldCount(hRecord); + if (cData) + { + rgsczData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cData, TRUE); + } + + for (DWORD i = 0; rgsczData && i < cData; ++i) + { + DWORD cch = 0; + + // get string from record +#pragma prefast(push) +#pragma prefast(disable:6298) + DWORD er = ::MsiRecordGetStringW(hRecord, i + 1, L"", &cch); +#pragma prefast(pop) + if (ERROR_MORE_DATA == er) + { + HRESULT hr = StrAlloc(&rgsczData[i], ++cch); + if (SUCCEEDED(hr)) + { + er = ::MsiRecordGetStringW(hRecord, i + 1, rgsczData[i], &cch); + } + } + } + } + + *prgsczData = rgsczData; + *pcData = cData; +} + +void UninitializeMessageData( + __in LPWSTR* rgsczData, + __in DWORD cData + ) +{ + // Clean up if there was any data allocated. + if (rgsczData) + { + for (DWORD i = 0; i < cData; ++i) + { + ReleaseStr(rgsczData[i]); + } + + MemFree(rgsczData); + } +} diff --git a/src/libs/dutil/WixToolset.DUtil/wuautil.cpp b/src/libs/dutil/WixToolset.DUtil/wuautil.cpp new file mode 100644 index 00000000..dfb28818 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/wuautil.cpp @@ -0,0 +1,104 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define WuaExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__) +#define WuaExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_WUAUTIL, p, x, e, s, __VA_ARGS__) +#define WuaExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, p, x, s, __VA_ARGS__) +#define WuaExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_WUAUTIL, p, x, e, s, __VA_ARGS__) +#define WuaExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, p, x, s, __VA_ARGS__) +#define WuaExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_WUAUTIL, e, x, s, __VA_ARGS__) +#define WuaExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_WUAUTIL, g, x, s, __VA_ARGS__) + + +// internal function declarations + +static HRESULT GetAutomaticUpdatesService( + __out IAutomaticUpdates **ppAutomaticUpdates + ); + + +// function definitions + +extern "C" HRESULT DAPI WuaPauseAutomaticUpdates() +{ + HRESULT hr = S_OK; + IAutomaticUpdates *pAutomaticUpdates = NULL; + + hr = GetAutomaticUpdatesService(&pAutomaticUpdates); + WuaExitOnFailure(hr, "Failed to get the Automatic Updates service."); + + hr = pAutomaticUpdates->Pause(); + WuaExitOnFailure(hr, "Failed to pause the Automatic Updates service."); + +LExit: + ReleaseObject(pAutomaticUpdates); + + return hr; +} + +extern "C" HRESULT DAPI WuaResumeAutomaticUpdates() +{ + HRESULT hr = S_OK; + IAutomaticUpdates *pAutomaticUpdates = NULL; + + hr = GetAutomaticUpdatesService(&pAutomaticUpdates); + WuaExitOnFailure(hr, "Failed to get the Automatic Updates service."); + + hr = pAutomaticUpdates->Resume(); + WuaExitOnFailure(hr, "Failed to resume the Automatic Updates service."); + +LExit: + ReleaseObject(pAutomaticUpdates); + + return hr; +} + +extern "C" HRESULT DAPI WuaRestartRequired( + __out BOOL* pfRestartRequired + ) +{ + HRESULT hr = S_OK; + ISystemInformation* pSystemInformation = NULL; + VARIANT_BOOL bRestartRequired; + + hr = ::CoCreateInstance(__uuidof(SystemInformation), NULL, CLSCTX_INPROC_SERVER, __uuidof(ISystemInformation), reinterpret_cast(&pSystemInformation)); + WuaExitOnRootFailure(hr, "Failed to get WUA system information interface."); + + hr = pSystemInformation->get_RebootRequired(&bRestartRequired); + WuaExitOnRootFailure(hr, "Failed to determine if restart is required from WUA."); + + *pfRestartRequired = (VARIANT_FALSE != bRestartRequired); + +LExit: + ReleaseObject(pSystemInformation); + + return hr; +} + + +// internal function definitions + +static HRESULT GetAutomaticUpdatesService( + __out IAutomaticUpdates **ppAutomaticUpdates + ) +{ + HRESULT hr = S_OK; + CLSID clsidAutomaticUpdates = { }; + + hr = ::CLSIDFromProgID(L"Microsoft.Update.AutoUpdate", &clsidAutomaticUpdates); + WuaExitOnFailure(hr, "Failed to get CLSID for Microsoft.Update.AutoUpdate."); + + hr = ::CoCreateInstance(clsidAutomaticUpdates, NULL, CLSCTX_INPROC_SERVER, IID_IAutomaticUpdates, reinterpret_cast(ppAutomaticUpdates)); + WuaExitOnFailure(hr, "Failed to create instance of Microsoft.Update.AutoUpdate."); + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/xmlutil.cpp b/src/libs/dutil/WixToolset.DUtil/xmlutil.cpp new file mode 100644 index 00000000..0f1e611d --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/xmlutil.cpp @@ -0,0 +1,1332 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define XmlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__) +#define XmlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_XMLUTIL, p, x, e, s, __VA_ARGS__) +#define XmlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, p, x, s, __VA_ARGS__) +#define XmlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_XMLUTIL, p, x, e, s, __VA_ARGS__) +#define XmlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, p, x, s, __VA_ARGS__) +#define XmlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_XMLUTIL, e, x, s, __VA_ARGS__) +#define XmlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_XMLUTIL, g, x, s, __VA_ARGS__) + +// intialization globals +CLSID vclsidXMLDOM = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0} }; +static volatile LONG vcXmlInitialized = 0; +static BOOL vfMsxml40 = FALSE; +static BOOL fComInitialized = FALSE; +BOOL vfMsxml30 = FALSE; + +/******************************************************************** + XmlInitialize - finds an appropriate version of the XML DOM + +*********************************************************************/ +extern "C" HRESULT DAPI XmlInitialize( + ) +{ + HRESULT hr = S_OK; + + if (!fComInitialized) + { + hr = ::CoInitialize(0); + if (RPC_E_CHANGED_MODE != hr) + { + XmlExitOnFailure(hr, "failed to initialize COM"); + fComInitialized = TRUE; + } + } + + LONG cInitialized = ::InterlockedIncrement(&vcXmlInitialized); + if (1 == cInitialized) + { + // NOTE: 4.0 behaves differently than 3.0 so there may be problems doing this +#if 0 + hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument.4.0", &vclsidXMLDOM); + if (S_OK == hr) + { + vfMsxml40 = TRUE; + Trace(REPORT_VERBOSE, "found Msxml2.DOMDocument.4.0"); + ExitFunction(); + } +#endif + hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument", &vclsidXMLDOM); + if (FAILED(hr)) + { + // try to fall back to old MSXML + hr = ::CLSIDFromProgID(L"MSXML.DOMDocument", &vclsidXMLDOM); + } + XmlExitOnFailure(hr, "failed to get CLSID for XML DOM"); + + Assert(IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument26) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument40) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument50) || + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument60)); + } + + hr = S_OK; +LExit: + return hr; +} + + +/******************************************************************** + XmUninitialize - + +*********************************************************************/ +extern "C" void DAPI XmlUninitialize( + ) +{ + AssertSz(vcXmlInitialized, "XmlUninitialize called when not initialized"); + + LONG cInitialized = ::InterlockedDecrement(&vcXmlInitialized); + + if (0 == cInitialized) + { + memset(&vclsidXMLDOM, 0, sizeof(vclsidXMLDOM)); + + if (fComInitialized) + { + ::CoUninitialize(); + } + } +} + +extern "C" HRESULT DAPI XmlCreateElement( + __in IXMLDOMDocument *pixdDocument, + __in_z LPCWSTR wzElementName, + __out IXMLDOMElement **ppixnElement + ) +{ + if (!ppixnElement || !pixdDocument) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + BSTR bstrElementName = ::SysAllocString(wzElementName); + XmlExitOnNull(bstrElementName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + hr = pixdDocument->createElement(bstrElementName, ppixnElement); +LExit: + ReleaseBSTR(bstrElementName); + return hr; +} + + +/******************************************************************** + XmlCreateDocument - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlCreateDocument( + __in_opt LPCWSTR pwzElementName, + __out IXMLDOMDocument** ppixdDocument, + __out_opt IXMLDOMElement** ppixeRootElement + ) +{ + HRESULT hr = S_OK; + BOOL (WINAPI *pfnDisableWow64)(__out PVOID* ) = NULL; + BOOLEAN (WINAPI *pfnEnableWow64)(__in BOOLEAN ) = NULL; + BOOL (WINAPI *pfnRevertWow64)(__in PVOID ) = NULL; + BOOL fWow64Available = FALSE; + void *pvWow64State = NULL; + + // RELEASEME + IXMLDOMElement* pixeRootElement = NULL; + IXMLDOMDocument *pixdDocument = NULL; + + // Test if we have access to the Wow64 API, and store the result in fWow64Available + HMODULE hKernel32 = ::GetModuleHandleA("kernel32.dll"); + XmlExitOnNullWithLastError(hKernel32, hr, "failed to get handle to kernel32.dll"); + + // This will test if we have access to the Wow64 API + if (NULL != GetProcAddress(hKernel32, "IsWow64Process")) + { + pfnDisableWow64 = (BOOL (WINAPI *)(PVOID *))::GetProcAddress(hKernel32, "Wow64DisableWow64FsRedirection"); + pfnEnableWow64 = (BOOLEAN (WINAPI *)(BOOLEAN))::GetProcAddress(hKernel32, "Wow64EnableWow64FsRedirection"); + pfnRevertWow64 = (BOOL (WINAPI *)(PVOID))::GetProcAddress(hKernel32, "Wow64RevertWow64FsRedirection"); + + fWow64Available = pfnDisableWow64 && pfnEnableWow64 && pfnRevertWow64; + } + + // create the top level XML document + AssertSz(vcXmlInitialized, "XmlInitialize() was not called"); + + // Enable Wow64 Redirection, if possible + if (fWow64Available) + { + // We want to enable Wow64 redirection, but the Wow64 API requires us to disable it first to get its current state (so we can revert it later) + pfnDisableWow64(&pvWow64State); + // If we fail to enable it, don't bother trying to disable it later on + fWow64Available = pfnEnableWow64(TRUE); + } + + hr = ::CoCreateInstance(vclsidXMLDOM, NULL, CLSCTX_INPROC_SERVER, XmlUtil_IID_IXMLDOMDocument, (void**)&pixdDocument); + XmlExitOnFailure(hr, "failed to create XML DOM Document"); + Assert(pixdDocument); + + if (IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) || IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20)) + { + vfMsxml30 = TRUE; + } + + if (pwzElementName) + { + hr = XmlCreateElement(pixdDocument, pwzElementName, &pixeRootElement); + XmlExitOnFailure(hr, "failed XmlCreateElement"); + hr = pixdDocument->appendChild(pixeRootElement, NULL); + XmlExitOnFailure(hr, "failed appendChild"); + } + + *ppixdDocument = pixdDocument; + pixdDocument = NULL; + + if (ppixeRootElement) + { + *ppixeRootElement = pixeRootElement; + pixeRootElement = NULL; + } + +LExit: + // Re-disable Wow64 Redirection, if appropriate + if (fWow64Available && !pfnRevertWow64(pvWow64State)) + { + // If we expected to be able to revert, and couldn't, fail in the only graceful way we can + ::ExitProcess(1); + } + + ReleaseObject(pixeRootElement); + ReleaseObject(pixdDocument); + return hr; +} + + +/******************************************************************** + XmlLoadDocument - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocument( + __in_z LPCWSTR wzDocument, + __out IXMLDOMDocument** ppixdDocument + ) +{ + return XmlLoadDocumentEx(wzDocument, 0, ppixdDocument); +} + + +/******************************************************************** + XmlReportParseError - + +*********************************************************************/ +static void XmlReportParseError( + __in IXMLDOMParseError* pixpe + ) +{ + HRESULT hr = S_OK; + long lNumber = 0; + BSTR bstr = NULL; + + Trace(REPORT_STANDARD, "Failed to parse XML. IXMLDOMParseError reports:"); + + hr = pixpe->get_errorCode(&lNumber); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.errorCode."); + Trace(REPORT_STANDARD, "errorCode = 0x%x", lNumber); + + hr = pixpe->get_filepos(&lNumber); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.filepos."); + Trace(REPORT_STANDARD, "filepos = %d", lNumber); + + hr = pixpe->get_line(&lNumber); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.line."); + Trace(REPORT_STANDARD, "line = %d", lNumber); + + hr = pixpe->get_linepos(&lNumber); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.linepos."); + Trace(REPORT_STANDARD, "linepos = %d", lNumber); + + hr = pixpe->get_reason(&bstr); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.reason."); + Trace(REPORT_STANDARD, "reason = %ls", bstr); + ReleaseNullBSTR(bstr); + + hr = pixpe->get_srcText (&bstr); + XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.srcText ."); + Trace(REPORT_STANDARD, "srcText = %ls", bstr); + ReleaseNullBSTR(bstr); + +LExit: + ReleaseBSTR(bstr); +} + +/******************************************************************** + XmlLoadDocumentEx - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocumentEx( + __in_z LPCWSTR wzDocument, + __in DWORD dwAttributes, + __out IXMLDOMDocument** ppixdDocument + ) +{ + HRESULT hr = S_OK; + VARIANT_BOOL vbSuccess = 0; + + // RELEASEME + IXMLDOMDocument* pixd = NULL; + IXMLDOMParseError* pixpe = NULL; + BSTR bstrLoad = NULL; + + if (!wzDocument || !*wzDocument) + { + hr = E_UNEXPECTED; + XmlExitOnFailure(hr, "string must be non-null"); + } + + hr = XmlCreateDocument(NULL, &pixd); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed XmlCreateDocument"); + + if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE) + { + hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE); + XmlExitOnFailure(hr, "failed put_preserveWhiteSpace"); + } + + // Security issue. Avoid triggering anything external. + hr = pixd->put_validateOnParse(VARIANT_FALSE); + XmlExitOnFailure(hr, "failed put_validateOnParse"); + hr = pixd->put_resolveExternals(VARIANT_FALSE); + XmlExitOnFailure(hr, "failed put_resolveExternals"); + + bstrLoad = ::SysAllocString(wzDocument); + XmlExitOnNull(bstrLoad, hr, E_OUTOFMEMORY, "failed to allocate bstr for Load in XmlLoadDocumentEx"); + + hr = pixd->loadXML(bstrLoad, &vbSuccess); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); + } + + if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe)) + { + XmlReportParseError(pixpe); + } + + XmlExitOnFailure(hr, "failed loadXML"); + + + hr = S_OK; +LExit: + if (ppixdDocument) + { + *ppixdDocument = pixd; + pixd = NULL; + } + ReleaseBSTR(bstrLoad); + ReleaseObject(pixd); + ReleaseObject(pixpe); + + return hr; +} + + +/******************************************************************* + XmlLoadDocumentFromFile + +********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocumentFromFile( + __in_z LPCWSTR wzPath, + __out IXMLDOMDocument** ppixdDocument + ) +{ + return XmlLoadDocumentFromFileEx(wzPath, 0, ppixdDocument); +} + + +/******************************************************************* + XmlLoadDocumentFromFileEx + +********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocumentFromFileEx( + __in_z LPCWSTR wzPath, + __in DWORD dwAttributes, + __out IXMLDOMDocument** ppixdDocument + ) +{ + HRESULT hr = S_OK; + VARIANT varPath; + VARIANT_BOOL vbSuccess = 0; + + IXMLDOMDocument* pixd = NULL; + IXMLDOMParseError* pixpe = NULL; + + ::VariantInit(&varPath); + varPath.vt = VT_BSTR; + varPath.bstrVal = ::SysAllocString(wzPath); + XmlExitOnNull(varPath.bstrVal, hr, E_OUTOFMEMORY, "failed to allocate bstr for Path in XmlLoadDocumentFromFileEx"); + + hr = XmlCreateDocument(NULL, &pixd); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed XmlCreateDocument"); + + if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE) + { + hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE); + XmlExitOnFailure(hr, "failed put_preserveWhiteSpace"); + } + + // Avoid triggering anything external. + hr = pixd->put_validateOnParse(VARIANT_FALSE); + XmlExitOnFailure(hr, "failed put_validateOnParse"); + hr = pixd->put_resolveExternals(VARIANT_FALSE); + XmlExitOnFailure(hr, "failed put_resolveExternals"); + + pixd->put_async(VARIANT_FALSE); + hr = pixd->load(varPath, &vbSuccess); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); + } + + if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe)) + { + XmlReportParseError(pixpe); + } + + XmlExitOnFailure(hr, "failed to load XML from: %ls", wzPath); + + if (ppixdDocument) + { + *ppixdDocument = pixd; + pixd = NULL; + } + + hr = S_OK; +LExit: + ReleaseVariant(varPath); + ReleaseObject(pixd); + ReleaseObject(pixpe); + + return hr; +} + + +/******************************************************************** + XmlLoadDocumentFromBuffer + +*********************************************************************/ +extern "C" HRESULT DAPI XmlLoadDocumentFromBuffer( + __in_bcount(cbSource) const BYTE* pbSource, + __in SIZE_T cbSource, + __out IXMLDOMDocument** ppixdDocument + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixdDocument = NULL; + SAFEARRAY sa = { }; + VARIANT vtXmlSource; + VARIANT_BOOL vbSuccess = 0; + + ::VariantInit(&vtXmlSource); + + // create document + hr = XmlCreateDocument(NULL, &pixdDocument); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed XmlCreateDocument"); + + // Security issue. Avoid triggering anything external. + hr = pixdDocument->put_validateOnParse(VARIANT_FALSE); + XmlExitOnFailure(hr, "failed put_validateOnParse"); + hr = pixdDocument->put_resolveExternals(VARIANT_FALSE); + XmlExitOnFailure(hr, "failed put_resolveExternals"); + + // load document + sa.cDims = 1; + sa.fFeatures = FADF_STATIC | FADF_FIXEDSIZE; + sa.cbElements = 1; + sa.pvData = (PVOID)pbSource; + sa.rgsabound[0].cElements = (ULONG)cbSource; + vtXmlSource.vt = VT_ARRAY | VT_UI1; + vtXmlSource.parray = &sa; + + hr = pixdDocument->load(vtXmlSource, &vbSuccess); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); + } + XmlExitOnFailure(hr, "failed loadXML"); + + // return value + *ppixdDocument = pixdDocument; + pixdDocument = NULL; + +LExit: + ReleaseObject(pixdDocument); + return hr; +} + + +/******************************************************************** + XmlSetAttribute - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSetAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __in_z LPCWSTR pwzAttributeValue + ) +{ + HRESULT hr = S_OK; + VARIANT varAttributeValue; + ::VariantInit(&varAttributeValue); + + // RELEASEME + IXMLDOMDocument* pixdDocument = NULL; + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMAttribute* pixaAttribute = NULL; + IXMLDOMNode* pixaNode = NULL; + BSTR bstrAttributeName = ::SysAllocString(pwzAttribute); + XmlExitOnNull(bstrAttributeName, hr, E_OUTOFMEMORY, "failed to allocate bstr for AttributeName in XmlSetAttribute"); + + hr = pixnNode->get_attributes(&pixnnmAttributes); + XmlExitOnFailure(hr, "failed get_attributes in XmlSetAttribute(%ls)", pwzAttribute); + + hr = pixnNode->get_ownerDocument(&pixdDocument); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); + + hr = pixdDocument->createAttribute(bstrAttributeName, &pixaAttribute); + XmlExitOnFailure(hr, "failed createAttribute in XmlSetAttribute(%ls)", pwzAttribute); + + varAttributeValue.vt = VT_BSTR; + varAttributeValue.bstrVal = ::SysAllocString(pwzAttributeValue); + if (!varAttributeValue.bstrVal) + { + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); + } + XmlExitOnFailure(hr, "failed SysAllocString in XmlSetAttribute"); + + hr = pixaAttribute->put_nodeValue(varAttributeValue); + XmlExitOnFailure(hr, "failed put_nodeValue in XmlSetAttribute(%ls)", pwzAttribute); + + hr = pixnnmAttributes->setNamedItem(pixaAttribute, &pixaNode); + XmlExitOnFailure(hr, "failed setNamedItem in XmlSetAttribute(%ls)", pwzAttribute); + +LExit: + ReleaseObject(pixdDocument); + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixaAttribute); + ReleaseObject(pixaNode); + ReleaseBSTR(varAttributeValue.bstrVal); + ReleaseBSTR(bstrAttributeName); + + return hr; +} + + +/******************************************************************** + XmlSelectSingleNode - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSelectSingleNode( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR wzXPath, + __out IXMLDOMNode **ppixnChild + ) +{ + HRESULT hr = S_OK; + + BSTR bstrXPath = NULL; + + XmlExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectSingleNode"); + XmlExitOnNull(ppixnChild, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectSingleNode"); + + bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L""); + XmlExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectSingleNode"); + + hr = pixnParent->selectSingleNode(bstrXPath, ppixnChild); + +LExit: + ReleaseBSTR(bstrXPath); + + return hr; +} + + +/******************************************************************** + XmlCreateTextNode - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlCreateTextNode( + __in IXMLDOMDocument *pixdDocument, + __in_z LPCWSTR wzText, + __out IXMLDOMText **ppixnTextNode + ) +{ + if (!ppixnTextNode || !pixdDocument) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + BSTR bstrText = ::SysAllocString(wzText); + XmlExitOnNull(bstrText, hr, E_OUTOFMEMORY, "failed SysAllocString"); + hr = pixdDocument->createTextNode(bstrText, ppixnTextNode); +LExit: + ReleaseBSTR(bstrText); + + return hr; +} + + +/******************************************************************** + XmlGetText + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetText( + __in IXMLDOMNode* pixnNode, + __deref_out_z BSTR* pbstrText + ) +{ + return pixnNode->get_text(pbstrText); +} + + +/******************************************************************** + XmlGetAttribute + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __deref_out_z BSTR* pbstrAttributeValue + ) +{ + Assert(pixnNode); + HRESULT hr = S_OK; + + // RELEASEME + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNode* pixnAttribute = NULL; + VARIANT varAttributeValue; + BSTR bstrAttribute = SysAllocString(pwzAttribute); + + // INIT + ::VariantInit(&varAttributeValue); + + // get attribute value from source + hr = pixnNode->get_attributes(&pixnnmAttributes); + XmlExitOnFailure(hr, "failed get_attributes"); + + hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute); + if (S_FALSE == hr) + { + // hr = E_FAIL; + ExitFunction(); + } + XmlExitOnFailure(hr, "failed getNamedItem in XmlGetAttribute(%ls)", pwzAttribute); + + hr = pixnAttribute->get_nodeValue(&varAttributeValue); + XmlExitOnFailure(hr, "failed get_nodeValue in XmlGetAttribute(%ls)", pwzAttribute); + + // steal the BSTR from the VARIANT + if (S_OK == hr && pbstrAttributeValue) + { + *pbstrAttributeValue = varAttributeValue.bstrVal; + varAttributeValue.bstrVal = NULL; + } + +LExit: + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixnAttribute); + ReleaseVariant(varAttributeValue); + ReleaseBSTR(bstrAttribute); + + return hr; +} + + +/******************************************************************** + XmlGetAttributeEx + +*********************************************************************/ +HRESULT DAPI XmlGetAttributeEx( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR wzAttribute, + __deref_out_z LPWSTR* psczAttributeValue + ) +{ + Assert(pixnNode); + HRESULT hr = S_OK; + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + IXMLDOMNode* pixnAttribute = NULL; + VARIANT varAttributeValue; + BSTR bstrAttribute = NULL; + + ::VariantInit(&varAttributeValue); + + // get attribute value from source + hr = pixnNode->get_attributes(&pixnnmAttributes); + XmlExitOnFailure(hr, "Failed get_attributes."); + + bstrAttribute = ::SysAllocString(wzAttribute); + XmlExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "Failed to allocate attribute name BSTR."); + + hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute); + if (S_FALSE == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + XmlExitOnFailure(hr, "Failed getNamedItem in XmlGetAttribute(%ls)", wzAttribute); + + hr = pixnAttribute->get_nodeValue(&varAttributeValue); + if (S_FALSE == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + XmlExitOnFailure(hr, "Failed get_nodeValue in XmlGetAttribute(%ls)", wzAttribute); + + // copy value + hr = StrAllocString(psczAttributeValue, varAttributeValue.bstrVal, 0); + XmlExitOnFailure(hr, "Failed to copy attribute value."); + +LExit: + ReleaseObject(pixnnmAttributes); + ReleaseObject(pixnAttribute); + ReleaseVariant(varAttributeValue); + ReleaseBSTR(bstrAttribute); + + return hr; +} + + +/******************************************************************** + XmlGetYesNoAttribute + +*********************************************************************/ +HRESULT DAPI XmlGetYesNoAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR wzAttribute, + __out BOOL* pfYes + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + hr = XmlGetAttributeEx(pixnNode, wzAttribute, &sczValue); + if (E_NOTFOUND != hr) + { + XmlExitOnFailure(hr, "Failed to get attribute."); + + *pfYes = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczValue, -1, L"yes", -1); + } + +LExit: + ReleaseStr(sczValue); + + return hr; +} + + + +/******************************************************************** + XmlGetAttributeNumber + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetAttributeNumber( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __out DWORD* pdwValue + ) +{ + HRESULT hr = XmlGetAttributeNumberBase(pixnNode, pwzAttribute, 10, pdwValue); + return hr; +} + + +/******************************************************************** + XmlGetAttributeNumberBase + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetAttributeNumberBase( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __in int nBase, + __out DWORD* pdwValue + ) +{ + HRESULT hr = S_OK; + BSTR bstrPointer = NULL; + + hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrPointer); + XmlExitOnFailure(hr, "Failed to get value from attribute."); + + if (S_OK == hr) + { + *pdwValue = wcstoul(bstrPointer, NULL, nBase); + } + +LExit: + ReleaseBSTR(bstrPointer); + return hr; +} + + +/******************************************************************** + XmlGetAttributeLargeNumber + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetAttributeLargeNumber( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute, + __out DWORD64* pdw64Value + ) +{ + HRESULT hr = S_OK; + BSTR bstrValue = NULL; + + hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrValue); + XmlExitOnFailure(hr, "failed XmlGetAttribute"); + + if (S_OK == hr) + { + LONGLONG ll = 0; + hr = StrStringToInt64(bstrValue, 0, &ll); + XmlExitOnFailure(hr, "Failed to treat attribute value as number."); + + *pdw64Value = ll; + } + else + { + *pdw64Value = 0; + } + +LExit: + ReleaseBSTR(bstrValue); + return hr; +} + + +/******************************************************************** + XmlGetNamedItem - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlGetNamedItem( + __in IXMLDOMNamedNodeMap *pixnmAttributes, + __in_opt LPCWSTR wzName, + __out IXMLDOMNode **ppixnNamedItem + ) +{ + if (!pixnmAttributes || !ppixnNamedItem) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + BSTR bstrName = ::SysAllocString(wzName); + XmlExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString"); + + hr = pixnmAttributes->getNamedItem(bstrName, ppixnNamedItem); + +LExit: + ReleaseBSTR(bstrName); + return hr; +} + + +/******************************************************************** + XmlSetText - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSetText( + __in IXMLDOMNode *pixnNode, + __in_z LPCWSTR pwzText + ) +{ + Assert(pixnNode && pwzText); + HRESULT hr = S_OK; + DOMNodeType dnType; + + // RELEASEME + IXMLDOMDocument* pixdDocument = NULL; + IXMLDOMNodeList* pixnlNodeList = NULL; + IXMLDOMNode* pixnChildNode = NULL; + IXMLDOMText* pixtTextNode = NULL; + VARIANT varText; + + ::VariantInit(&varText); + + // find the text node + hr = pixnNode->get_childNodes(&pixnlNodeList); + XmlExitOnFailure(hr, "failed to get child nodes"); + + while (S_OK == (hr = pixnlNodeList->nextNode(&pixnChildNode))) + { + hr = pixnChildNode->get_nodeType(&dnType); + XmlExitOnFailure(hr, "failed to get node type"); + + if (NODE_TEXT == dnType) + break; + ReleaseNullObject(pixnChildNode); + } + if (S_FALSE == hr) + { + hr = S_OK; + } + + if (pixnChildNode) + { + varText.vt = VT_BSTR; + varText.bstrVal = ::SysAllocString(pwzText); + if (!varText.bstrVal) + { + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); + } + XmlExitOnFailure(hr, "failed SysAllocString in XmlSetText"); + + hr = pixnChildNode->put_nodeValue(varText); + XmlExitOnFailure(hr, "failed IXMLDOMNode::put_nodeValue"); + } + else + { + hr = pixnNode->get_ownerDocument(&pixdDocument); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute"); + + hr = XmlCreateTextNode(pixdDocument, pwzText, &pixtTextNode); + XmlExitOnFailure(hr, "failed createTextNode in XmlSetText(%ls)", pwzText); + + hr = pixnNode->appendChild(pixtTextNode, NULL); + XmlExitOnFailure(hr, "failed appendChild in XmlSetText(%ls)", pwzText); + } + + hr = *pwzText ? S_OK : S_FALSE; + +LExit: + ReleaseObject(pixnlNodeList); + ReleaseObject(pixnChildNode); + ReleaseObject(pixdDocument); + ReleaseObject(pixtTextNode); + ReleaseVariant(varText); + return hr; +} + + +/******************************************************************** + XmlSetTextNumber - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSetTextNumber( + __in IXMLDOMNode *pixnNode, + __in DWORD dwValue + ) +{ + HRESULT hr = S_OK; + WCHAR wzValue[12]; + + hr = ::StringCchPrintfW(wzValue, countof(wzValue), L"%u", dwValue); + XmlExitOnFailure(hr, "Failed to format numeric value as string."); + + hr = XmlSetText(pixnNode, wzValue); + +LExit: + return hr; +} + + +/******************************************************************** + XmlCreateChild - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlCreateChild( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR pwzElementType, + __out IXMLDOMNode** ppixnChild + ) +{ + HRESULT hr = S_OK; + + // RELEASEME + IXMLDOMDocument* pixdDocument = NULL; + IXMLDOMNode* pixnChild = NULL; + + hr = pixnParent->get_ownerDocument(&pixdDocument); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed get_ownerDocument"); + + hr = XmlCreateElement(pixdDocument, pwzElementType, (IXMLDOMElement**) &pixnChild); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed createElement"); + + pixnParent->appendChild(pixnChild,NULL); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed appendChild"); + + if (ppixnChild) + { + *ppixnChild = pixnChild; + pixnChild = NULL; + } + +LExit: + ReleaseObject(pixdDocument); + ReleaseObject(pixnChild); + return hr; +} + +/******************************************************************** + XmlRemoveAttribute - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlRemoveAttribute( + __in IXMLDOMNode* pixnNode, + __in_z LPCWSTR pwzAttribute + ) +{ + HRESULT hr = S_OK; + + // RELEASEME + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL; + BSTR bstrAttribute = ::SysAllocString(pwzAttribute); + XmlExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "failed to allocate bstr for attribute in XmlRemoveAttribute"); + + hr = pixnNode->get_attributes(&pixnnmAttributes); + XmlExitOnFailure(hr, "failed get_attributes in RemoveXmlAttribute(%ls)", pwzAttribute); + + hr = pixnnmAttributes->removeNamedItem(bstrAttribute, NULL); + XmlExitOnFailure(hr, "failed removeNamedItem in RemoveXmlAttribute(%ls)", pwzAttribute); + +LExit: + ReleaseObject(pixnnmAttributes); + ReleaseBSTR(bstrAttribute); + + return hr; +} + + +/******************************************************************** + XmlSelectNodes - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSelectNodes( + __in IXMLDOMNode* pixnParent, + __in_z LPCWSTR wzXPath, + __out IXMLDOMNodeList **ppixnlChildren + ) +{ + HRESULT hr = S_OK; + + BSTR bstrXPath = NULL; + + XmlExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectNodes"); + XmlExitOnNull(ppixnlChildren, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectNodes"); + + bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L""); + XmlExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectNodes"); + + hr = pixnParent->selectNodes(bstrXPath, ppixnlChildren); + +LExit: + ReleaseBSTR(bstrXPath); + return hr; +} + + +/******************************************************************** + XmlNextAttribute - returns the next attribute in a node list + + NOTE: pbstrAttribute is optional + returns S_OK if found an element + returns S_FALSE if no element found + returns E_* if something went wrong +********************************************************************/ +extern "C" HRESULT DAPI XmlNextAttribute( + __in IXMLDOMNamedNodeMap* pixnnm, + __out IXMLDOMNode** pixnAttribute, + __deref_opt_out_z_opt BSTR* pbstrAttribute + ) +{ + Assert(pixnnm && pixnAttribute); + + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + DOMNodeType nt; + + // null out the return values + *pixnAttribute = NULL; + if (pbstrAttribute) + { + *pbstrAttribute = NULL; + } + + hr = pixnnm->nextNode(&pixn); + XmlExitOnFailure(hr, "Failed to get next attribute."); + + if (S_OK == hr) + { + hr = pixn->get_nodeType(&nt); + XmlExitOnFailure(hr, "failed to get node type"); + + if (NODE_ATTRIBUTE != nt) + { + hr = E_UNEXPECTED; + XmlExitOnFailure(hr, "Failed to get expected node type back: attribute"); + } + + // if the caller asked for the attribute name + if (pbstrAttribute) + { + hr = pixn->get_baseName(pbstrAttribute); + XmlExitOnFailure(hr, "failed to get attribute name"); + } + + *pixnAttribute = pixn; + pixn = NULL; + } + +LExit: + ReleaseObject(pixn); + return hr; +} + + +/******************************************************************** + XmlNextElement - returns the next element in a node list + + NOTE: pbstrElement is optional + returns S_OK if found an element + returns S_FALSE if no element found + returns E_* if something went wrong +********************************************************************/ +extern "C" HRESULT DAPI XmlNextElement( + __in IXMLDOMNodeList* pixnl, + __out IXMLDOMNode** pixnElement, + __deref_opt_out_z_opt BSTR* pbstrElement + ) +{ + Assert(pixnl && pixnElement); + + HRESULT hr = S_OK; + IXMLDOMNode* pixn = NULL; + DOMNodeType nt; + + // null out the return values + *pixnElement = NULL; + if (pbstrElement) + { + *pbstrElement = NULL; + } + + // + // find the next element in the list + // + while (S_OK == (hr = pixnl->nextNode(&pixn))) + { + hr = pixn->get_nodeType(&nt); + XmlExitOnFailure(hr, "failed to get node type"); + + if (NODE_ELEMENT == nt) + break; + + ReleaseNullObject(pixn); + } + XmlExitOnFailure(hr, "failed to get next element"); + + // if we have a node and the caller asked for the element name + if (pixn && pbstrElement) + { + hr = pixn->get_baseName(pbstrElement); + XmlExitOnFailure(hr, "failed to get element name"); + } + + *pixnElement = pixn; + pixn = NULL; + + hr = *pixnElement ? S_OK : S_FALSE; +LExit: + ReleaseObject(pixn); + return hr; +} + + +/******************************************************************** + XmlRemoveChildren - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlRemoveChildren( + __in IXMLDOMNode* pixnSource, + __in_z LPCWSTR pwzXPath + ) +{ + HRESULT hr = S_OK; + + // RELEASEME + IXMLDOMNodeList* pixnlNodeList = NULL; + IXMLDOMNode* pixnNode = NULL; + IXMLDOMNode* pixnRemoveChild = NULL; + + if (pwzXPath) + { + hr = XmlSelectNodes(pixnSource, pwzXPath, &pixnlNodeList); + XmlExitOnFailure(hr, "failed XmlSelectNodes"); + } + else + { + hr = pixnSource->get_childNodes(&pixnlNodeList); + XmlExitOnFailure(hr, "failed childNodes"); + } + if (S_FALSE == hr) + { + ExitFunction(); + } + + while (S_OK == (hr = pixnlNodeList->nextNode(&pixnNode))) + { + hr = pixnSource->removeChild(pixnNode, &pixnRemoveChild); + XmlExitOnFailure(hr, "failed removeChild"); + + ReleaseNullObject(pixnRemoveChild); + ReleaseNullObject(pixnNode); + } + if (S_FALSE == hr) + { + hr = S_OK; + } + +LExit: + ReleaseObject(pixnlNodeList); + ReleaseObject(pixnNode); + ReleaseObject(pixnRemoveChild); + + return hr; +} + + +/******************************************************************** + XmlSaveDocument - + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSaveDocument( + __in IXMLDOMDocument* pixdDocument, + __inout LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + + // RELEASEME + VARIANT varsDestPath; + + ::VariantInit(&varsDestPath); + varsDestPath.vt = VT_BSTR; + varsDestPath.bstrVal = ::SysAllocString(wzPath); + if (!varsDestPath.bstrVal) + { + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); + } + XmlExitOnFailure(hr, "failed to create BSTR"); + + hr = pixdDocument->save(varsDestPath); + if (hr == S_FALSE) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "failed save in WriteDocument"); + +LExit: + ReleaseVariant(varsDestPath); + return hr; +} + + +/******************************************************************** + XmlSaveDocumentToBuffer + +*********************************************************************/ +extern "C" HRESULT DAPI XmlSaveDocumentToBuffer( + __in IXMLDOMDocument* pixdDocument, + __deref_out_bcount(*pcbDest) BYTE** ppbDest, + __out DWORD* pcbDest + ) +{ + HRESULT hr = S_OK; + IStream* pStream = NULL; + LARGE_INTEGER li = { }; + STATSTG statstg = { }; + BYTE* pbDest = NULL; + ULONG cbRead = 0; + VARIANT vtDestination; + + ::VariantInit(&vtDestination); + + // create stream + hr = ::CreateStreamOnHGlobal(NULL, TRUE, &pStream); + XmlExitOnFailure(hr, "Failed to create stream."); + + // write document to stream + vtDestination.vt = VT_UNKNOWN; + vtDestination.punkVal = (IUnknown*)pStream; + hr = pixdDocument->save(vtDestination); + XmlExitOnFailure(hr, "Failed to save document."); + + // get stream size + hr = pStream->Stat(&statstg, STATFLAG_NONAME); + XmlExitOnFailure(hr, "Failed to get stream size."); + + // allocate buffer + pbDest = static_cast(MemAlloc(statstg.cbSize.LowPart, TRUE)); + XmlExitOnNull(pbDest, hr, E_OUTOFMEMORY, "Failed to allocate destination buffer."); + + // read data from stream + li.QuadPart = 0; + hr = pStream->Seek(li, STREAM_SEEK_SET, NULL); + XmlExitOnFailure(hr, "Failed to seek stream."); + + hr = pStream->Read(pbDest, statstg.cbSize.LowPart, &cbRead); + if (cbRead < statstg.cbSize.LowPart) + { + hr = E_FAIL; + } + XmlExitOnFailure(hr, "Failed to read stream content to buffer."); + + // return value + *ppbDest = pbDest; + pbDest = NULL; + *pcbDest = statstg.cbSize.LowPart; + +LExit: + ReleaseObject(pStream); + ReleaseMem(pbDest); + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/xsd/thmutil.xsd b/src/libs/dutil/WixToolset.DUtil/xsd/thmutil.xsd new file mode 100644 index 00000000..46c20e4a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/xsd/thmutil.xsd @@ -0,0 +1,1188 @@ + + + + + + + + Schema for describing Theme files processed by thmutil. + + + + + + + + + This is the top-level container element for every thmutil Theme file. + + + + + + + + + + + Relative path to an image file that can serve as a single source for images in the rest of the theme. + This image is referenced by controls using the SourceX and SourceY attributes. + Mutually exclusive with the ImageResource attribute. + + + + + + + Identifier that references an image resource in the module for the window. + Mutually exclusive with the ImageFile attribute. + + + + + + + + + Defines a font including the size and color. + + + + + + Name of the font face (required). + + + + Numeric identifier for the font. Due to limitations in thmutil the first Font must start with "0" and each subsequent Font must increment the Id by 1. Failure to ensure the Font identifiers follow this strict ordering will create unexpected behavior or crashes. + + + + + Font size. Use negative numbers to specify the font in pixels. + + + + + Font weight. + + + + + + A system color id or a hexadecimal value representing BGR foreground color of the font. + "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black. + If this attribute is absent the foreground will be transparent. + Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext. + + + + + + + A system color id or a hexadecimal value representing BGR background color of the font. + "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black. + If this attribute is absent the background will be transparent. + Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext. + + + + + + Specifies whether the font is underlined. + + + + + + + + + + List of images which can be shared between multiple controls. + + + + + + + + + Name of the ImageList, to be referenced by other controls. + + + + + + + + + Named set of controls that can be shown and hidden collectively. + + + + + + + Optional name for the page. + + + + + + + + + Defines the overall look of the main window. + + + + + + + + + + Specifies whether the ThmUtil default window proc should process WM_SIZE and WM_SIZING events. + + + + + + Caption for the window. + This is required if not using the StringId attribute. + + + + + + Numeric identifier to the Font element that serves as the default font for the window. + + + + + Height of the window's client area. + + + + + + Hexadecimal window style. If this is not specified the default value is: WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU. + If SourceX and SourceY are specified, then WS_OVERLAPPED is replaced with WS_POPUP. + + + + + + Relative path to an icon file for the window. Mutually exclusive with IconResource and SourceX and SourceY attributes. + + + + + + Identifier that references an icon resource in the module for the icon for the window. + Mutually exclusive with IconFile and SourceX and SourceY attributes. + + + + + + Minimum height of the window. Only functions if AutoResize is enabled. + + + + + Minimum width of the window. Only functions if AutoResize is enabled. + + + + + X offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource. + + + + + Y offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource. + + + + + + Identifier that references a string resource in the module to define the window caption. + Mutually exclusive with the Caption attribute. + + + + + + Width of the window's client area. + + + + + + + + Defines a control that rotates through a set of images on a specified interval. + + + + + + + + + + Specifies the time to wait before showing the next image, in milliseconds. + + + + + + Specifies whether the billboard should loop through the images infinitely. + + + + + + + + Defines a button. + + + + + Text to display in the button. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + If multiple Action elements are given, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and could be changed at any time). + If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute. + + + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons. + + + + + Numeric identifier to the Font element that serves as the font when the control is hovered over. Only valid when using graphic buttons. + + + + + + Relative path to an image file to define a graphic button. + The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused. + Mutually exclusive with ImageResource and SourceX and SourceY attributes. + + + + + + + Identifier that references an image resource in the module to define a graphic button. + The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused. + Mutually exclusive with ImageFile and SourceX and SourceY attributes. + + + + + + Numeric identifier to the Font element that serves as the font when the control is selected. Only valid when using graphic buttons. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + + When the button is pressed, a directory browser dialog is shown. + + + + + + + The condition that determines if the parent control will execute this action. + + + + + + + The name of the variable to update when the user selects a directory from the dialog. + + + + + + + + + + When the button is pressed, the specified page is shown. + + + + + + + When set to 'yes', none of the variable changes made on the current page are saved. + + + + + + + The condition that determines if the parent control will execute this action. + + + + + + + The Name of the Page to show. + + + + + + + + + + When the button is pressed, the WM_CLOSE message is sent to the window. + + + + + + + The condition that determines if the parent control will execute this action. + + + + + + + + + Defines a checkbox. + + + + + Text to display beside the checkbox. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + Defines a combobox. + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + + + Defines a button. + + + + + Text to display in the button. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + If multiple Action elements are given, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and could be changed at any time). + If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute. + + + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons. + + + + + + Relative path to an icon file to define a command link glyph. + Mutually exclusive with ImageResource and SourceX and SourceY attributes. + + + + + + + Identifier that references an icon resource in the module to define a command link glyph. + Mutually exclusive with ImageFile and SourceX and SourceY attributes. + + + + + + + Relative path to an image file to define a command link glyph. + Mutually exclusive with ImageResource and SourceX and SourceY attributes. + + + + + + + Identifier that references an image resource in the module to define a command link glyph. + Mutually exclusive with ImageFile and SourceX and SourceY attributes. + + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + Defines an edit box. + + + + + + + Initial text for the control. + Mutually exclusive with the StringId attribute. + + + + + + Specifies whether the edit box should auto-complete with file system paths. + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the initial text for the control. + + + + + + + + + + + Defines a hyperlink. + + + + + Text to display as the link. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Numeric identifier to the Font element that serves as the unselected font. + + + + + Numeric identifier to the Font element that serves as the font when the control is hovered over. + + + + + Numeric identifier to the Font element that serves as the font when the control is selected. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + Defines a text block with support for HTML <a> tags. + + + + + Text to display as the link. + Use HTML <a href="URL"> to create a link. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + + + + Defines an image for an ImageList or Billboard. + + + + + Relative path to an image file. Mutually exclusive with ImageResource. + + + + + Identifier that references an image resource in the module. Mutually exclusive with ImageFile. + + + + + + + + Defines an image. + + + + + + Relative path to an image file. Mutually exclusive with ImageResource and SourceX and SourceY attributes. + + + + + Identifier that references an image resource in the module. Mutually exclusive with ImageFile and SourceX and SourceY attributes. + + + + + + + + Defines a label. + + + + + Text for the label to display. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Specifies whether the text should be centered horizontally in the width of the control. Default is "no". + + + + + By default ampersands (&) in the text will underline the next character and treat it as an accelerator key. Set this attribute to "yes" to disable that behavior. Default is "no". + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the text for the label. + + + + + + + + + Defines a listview. + + + + + + + + + Numeric identifier to the Font element that serves as the default font for the ListView. + + + + + Hexadecimal extended window style. + + + + + + The name of the ImageList to assign to this listview with type LVSIL_NORMAL. + + + + + + + The name of the ImageList to assign to this listview with type LVSIL_SMALL. + + + + + + + The name of the ImageList to assign to this listview with type LVSIL_STATE. + + + + + + + The name of the ImageList to assign to this listview with type LVSIL_GROUPHEADER. + + + + + + + + + + Defines note text for a command link control based on an optional condition. + If multiple Note elements are given for one control, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and may be changed at any time). + If none of the conditions of a control's Note elements are true, then it uses the text of the Note element without the Condition attribute. + + + + + + + + Note text for the parent command link control. + + + + + + The condition that determines when the parent control will use this note text. + + + + + + + + + + + Defines a collection of controls. + + + + + + + + + + Defines a progress bar. + + + + + + Relative path to an image file for the control. The image must be 4 pixels wide: left pixel is the left side of progress bar, left middle pixel is progress used, right middle pixel is progress unused, right pixel is right side of progress bar. Mutually exclusive with ImageResource and SourceX and SourceY attributes. + + + + + Identifier that references an image resource in the module for the control. The image must be 4 pixels wide: left pixel is the left side of progress bar, left middle pixel is progress used, right middle pixel is progress unused, right pixel is right side of progress bar. Mutually exclusive with ImageFile and SourceX and SourceY attributes. + + + + + + + + Defines an individual radio button within a set of radio buttons. + + + + + Text to display beside the radio button. + Mutually exclusive with the StringId attribute and child Text elements. + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + Identifier that references a string resource in the module to define the text for the control. + + + + + + Optional value used when setting the variable associated with the set of radio buttons. + + + + + + + + Defines a set of radio buttons. + + + + + + + + Optional variable name for the set of radio buttons. + + + + + + + + Defines a rich edit control. + + + + + Initial text for the control. + Mutually exclusive with the StringId attribute. + + + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + + Identifier that references a string resource in the module to define the initial text for the control. + + + + + + + + + Defines a straight line. + + + + + + + + + Defines an individual tab within a set of tabs. + + + + + + + Caption of the tab. + Mutually exclusive with the StringId attribute. + + + + + + Identifier that references a string resource in the module to define the caption of the tab. + + + + + + + + + + + Defines a set of tabs. + + + + + + + + + Numeric identifier to the Font element that serves as the font for the control. + + + + + + + + + Defines text for the parent control based on an optional condition. + If multiple Text elements are given for one control, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and may be changed at any time). + If none of the conditions of a control's Text elements are true, then it uses the text of the Text element without the Condition attribute. + + + + + + + + Text for the parent control. + + + + + + The condition that determines when the parent control will use this text. + + + + + + + + + + + + Defines text for the parent control's tooltip. + + + + + + + + Text for the parent control's tooltip. + + + + + + + + + + Defines a treeview. + + + + + + Specifies whether the row always appears selected even when the treeview has lost focus. + + + + + Specifies whether drag and drop is enabled for the treeview. + + + + + Specifies whether an entire row is selected for the treeview. + + + + + Specifies whether the treeview will show buttons. + + + + + Specifies whether lines appear for all treeview items. + + + + + Specifies whether the root nodes have lines beside them. + + + + + + + + A column of a list. + + + + + + + Text for the column header. + Mutually exclusive with the StringId attribute. + + + + + Width of the column. + + + + + + Whether or not this column can grow to fill available width of the listview. + More than one column can be marked with yes - all expandable columns will share available extra space. + This is especially useful if the Window/@AutoResize is yes. + + + + + + + Identifier that references a string resource in the module to define the text for the column header. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Optional name for the control. + + + + + Set to 'yes' to disable automatic variable getting and setting, EnableCondition, VisibleCondition, and conditional Text elements. The default is 'no'. + + + + + A condition that determines if the control is enabled. If this condition is true or omitted, then the control will be enabled. + + + + + Height of the control. Non-positive values extend the control to the bottom of the window minus the value. + + + + + Hexadecimal window style for the control. + + + + + Specifies whether the control should be hidden when disabled. + + + + + Specifies whether the control is part of the tab sequence of controls. + + + + + Specifies whether the control is initially visible. + + + + + + A condition that determines if the control is visible. If this condition is true or omitted, then the control will be visible. + + + + + + Width of the control. Non-positive values extend the control to the right of the window minus the value. + + + + + X coordinate for the control from the left of the window. Negative values are coordinates from the right of the window minus the width of the control. + + + + + Y coordinate for the control from the top of the window. Negative values are coordinates from the bottom of the window minus the height of the control. + + + + + + + Values of this type will either be "yes" or "no". + + + + + + + + + + + Indicates a system color for a font. + + + + + + + + + + + + + + + + + + Indicates the foreground or background color of a font. + + + + + diff --git a/src/libs/dutil/appveyor.cmd b/src/libs/dutil/appveyor.cmd new file mode 100644 index 00000000..85476b8e --- /dev/null +++ b/src/libs/dutil/appveyor.cmd @@ -0,0 +1,24 @@ +@setlocal +@pushd %~dp0 +@set _C=Release +@if /i "%1"=="debug" set _C=Debug + +nuget restore || exit /b + +msbuild -t:Test -p:Configuration=%_C% src\test\DUtilUnitTest || exit /b + +msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v140 || exit /b +msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v140 || exit /b + +msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v141 || exit /b +msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v141 || exit /b +msbuild -p:Configuration=%_C%;Platform=ARM64;PlatformToolset=v141 || exit /b + +msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v142 || exit /b +msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v142 || exit /b +msbuild -p:Configuration=%_C%;Platform=ARM64;PlatformToolset=v142 || exit /b + +msbuild -p:Configuration=%_C% -t:PackNative src\dutil\dutil.vcxproj || exit /b + +@popd +@endlocal \ No newline at end of file diff --git a/src/libs/dutil/appveyor.yml b/src/libs/dutil/appveyor.yml new file mode 100644 index 00000000..f602d07c --- /dev/null +++ b/src/libs/dutil/appveyor.yml @@ -0,0 +1,44 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml +# then update all of the repos. + +branches: + only: + - master + - develop + +image: Visual Studio 2019 + +version: 0.0.0.{build} +configuration: Release + +environment: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + NUGET_XMLDOC_MODE: skip + +build_script: + - appveyor.cmd + +test: off + +pull_requests: + do_not_increment_build_number: true + +nuget: + disable_publish_on_pr: true + +skip_branch_with_pr: true +skip_tags: true + +artifacts: +- path: build\Release\**\*.nupkg + name: nuget +- path: build\Release\**\*.msi + name: msi + +notifications: +- provider: Slack + incoming_webhook: + secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA= diff --git a/src/libs/dutil/dutil.sln b/src/libs/dutil/dutil.sln new file mode 100644 index 00000000..433f42a5 --- /dev/null +++ b/src/libs/dutil/dutil.sln @@ -0,0 +1,47 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30711.63 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dutil", "src\dutil\dutil.vcxproj", "{1244E671-F108-4334-BA52-8A7517F26ECD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DUtilUnitTest", "src\test\DUtilUnitTest\DUtilUnitTest.vcxproj", "{AB7EE608-E5FB-42A5-831F-0DEEEA141223}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM64.Build.0 = Debug|ARM64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.ActiveCfg = Debug|x64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.Build.0 = Debug|x64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.ActiveCfg = Debug|Win32 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.Build.0 = Debug|Win32 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM64.ActiveCfg = Release|ARM64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM64.Build.0 = Release|ARM64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.ActiveCfg = Release|x64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.Build.0 = Release|x64 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.ActiveCfg = Release|Win32 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.Build.0 = Release|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|ARM64.ActiveCfg = Debug|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x64.ActiveCfg = Debug|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x86.ActiveCfg = Debug|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x86.Build.0 = Debug|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|ARM64.ActiveCfg = Release|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x64.ActiveCfg = Release|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x86.ActiveCfg = Release|Win32 + {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DD209744-C40E-4C34-8CB4-BC6B71F9A133} + EndGlobalSection +EndGlobal diff --git a/src/libs/dutil/nuget.config b/src/libs/dutil/nuget.config new file mode 100644 index 00000000..d5ef8952 --- /dev/null +++ b/src/libs/dutil/nuget.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/libs/dutil/test/DUtilUnitTest/ApupUtilTests.cpp b/src/libs/dutil/test/DUtilUnitTest/ApupUtilTests.cpp new file mode 100644 index 00000000..30a45f5a --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/ApupUtilTests.cpp @@ -0,0 +1,46 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class ApupUtil + { + public: + [Fact] + void AllocChainFromAtomSortsDescending() + { + HRESULT hr = S_OK; + ATOM_FEED* pFeed = NULL; + APPLICATION_UPDATE_CHAIN* pChain = NULL; + + DutilInitialize(&DutilTestTraceError); + + try + { + XmlInitialize(); + NativeAssert::Succeeded(hr, "Failed to initialize Xml."); + + pin_ptr feedFilePath = PtrToStringChars(TestData::Get("TestData", "ApupUtilTests", "FeedBv2.0.xml")); + hr = AtomParseFromFile(feedFilePath, &pFeed); + NativeAssert::Succeeded(hr, "Failed to parse feed: {0}", feedFilePath); + + hr = ApupAllocChainFromAtom(pFeed, &pChain); + NativeAssert::Succeeded(hr, "Failed to get chain from feed."); + + Assert::Equal(3ul, pChain->cEntries); + NativeAssert::StringEqual(L"Bundle v2.0", pChain->rgEntries[0].wzTitle); + NativeAssert::StringEqual(L"Bundle v1.0", pChain->rgEntries[1].wzTitle); + NativeAssert::StringEqual(L"Bundle v1.0-preview", pChain->rgEntries[2].wzTitle); + } + finally + { + DutilUninitialize(); + } + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/AssemblyInfo.cpp b/src/libs/dutil/test/DUtilUnitTest/AssemblyInfo.cpp new file mode 100644 index 00000000..2d527910 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/AssemblyInfo.cpp @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; + +[assembly: AssemblyTitleAttribute("Windows Installer XML Dutil unit tests")]; +[assembly: AssemblyDescriptionAttribute("Dutil unit tests")]; +[assembly: AssemblyCultureAttribute("")]; +[assembly: ComVisible(false)]; diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilTests.cpp b/src/libs/dutil/test/DUtilUnitTest/DUtilTests.cpp new file mode 100644 index 00000000..55e81d46 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/DUtilTests.cpp @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class DUtil + { + public: + [Fact] + void DUtilTraceErrorSourceFiltersOnTraceLevel() + { + DutilInitialize(&DutilTestTraceError); + + CallDutilTraceErrorSource(); + + Dutil_TraceSetLevel(REPORT_DEBUG, FALSE); + + Action^ action = gcnew Action(this, &DUtil::CallDutilTraceErrorSource); + Assert::Throws(action); + + DutilUninitialize(); + } + + private: + void CallDutilTraceErrorSource() + { + Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_DEBUG, DUTIL_SOURCE_EXTERNAL, E_FAIL, "Error message"); + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj new file mode 100644 index 00000000..18410e5d --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj @@ -0,0 +1,99 @@ + + + + + + + + Debug + Win32 + + + Release + Win32 + + + + + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} + {AB7EE608-E5FB-42A5-831F-0DEEEA141223} + DUtilUnitTests + ManagedCProj + DynamicLibrary + Unicode + true + false + + + + + + + ..\..\dutil\inc + rpcrt4.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib + + + + + + + + + + + + + + + + + Create + + 4564;4691 + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\..\packages\WixBuildTools.TestSupport.4.0.47\lib\net472\WixBuildTools.TestSupport.dll + + + ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\lib\net472\WixBuildTools.TestSupport.Native.dll + + + + + + {1244E671-F108-4334-BA52-8A7517F26ECD} + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters new file mode 100644 index 00000000..4df7af89 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters @@ -0,0 +1,80 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/src/libs/dutil/test/DUtilUnitTest/DictUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/DictUtilTest.cpp new file mode 100644 index 00000000..4b4777d7 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/DictUtilTest.cpp @@ -0,0 +1,191 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +const DWORD numIterations = 100000; + +namespace DutilTests +{ + struct Value + { + DWORD dwNum; + LPWSTR sczKey; + }; + + public ref class DictUtil + { + public: + [Fact] + void DictUtilTest() + { + DutilInitialize(&DutilTestTraceError); + + EmbeddedKeyTestHelper(DICT_FLAG_NONE, numIterations); + + EmbeddedKeyTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations); + + StringListTestHelper(DICT_FLAG_NONE, numIterations); + + StringListTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations); + + DutilUninitialize(); + } + + private: + void EmbeddedKeyTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations) + { + HRESULT hr = S_OK; + Value *rgValues = NULL; + Value *valueFound = NULL; + DWORD cValues = 0; + LPWSTR sczExpectedKey = NULL; + STRINGDICT_HANDLE sdValues = NULL; + + try + { + hr = DictCreateWithEmbeddedKey(&sdValues, 0, (void **)&rgValues, offsetof(Value, sczKey), dfFlags); + NativeAssert::Succeeded(hr, "Failed to create dictionary of values"); + + for (DWORD i = 0; i < dwNumIterations; ++i) + { + cValues++; + + hr = MemEnsureArraySize((void **)&rgValues, cValues, sizeof(Value), 5); + NativeAssert::Succeeded(hr, "Failed to grow value array"); + + hr = StrAllocFormatted(&rgValues[i].sczKey, L"%u_a_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i); + + hr = DictAddValue(sdValues, rgValues + i); + NativeAssert::Succeeded(hr, "Failed to add item {0} to dict", i); + } + + for (DWORD i = 0; i < dwNumIterations; ++i) + { + hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i); + + hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); + NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); + + NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey); + + hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i); + + hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); + + if (dfFlags & DICT_FLAG_CASEINSENSITIVE) + { + NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); + + NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey, TRUE); + } + else + { + if (E_NOTFOUND != hr) + { + hr = E_FAIL; + ExitOnFailure(hr, "This embedded key is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey); + } + } + + hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i); + + hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); + if (E_NOTFOUND != hr) + { + hr = E_FAIL; + ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey); + } + } + } + finally + { + for (DWORD i = 0; i < cValues; ++i) + { + ReleaseStr(rgValues[i].sczKey); + } + ReleaseMem(rgValues); + ReleaseStr(sczExpectedKey); + ReleaseDict(sdValues); + } + + LExit: + return; + } + + void StringListTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations) + { + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + LPWSTR sczExpectedKey = NULL; + STRINGDICT_HANDLE sdValues = NULL; + + try + { + hr = DictCreateStringList(&sdValues, 0, dfFlags); + NativeAssert::Succeeded(hr, "Failed to create dictionary of keys"); + + for (DWORD i = 0; i < dwNumIterations; ++i) + { + hr = StrAllocFormatted(&sczKey, L"%u_a_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i); + + hr = DictAddKey(sdValues, sczKey); + NativeAssert::Succeeded(hr, "Failed to add key {0} to dict", i); + } + + for (DWORD i = 0; i < dwNumIterations; ++i) + { + hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i); + + hr = DictKeyExists(sdValues, sczExpectedKey); + NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); + + hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i); + + hr = DictKeyExists(sdValues, sczExpectedKey); + if (dfFlags & DICT_FLAG_CASEINSENSITIVE) + { + NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); + } + else + { + if (E_NOTFOUND != hr) + { + hr = E_FAIL; + ExitOnFailure(hr, "This stringlist dict is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey); + } + } + + hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i); + NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i); + + hr = DictKeyExists(sdValues, sczExpectedKey); + if (E_NOTFOUND != hr) + { + hr = E_FAIL; + ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey); + } + } + } + finally + { + ReleaseStr(sczKey); + ReleaseStr(sczExpectedKey); + ReleaseDict(sdValues); + } + + LExit: + return; + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/DirUtilTests.cpp b/src/libs/dutil/test/DUtilUnitTest/DirUtilTests.cpp new file mode 100644 index 00000000..7643366f --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/DirUtilTests.cpp @@ -0,0 +1,70 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class DirUtil + { + public: + [Fact] + void DirUtilTest() + { + HRESULT hr = S_OK; + LPWSTR sczCurrentDir = NULL; + LPWSTR sczGuid = NULL; + LPWSTR sczFolder = NULL; + LPWSTR sczSubFolder = NULL; + + try + { + hr = GuidCreate(&sczGuid); + NativeAssert::Succeeded(hr, "Failed to create guid."); + + hr = DirGetCurrent(&sczCurrentDir); + NativeAssert::Succeeded(hr, "Failed to get current directory."); + + hr = PathConcat(sczCurrentDir, sczGuid, &sczFolder); + NativeAssert::Succeeded(hr, "Failed to combine current directory: '{0}' with Guid: '{1}'", sczCurrentDir, sczGuid); + + BOOL fExists = DirExists(sczFolder, NULL); + Assert::False(fExists == TRUE); + + hr = PathConcat(sczFolder, L"foo", &sczSubFolder); + NativeAssert::Succeeded(hr, "Failed to combine folder: '%ls' with subfolder: 'foo'", sczFolder); + + hr = DirEnsureExists(sczSubFolder, NULL); + NativeAssert::Succeeded(hr, "Failed to create multiple directories: %ls", sczSubFolder); + + // Test failure to delete non-empty folder. + hr = DirEnsureDelete(sczFolder, FALSE, FALSE); + Assert::Equal(HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY), hr); + + hr = DirEnsureDelete(sczSubFolder, FALSE, FALSE); + NativeAssert::Succeeded(hr, "Failed to delete single directory: %ls", sczSubFolder); + + // Put the directory back and we'll test deleting tree. + hr = DirEnsureExists(sczSubFolder, NULL); + NativeAssert::Succeeded(hr, "Failed to create single directory: %ls", sczSubFolder); + + hr = DirEnsureDelete(sczFolder, FALSE, TRUE); + NativeAssert::Succeeded(hr, "Failed to delete directory tree: %ls", sczFolder); + + // Finally, try to create "C:\" which would normally fail, but we want success + hr = DirEnsureExists(L"C:\\", NULL); + NativeAssert::Succeeded(hr, "Failed to create C:\\"); + } + finally + { + ReleaseStr(sczSubFolder); + ReleaseStr(sczFolder); + ReleaseStr(sczGuid); + ReleaseStr(sczCurrentDir); + } + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/FileUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/FileUtilTest.cpp new file mode 100644 index 00000000..ac071ef2 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/FileUtilTest.cpp @@ -0,0 +1,125 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class FileUtil + { + public: + [Fact(Skip="Skipped until we have a good way to reference ANSI.txt.")] + void FileUtilTest() + { + HRESULT hr = S_OK; + LPWSTR sczTempDir = NULL; + LPWSTR sczFileDir = NULL; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = PathExpand(&sczTempDir, L"%TEMP%\\FileUtilTest\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::Succeeded(hr, "Failed to get temp dir"); + + hr = PathExpand(&sczFileDir, L"%WIX_ROOT%\\examples\\data\\TextEncodings\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::Succeeded(hr, "Failed to get path to encodings file dir"); + + hr = DirEnsureExists(sczTempDir, NULL); + NativeAssert::Succeeded(hr, "Failed to ensure directory exists: {0}", sczTempDir); + + TestFile(sczFileDir, sczTempDir, L"ANSI.txt", 32, FILE_ENCODING_UTF8); + // Big endian not supported today! + //TestFile(sczFileDir, L"UnicodeBENoBOM.txt", 34); + //TestFile(sczFileDir, L"UnicodeBEWithBOM.txt", 34); + TestFile(sczFileDir, sczTempDir, L"UnicodeLENoBOM.txt", 34, FILE_ENCODING_UTF16); + TestFile(sczFileDir, sczTempDir, L"UnicodeLEWithBOM.txt", 34, FILE_ENCODING_UTF16_WITH_BOM); + TestFile(sczFileDir, sczTempDir, L"UTF8WithSignature.txt", 34, FILE_ENCODING_UTF8_WITH_BOM); + + hr = DirEnsureDelete(sczTempDir, TRUE, TRUE); + } + finally + { + ReleaseStr(sczTempDir); + ReleaseStr(sczFileDir); + DutilUninitialize(); + } + } + + private: + void TestFile(LPWSTR wzDir, LPCWSTR wzTempDir, LPWSTR wzFileName, size_t cbExpectedStringLength, FILE_ENCODING feExpectedEncoding) + { + HRESULT hr = S_OK; + LPWSTR sczFullPath = NULL; + LPWSTR sczContents = NULL; + LPWSTR sczOutputPath = NULL; + FILE_ENCODING feEncodingFound = FILE_ENCODING_UNSPECIFIED; + BYTE *pbFile1 = NULL; + DWORD cbFile1 = 0; + BYTE *pbFile2 = NULL; + DWORD cbFile2 = 0; + size_t cbActualStringLength = 0; + + try + { + hr = PathConcat(wzDir, wzFileName, &sczFullPath); + NativeAssert::Succeeded(hr, "Failed to create path to test file: {0}", sczFullPath); + + hr = FileToString(sczFullPath, &sczContents, &feEncodingFound); + hr = E_FAIL; + NativeAssert::Succeeded(hr, "Failed to read text from file: {0}", sczFullPath); + + if (!sczContents) + { + hr = E_FAIL; + NativeAssert::Succeeded(hr, "FileToString() returned NULL for file: {0}", sczFullPath); + } + + hr = ::StringCchLengthW(sczContents, STRSAFE_MAX_CCH, &cbActualStringLength); + NativeAssert::Succeeded(hr, "Failed to get length of text from file: {0}", sczFullPath); + + if (cbActualStringLength != cbExpectedStringLength) + { + hr = E_FAIL; + ExitOnFailure(hr, "FileToString() returned wrong size for file: %ls (expected size %Iu, found size %Iu)", sczFullPath, cbExpectedStringLength, cbActualStringLength); + } + + if (feEncodingFound != feExpectedEncoding) + { + hr = E_FAIL; + ExitOnFailure(hr, "FileToString() returned unexpected encoding type for file: %ls (expected type %u, found type %u)", sczFullPath, feExpectedEncoding, feEncodingFound); + } + + hr = PathConcat(wzTempDir, wzFileName, &sczOutputPath); + NativeAssert::Succeeded(hr, "Failed to get output path"); + + hr = FileFromString(sczOutputPath, 0, sczContents, feExpectedEncoding); + NativeAssert::Succeeded(hr, "Failed to write contents of file back out to disk"); + + hr = FileRead(&pbFile1, &cbFile1, sczFullPath); + NativeAssert::Succeeded(hr, "Failed to read input file as binary"); + + hr = FileRead(&pbFile2, &cbFile2, sczOutputPath); + NativeAssert::Succeeded(hr, "Failed to read output file as binary"); + + if (cbFile1 != cbFile2 || 0 != memcmp(pbFile1, pbFile2, cbFile1)) + { + hr = E_FAIL; + ExitOnFailure(hr, "Outputted file doesn't match input file: \"%ls\" and \"%ls\"", sczFullPath, sczOutputPath); + } + } + finally + { + ReleaseStr(sczOutputPath); + ReleaseStr(sczFullPath); + ReleaseStr(sczContents); + } + + LExit: + return; + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/GuidUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/GuidUtilTest.cpp new file mode 100644 index 00000000..a6e27a09 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/GuidUtilTest.cpp @@ -0,0 +1,60 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class GuidUtil + { + public: + [Fact] + void GuidCreateTest() + { + HRESULT hr = S_OK; + WCHAR wzGuid1[GUID_STRING_LENGTH]; + WCHAR wzGuid2[GUID_STRING_LENGTH]; + + hr = GuidFixedCreate(wzGuid1); + NativeAssert::Succeeded(hr, "Failed to create first guid."); + Guid firstGuid = Guid::Parse(gcnew String(wzGuid1)); + + hr = GuidFixedCreate(wzGuid2); + NativeAssert::Succeeded(hr, "Failed to create second guid."); + Guid secondGuid = Guid::Parse(gcnew String(wzGuid2)); + + NativeAssert::NotStringEqual(wzGuid1, wzGuid2); + NativeAssert::NotEqual(firstGuid, secondGuid); + } + + [Fact] + void GuidCreateSczTest() + { + HRESULT hr = S_OK; + LPWSTR sczGuid1 = NULL; + LPWSTR sczGuid2 = NULL; + + try + { + hr = GuidCreate(&sczGuid1); + NativeAssert::Succeeded(hr, "Failed to create first guid."); + Guid firstGuid = Guid::Parse(gcnew String(sczGuid1)); + + hr = GuidCreate(&sczGuid2); + NativeAssert::Succeeded(hr, "Failed to create second guid."); + Guid secondGuid = Guid::Parse(gcnew String(sczGuid2)); + + NativeAssert::NotStringEqual(sczGuid1, sczGuid2); + NativeAssert::NotEqual(firstGuid, secondGuid); + } + finally + { + ReleaseStr(sczGuid1); + ReleaseStr(sczGuid2); + } + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/IniUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/IniUtilTest.cpp new file mode 100644 index 00000000..946f19c5 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/IniUtilTest.cpp @@ -0,0 +1,345 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +typedef HRESULT (__clrcall *IniFormatParameters)( + INI_HANDLE + ); + +namespace DutilTests +{ + public ref class IniUtil + { + public: + [Fact] + void IniUtilTest() + { + HRESULT hr = S_OK; + LPWSTR sczTempIniFilePath = NULL; + LPWSTR sczTempIniFileDir = NULL; + LPWSTR wzIniContents = L" PlainValue = \t Blah \r\n;CommentHere\r\n[Section1]\r\n ;Another Comment With = Equal Sign\r\nSection1ValueA=Foo\r\n\r\nSection1ValueB=Bar\r\n[Section2]\r\nSection2ValueA=Cha\r\nArray[0]=Arr\r\n"; + LPWSTR wzScriptContents = L"setf ~PlainValue Blah\r\n;CommentHere\r\n\r\nsetf ~Section1\\Section1ValueA Foo\r\n\r\nsetf ~Section1\\Section1ValueB Bar\r\nsetf ~Section2\\Section2ValueA Cha\r\nsetf ~Section2\\Array[0] Arr\r\n"; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = PathExpand(&sczTempIniFilePath, L"%TEMP%\\IniUtilTest\\Test.ini", PATH_EXPAND_ENVIRONMENT); + NativeAssert::Succeeded(hr, "Failed to get path to temp INI file"); + + hr = PathGetDirectory(sczTempIniFilePath, &sczTempIniFileDir); + NativeAssert::Succeeded(hr, "Failed to get directory to temp INI file"); + + hr = DirEnsureDelete(sczTempIniFileDir, TRUE, TRUE); + if (E_PATHNOTFOUND == hr) + { + hr = S_OK; + } + NativeAssert::Succeeded(hr, "Failed to delete IniUtilTest directory: {0}", sczTempIniFileDir); + + hr = DirEnsureExists(sczTempIniFileDir, NULL); + NativeAssert::Succeeded(hr, "Failed to ensure temp directory exists: {0}", sczTempIniFileDir); + + // Tests parsing, then modifying a regular INI file + TestReadThenWrite(sczTempIniFilePath, StandardIniFormat, wzIniContents); + + // Tests programmatically creating from scratch, then parsing an INI file + TestWriteThenRead(sczTempIniFilePath, StandardIniFormat); + + // Tests parsing, then modifying a regular INI file + TestReadThenWrite(sczTempIniFilePath, ScriptFormat, wzScriptContents); + + // Tests programmatically creating from scratch, then parsing an INI file + TestWriteThenRead(sczTempIniFilePath, ScriptFormat); + } + finally + { + ReleaseStr(sczTempIniFilePath); + ReleaseStr(sczTempIniFileDir); + DutilUninitialize(); + } + } + + private: + void AssertValue(INI_HANDLE iniHandle, LPCWSTR wzValueName, LPCWSTR wzValue) + { + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + try + { + hr = IniGetValue(iniHandle, wzValueName, &sczValue); + NativeAssert::Succeeded(hr, "Failed to get ini value: {0}", wzValueName); + + if (0 != wcscmp(sczValue, wzValue)) + { + hr = E_FAIL; + ExitOnFailure(hr, "Expected to find value in INI: '%ls'='%ls' - but found value '%ls' instead", wzValueName, wzValue, sczValue); + } + } + finally + { + ReleaseStr(sczValue); + } + + LExit: + return; + } + + void AssertNoValue(INI_HANDLE iniHandle, LPCWSTR wzValueName) + { + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + try + { + hr = IniGetValue(iniHandle, wzValueName, &sczValue); + if (E_NOTFOUND != hr) + { + if (SUCCEEDED(hr)) + { + hr = E_FAIL; + } + ExitOnFailure(hr, "INI value shouldn't have been found: %ls", wzValueName); + } + } + finally + { + ReleaseStr(sczValue); + } + + LExit: + return; + } + + static HRESULT StandardIniFormat(__inout INI_HANDLE iniHandle) + { + HRESULT hr = S_OK; + + hr = IniSetOpenTag(iniHandle, L"[", L"]"); + NativeAssert::Succeeded(hr, "Failed to set open tag settings on ini handle"); + + hr = IniSetValueStyle(iniHandle, NULL, L"="); + NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle"); + + hr = IniSetCommentStyle(iniHandle, L";"); + NativeAssert::Succeeded(hr, "Failed to set comment style setting on ini handle"); + + return hr; + } + + static HRESULT ScriptFormat(__inout INI_HANDLE iniHandle) + { + HRESULT hr = S_OK; + + hr = IniSetValueStyle(iniHandle, L"setf ~", L" "); + NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle"); + + return hr; + } + + void TestReadThenWrite(LPWSTR wzIniFilePath, IniFormatParameters SetFormat, LPCWSTR wzContents) + { + HRESULT hr = S_OK; + INI_HANDLE iniHandle = NULL; + INI_HANDLE iniHandle2 = NULL; + INI_VALUE *rgValues = NULL; + DWORD cValues = 0; + + try + { + hr = FileWrite(wzIniFilePath, 0, reinterpret_cast(wzContents), lstrlenW(wzContents) * sizeof(WCHAR), NULL); + NativeAssert::Succeeded(hr, "Failed to write out INI file"); + + hr = IniInitialize(&iniHandle); + NativeAssert::Succeeded(hr, "Failed to initialize INI object"); + + hr = SetFormat(iniHandle); + NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); + + hr = IniParse(iniHandle, wzIniFilePath, NULL); + NativeAssert::Succeeded(hr, "Failed to parse INI file"); + + hr = IniGetValueList(iniHandle, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(5, cValues); + + AssertValue(iniHandle, L"PlainValue", L"Blah"); + AssertNoValue(iniHandle, L"PlainValue2"); + AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo"); + AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar"); + AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha"); + AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist"); + AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr"); + + hr = IniSetValue(iniHandle, L"PlainValue2", L"Blah2"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section1\\CreatedValue", L"Woo"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arrmod"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniGetValueList(iniHandle, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(7, cValues); + + AssertValue(iniHandle, L"PlainValue", L"Blah"); + AssertValue(iniHandle, L"PlainValue2", L"Blah2"); + AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo"); + AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar"); + AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha"); + AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist"); + AssertValue(iniHandle, L"Section1\\CreatedValue", L"Woo"); + AssertValue(iniHandle, L"Section2\\Array[0]", L"Arrmod"); + + // Try deleting a value as well + hr = IniSetValue(iniHandle, L"Section1\\Section1ValueB", NULL); + NativeAssert::Succeeded(hr, "Failed to kill value in INI"); + + hr = IniWriteFile(iniHandle, NULL, FILE_ENCODING_UNSPECIFIED); + NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk"); + + ReleaseNullIni(iniHandle); + // Now re-parse the INI we just wrote and make sure it matches the values we expect + hr = IniInitialize(&iniHandle2); + NativeAssert::Succeeded(hr, "Failed to initialize INI object"); + + hr = SetFormat(iniHandle2); + NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); + + hr = IniParse(iniHandle2, wzIniFilePath, NULL); + NativeAssert::Succeeded(hr, "Failed to parse INI file"); + + hr = IniGetValueList(iniHandle2, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(6, cValues); + + AssertValue(iniHandle2, L"PlainValue", L"Blah"); + AssertValue(iniHandle2, L"PlainValue2", L"Blah2"); + AssertValue(iniHandle2, L"Section1\\Section1ValueA", L"Foo"); + AssertNoValue(iniHandle2, L"Section1\\Section1ValueB"); + AssertValue(iniHandle2, L"Section2\\Section2ValueA", L"Cha"); + AssertNoValue(iniHandle2, L"Section1\\ValueDoesntExist"); + AssertValue(iniHandle2, L"Section1\\CreatedValue", L"Woo"); + AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arrmod"); + } + finally + { + ReleaseIni(iniHandle); + ReleaseIni(iniHandle2); + } + } + + void TestWriteThenRead(LPWSTR wzIniFilePath, IniFormatParameters SetFormat) + { + HRESULT hr = S_OK; + INI_HANDLE iniHandle = NULL; + INI_HANDLE iniHandle2 = NULL; + INI_VALUE *rgValues = NULL; + DWORD cValues = 0; + + try + { + hr = FileEnsureDelete(wzIniFilePath); + NativeAssert::Succeeded(hr, "Failed to ensure file is deleted"); + + hr = IniInitialize(&iniHandle); + NativeAssert::Succeeded(hr, "Failed to initialize INI object"); + + hr = SetFormat(iniHandle); + NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); + + hr = IniGetValueList(iniHandle, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(0, cValues); + + hr = IniSetValue(iniHandle, L"Value1", L"BlahTypo"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value2", L"Blah2"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section1\\Value1", L"Section1Value1"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section1\\Value2", L"Section1Value2"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section2\\Value1", L"Section2Value1"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arr"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value3", L"Blah3"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value4", L"Blah4"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value4", NULL); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniSetValue(iniHandle, L"Value1", L"Blah1"); + NativeAssert::Succeeded(hr, "Failed to set value in INI"); + + hr = IniGetValueList(iniHandle, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(8, cValues); + + AssertValue(iniHandle, L"Value1", L"Blah1"); + AssertValue(iniHandle, L"Value2", L"Blah2"); + AssertValue(iniHandle, L"Value3", L"Blah3"); + AssertNoValue(iniHandle, L"Value4"); + AssertValue(iniHandle, L"Section1\\Value1", L"Section1Value1"); + AssertValue(iniHandle, L"Section1\\Value2", L"Section1Value2"); + AssertValue(iniHandle, L"Section2\\Value1", L"Section2Value1"); + AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr"); + + hr = IniWriteFile(iniHandle, wzIniFilePath, FILE_ENCODING_UNSPECIFIED); + NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk"); + + ReleaseNullIni(iniHandle); + // Now re-parse the INI we just wrote and make sure it matches the values we expect + hr = IniInitialize(&iniHandle2); + NativeAssert::Succeeded(hr, "Failed to initialize INI object"); + + hr = SetFormat(iniHandle2); + NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); + + hr = IniParse(iniHandle2, wzIniFilePath, NULL); + NativeAssert::Succeeded(hr, "Failed to parse INI file"); + + hr = IniGetValueList(iniHandle2, &rgValues, &cValues); + NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); + + NativeAssert::Equal(7, cValues); + + AssertValue(iniHandle2, L"Value1", L"Blah1"); + AssertValue(iniHandle2, L"Value2", L"Blah2"); + AssertValue(iniHandle2, L"Value3", L"Blah3"); + AssertNoValue(iniHandle2, L"Value4"); + AssertValue(iniHandle2, L"Section1\\Value1", L"Section1Value1"); + AssertValue(iniHandle2, L"Section1\\Value2", L"Section1Value2"); + AssertValue(iniHandle2, L"Section2\\Value1", L"Section2Value1"); + AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arr"); + } + finally + { + ReleaseIni(iniHandle); + ReleaseIni(iniHandle2); + } + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/MemUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/MemUtilTest.cpp new file mode 100644 index 00000000..09692bfb --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/MemUtilTest.cpp @@ -0,0 +1,505 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + struct ArrayValue + { + DWORD dwNum; + void *pvNull1; + LPWSTR sczString; + void *pvNull2; + }; + + public ref class MemUtil + { + public: + [Fact] + void MemUtilAppendTest() + { + HRESULT hr = S_OK; + DWORD dwSize; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 1"); + ++cValues; + SetItem(rgValues + 0, 0); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 2"); + ++cValues; + SetItem(rgValues + 1, 1); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 3"); + ++cValues; + SetItem(rgValues + 2, 2); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 4"); + ++cValues; + SetItem(rgValues + 3, 3); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 5"); + ++cValues; + SetItem(rgValues + 4, 4); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 6"); + ++cValues; + SetItem(rgValues + 5, 5); + + // OK, we used growth size 5, so let's try ensuring we have space for 6 (5 + first item) items + // and make sure it doesn't grow since we already have enough space + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to ensure array size matches what it should already be"); + dwSize = MemSize(rgValues); + if (dwSize != 6 * sizeof(ArrayValue)) + { + hr = E_FAIL; + ExitOnFailure(hr, "MemEnsureArraySize is growing an array that is already big enough!"); + } + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); + ++cValues; + SetItem(rgValues + 6, 6); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); + ++cValues; + SetItem(rgValues + 7, 7); + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); + ++cValues; + SetItem(rgValues + 8, 8); + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + } + finally + { + ReleaseMem(rgValues); + } + + LExit: + DutilUninitialize(); + } + + [Fact] + void MemUtilInsertTest() + { + HRESULT hr = S_OK; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of empty array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 5); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert at end of array"); + ++cValues; + CheckNullItem(rgValues + 1); + SetItem(rgValues + 1, 6); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 4); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 3); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 1); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 1); + SetItem(rgValues + 1, 2); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 0); + SetItem(rgValues + 0, 0); + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); + ++cValues; + CheckNullItem(rgValues + 7); + SetItem(rgValues + 7, 7); + + hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 8, 1, cValues + 1, sizeof(ArrayValue), 5); + NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); + ++cValues; + CheckNullItem(rgValues + 8); + SetItem(rgValues + 8, 8); + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + } + finally + { + ReleaseMem(rgValues); + DutilUninitialize(); + } + } + + [Fact] + void MemUtilRemovePreserveOrderTest() + { + HRESULT hr = S_OK; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); + NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); + + cValues = 10; + for (DWORD i = 0; i < cValues; ++i) + { + SetItem(rgValues + i, i); + } + + // Remove last item + MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), TRUE); + --cValues; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Remove last two items + MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), TRUE); + cValues -= 2; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Remove first item + MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), TRUE); + --cValues; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i + 1); + } + + + // Remove first two items + MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE); + cValues -= 2; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i + 3); + } + + // Remove middle two items + MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), TRUE); + cValues -= 2; + + CheckItem(rgValues, 3); + CheckItem(rgValues + 1, 6); + + // Remove last 2 items to ensure we don't crash + MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE); + cValues -= 2; + } + finally + { + ReleaseMem(rgValues); + DutilUninitialize(); + } + } + + [Fact] + void MemUtilRemoveFastTest() + { + HRESULT hr = S_OK; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); + NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); + + cValues = 10; + for (DWORD i = 0; i < cValues; ++i) + { + SetItem(rgValues + i, i); + } + + // Remove last item + MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), FALSE); + --cValues; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Remove last two items + MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), FALSE); + cValues -= 2; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Remove first item + MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), FALSE); + --cValues; + + CheckItem(rgValues, 6); + CheckItem(rgValues + 1, 1); + CheckItem(rgValues + 2, 2); + CheckItem(rgValues + 3, 3); + CheckItem(rgValues + 4, 4); + CheckItem(rgValues + 5, 5); + + // Remove first two items + MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE); + cValues -= 2; + + CheckItem(rgValues, 4); + CheckItem(rgValues + 1, 5); + CheckItem(rgValues + 2, 2); + CheckItem(rgValues + 3, 3); + + + // Remove middle two items + MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), FALSE); + cValues -= 2; + + CheckItem(rgValues, 4); + CheckItem(rgValues + 1, 3); + + // Remove last 2 items to ensure we don't crash + MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE); + cValues -= 2; + } + finally + { + ReleaseMem(rgValues); + DutilUninitialize(); + } + } + + [Fact] + void MemUtilSwapTest() + { + HRESULT hr = S_OK; + ArrayValue *rgValues = NULL; + DWORD cValues = 0; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); + NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); + + cValues = 10; + for (DWORD i = 0; i < cValues; ++i) + { + SetItem(rgValues + i, i); + } + + // Swap first two + MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 1); + CheckItem(rgValues + 1, 0); + for (DWORD i = 2; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap them back + MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); + --cValues; + + for (DWORD i = 0; i < cValues; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap first and last items (index 0 and 9) + MemArraySwapItems(rgValues, 0, 9, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 9); + CheckItem(rgValues + 9, 0); + for (DWORD i = 1; i < cValues - 1; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap index 1 and 8 + MemArraySwapItems(rgValues, 1, 8, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 9); + CheckItem(rgValues + 1, 8); + CheckItem(rgValues + 8, 1); + CheckItem(rgValues + 9, 0); + for (DWORD i = 2; i < cValues - 2; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap index 2 and 7 + MemArraySwapItems(rgValues, 2, 7, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 9); + CheckItem(rgValues + 1, 8); + CheckItem(rgValues + 2, 7); + CheckItem(rgValues + 7, 2); + CheckItem(rgValues + 8, 1); + CheckItem(rgValues + 9, 0); + for (DWORD i = 3; i < cValues - 3; ++i) + { + CheckItem(rgValues + i, i); + } + + // Swap index 0 and 1 + MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); + --cValues; + + CheckItem(rgValues, 8); + CheckItem(rgValues + 1, 9); + CheckItem(rgValues + 2, 7); + CheckItem(rgValues + 7, 2); + CheckItem(rgValues + 8, 1); + CheckItem(rgValues + 9, 0); + for (DWORD i = 3; i < cValues - 3; ++i) + { + CheckItem(rgValues + i, i); + } + } + finally + { + ReleaseMem(rgValues); + DutilUninitialize(); + } + } + + private: + void SetItem(ArrayValue *pValue, DWORD dwValue) + { + HRESULT hr = S_OK; + pValue->dwNum = dwValue; + + hr = StrAllocFormatted(&pValue->sczString, L"%u", dwValue); + NativeAssert::Succeeded(hr, "Failed to allocate string"); + } + + void CheckItem(ArrayValue *pValue, DWORD dwValue) + { + HRESULT hr = S_OK; + LPWSTR sczTemp = NULL; + + try + { + NativeAssert::Equal(dwValue, pValue->dwNum); + + hr = StrAllocFormatted(&sczTemp, L"%u", dwValue); + NativeAssert::Succeeded(hr, "Failed to allocate temp string"); + + NativeAssert::StringEqual(sczTemp, pValue->sczString, TRUE); + + if (pValue->pvNull1 || pValue->pvNull2) + { + hr = E_FAIL; + ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!"); + } + } + finally + { + ReleaseStr(sczTemp); + } + + LExit: + return; + } + + void CheckNullItem(ArrayValue *pValue) + { + HRESULT hr = S_OK; + + NativeAssert::Equal(0, pValue->dwNum); + + if (pValue->sczString) + { + hr = E_FAIL; + ExitOnFailure(hr, "Item found isn't NULL!"); + } + + if (pValue->pvNull1 || pValue->pvNull2) + { + hr = E_FAIL; + ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!"); + } + + LExit: + return; + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp new file mode 100644 index 00000000..273f2eb6 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp @@ -0,0 +1,487 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#undef RemoveDirectory + +using namespace System; +using namespace System::Collections::Generic; +using namespace System::Runtime::InteropServices; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + const int PREWAIT = 20; + const int POSTWAIT = 480; + const int FULLWAIT = 500; + const int SILENCEPERIOD = 100; + + struct RegKey + { + HRESULT hr; + HKEY hkRoot; + LPCWSTR wzSubKey; + REG_KEY_BITNESS kbKeyBitness; + BOOL fRecursive; + }; + struct Directory + { + HRESULT hr; + LPCWSTR wzPath; + BOOL fRecursive; + }; + struct Results + { + RegKey *rgRegKeys; + DWORD cRegKeys; + Directory *rgDirectories; + DWORD cDirectories; + }; + + public delegate void MonGeneralDelegate(HRESULT, LPVOID); + + public delegate void MonDriveStatusDelegate(WCHAR, BOOL, LPVOID); + + public delegate void MonDirectoryDelegate(HRESULT, LPCWSTR, BOOL, LPVOID, LPVOID); + + public delegate void MonRegKeyDelegate(HRESULT, HKEY, LPCWSTR, REG_KEY_BITNESS, BOOL, LPVOID, LPVOID); + + static void MonGeneral( + __in HRESULT /*hrResult*/, + __in_opt LPVOID /*pvContext*/ + ) + { + Assert::True(false); + } + + static void MonDriveStatus( + __in WCHAR /*chDrive*/, + __in BOOL /*fArriving*/, + __in_opt LPVOID /*pvContext*/ + ) + { + } + + static void MonDirectory( + __in HRESULT hrResult, + __in_z LPCWSTR wzPath, + __in_z BOOL fRecursive, + __in_opt LPVOID pvContext, + __in_opt LPVOID pvDirectoryContext + ) + { + Assert::Equal(S_OK, hrResult); + Assert::Equal(0, reinterpret_cast(pvDirectoryContext)); + + HRESULT hr = S_OK; + Results *pResults = reinterpret_cast(pvContext); + + hr = MemEnsureArraySize(reinterpret_cast(&pResults->rgDirectories), pResults->cDirectories + 1, sizeof(Directory), 5); + NativeAssert::ValidReturnCode(hr, S_OK); + ++pResults->cDirectories; + + pResults->rgDirectories[pResults->cDirectories - 1].hr = hrResult; + pResults->rgDirectories[pResults->cDirectories - 1].wzPath = wzPath; + pResults->rgDirectories[pResults->cDirectories - 1].fRecursive = fRecursive; + } + + static void MonRegKey( + __in HRESULT hrResult, + __in HKEY hkRoot, + __in_z LPCWSTR wzSubKey, + __in REG_KEY_BITNESS kbKeyBitness, + __in_z BOOL fRecursive, + __in_opt LPVOID pvContext, + __in_opt LPVOID pvRegKeyContext + ) + { + Assert::Equal(S_OK, hrResult); + Assert::Equal(0, reinterpret_cast(pvRegKeyContext)); + + HRESULT hr = S_OK; + Results *pResults = reinterpret_cast(pvContext); + + hr = MemEnsureArraySize(reinterpret_cast(&pResults->rgRegKeys), pResults->cRegKeys + 1, sizeof(RegKey), 5); + NativeAssert::ValidReturnCode(hr, S_OK); + ++pResults->cRegKeys; + + pResults->rgRegKeys[pResults->cRegKeys - 1].hr = hrResult; + pResults->rgRegKeys[pResults->cRegKeys - 1].hkRoot = hkRoot; + pResults->rgRegKeys[pResults->cRegKeys - 1].wzSubKey = wzSubKey; + pResults->rgRegKeys[pResults->cRegKeys - 1].kbKeyBitness = kbKeyBitness; + pResults->rgRegKeys[pResults->cRegKeys - 1].fRecursive = fRecursive; + } + + public ref class MonUtil + { + public: + void ClearResults(Results *pResults) + { + ReleaseNullMem(pResults->rgDirectories); + pResults->cDirectories = 0; + ReleaseNullMem(pResults->rgRegKeys); + pResults->cRegKeys = 0; + } + + void RemoveDirectory(LPCWSTR wzPath) + { + DWORD dwRetryCount = 0; + const DWORD c_dwMaxRetryCount = 100; + const DWORD c_dwRetryInterval = 50; + + HRESULT hr = DirEnsureDelete(wzPath, TRUE, TRUE); + + // Monitoring a directory opens a handle to that directory, which means delete requests for that directory will succeed + // (and deletion will be "pending" until our monitor handle is closed) + // but deletion of the directory containing that directory cannot complete until the handle is closed. This means DirEnsureDelete() + // can sometimes encounter HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) failures, which just means it needs to retry a bit later + // (after the waiter thread wakes up, it will release the handle) + while (HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) == hr && c_dwMaxRetryCount > dwRetryCount) + { + ::Sleep(c_dwRetryInterval); + ++dwRetryCount; + hr = DirEnsureDelete(wzPath, TRUE, TRUE); + } + + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); + } + + void TestDirectory(MON_HANDLE handle, Results *pResults) + { + HRESULT hr = S_OK; + LPWSTR sczShallowPath = NULL; + LPWSTR sczParentPath = NULL; + LPWSTR sczDeepPath = NULL; + LPWSTR sczChildPath = NULL; + LPWSTR sczChildFilePath = NULL; + + try + { + hr = PathExpand(&sczShallowPath, L"%TEMP%\\MonUtilTest\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = PathExpand(&sczParentPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = PathExpand(&sczDeepPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = PathExpand(&sczChildPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = PathExpand(&sczChildFilePath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\file.txt", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + RemoveDirectory(sczShallowPath); + + hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = DirEnsureExists(sczParentPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + // Make sure creating the parent directory does nothing, even after silence period + ::Sleep(FULLWAIT); + Assert::Equal(0, pResults->cDirectories); + + // Now create the target path, no notification until after the silence period + hr = DirEnsureExists(sczDeepPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(PREWAIT); + Assert::Equal(0, pResults->cDirectories); + + // Now after the full silence period, it should have triggered + ::Sleep(POSTWAIT); + Assert::Equal(1, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[0].hr, S_OK); + + // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists. + RemoveDirectory(sczShallowPath); + + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[1].hr, S_OK); + + // Create the parent directory again, still should be nothing even after full silence period + hr = DirEnsureExists(sczParentPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cDirectories); + + hr = DirEnsureExists(sczChildPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(PREWAIT); + Assert::Equal(2, pResults->cDirectories); + + ::Sleep(POSTWAIT); + Assert::Equal(3, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK); + + // Write a file to a deep child subfolder, and make sure it's detected + hr = FileFromString(sczChildFilePath, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM); + NativeAssert::ValidReturnCode(hr, S_OK); + ::Sleep(PREWAIT); + Assert::Equal(3, pResults->cDirectories); + + ::Sleep(POSTWAIT); + Assert::Equal(4, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK); + + RemoveDirectory(sczParentPath); + + ::Sleep(FULLWAIT); + Assert::Equal(5, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK); + + // Now remove the directory from the list of things to monitor, and confirm changes are no longer tracked + hr = MonRemoveDirectory(handle, sczDeepPath, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK); + ::Sleep(PREWAIT); + + hr = DirEnsureExists(sczDeepPath, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(FULLWAIT); + Assert::Equal(5, pResults->cDirectories); + NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK); + + // Finally, add it back so we can test multiple things to monitor at once + hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + } + finally + { + ReleaseStr(sczShallowPath); + ReleaseStr(sczDeepPath); + ReleaseStr(sczParentPath); + } + } + + void TestRegKey(MON_HANDLE handle, Results *pResults) + { + HRESULT hr = S_OK; + LPCWSTR wzShallowRegKey = L"Software\\MonUtilTest\\"; + LPCWSTR wzParentRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\"; + LPCWSTR wzDeepRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\"; + LPCWSTR wzChildRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\"; + HKEY hk = NULL; + + try + { + hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); + + hr = MonAddRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + ReleaseRegKey(hk); + // Make sure creating the parent key does nothing, even after silence period + ::Sleep(FULLWAIT); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + Assert::Equal(0, pResults->cRegKeys); + + // Now create the target path, no notification until after the silence period + hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ReleaseRegKey(hk); + ::Sleep(PREWAIT); + Assert::Equal(0, pResults->cRegKeys); + + // Now after the full silence period, it should have triggered + ::Sleep(POSTWAIT); + Assert::Equal(1, pResults->cRegKeys); + NativeAssert::ValidReturnCode(pResults->rgRegKeys[0].hr, S_OK); + + // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists. + hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); + ::Sleep(PREWAIT); + Assert::Equal(1, pResults->cRegKeys); + + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cRegKeys); + NativeAssert::ValidReturnCode(pResults->rgRegKeys[1].hr, S_OK); + + // Create the parent directory again, still should be nothing even after full silence period + hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ReleaseRegKey(hk); + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cRegKeys); + + hr = RegCreate(HKEY_CURRENT_USER, wzChildRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ::Sleep(PREWAIT); + Assert::Equal(2, pResults->cRegKeys); + + ::Sleep(FULLWAIT); + Assert::Equal(3, pResults->cRegKeys); + NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK); + + // Write a registry value to some deep child subkey, and make sure it's detected + hr = RegWriteString(hk, L"valuename", L"testvalue"); + NativeAssert::ValidReturnCode(hr, S_OK); + ReleaseRegKey(hk); + ::Sleep(PREWAIT); + Assert::Equal(3, pResults->cRegKeys); + + ::Sleep(FULLWAIT); + Assert::Equal(4, pResults->cRegKeys); + NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK); + + hr = RegDelete(HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_32BIT, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK); + + ::Sleep(FULLWAIT); + Assert::Equal(5, pResults->cRegKeys); + + // Now remove the regkey from the list of things to monitor, and confirm changes are no longer tracked + hr = MonRemoveRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + ReleaseRegKey(hk); + ::Sleep(FULLWAIT); + Assert::Equal(5, pResults->cRegKeys); + } + finally + { + ReleaseRegKey(hk); + } + } + + void TestMoreThan64(MON_HANDLE handle, Results *pResults) + { + HRESULT hr = S_OK; + LPWSTR sczBaseDir = NULL; + LPWSTR sczDir = NULL; + LPWSTR sczFile = NULL; + + try + { + hr = PathExpand(&sczBaseDir, L"%TEMP%\\ScalabilityTest\\", PATH_EXPAND_ENVIRONMENT); + NativeAssert::ValidReturnCode(hr, S_OK); + + for (DWORD i = 0; i < 200; ++i) + { + hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = DirEnsureExists(sczDir, NULL); + NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); + + hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + } + + hr = PathConcat(sczDir, L"file.txt", &sczFile); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = FileFromString(sczFile, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM); + NativeAssert::ValidReturnCode(hr, S_OK); + + ::Sleep(FULLWAIT); + Assert::Equal(1, pResults->cDirectories); + + for (DWORD i = 0; i < 199; ++i) + { + hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = MonRemoveDirectory(handle, sczDir, FALSE); + NativeAssert::ValidReturnCode(hr, S_OK); + } + ::Sleep(FULLWAIT); + + hr = FileFromString(sczFile, 0, L"contents2", FILE_ENCODING_UTF16_WITH_BOM); + NativeAssert::ValidReturnCode(hr, S_OK); + + ::Sleep(FULLWAIT); + Assert::Equal(2, pResults->cDirectories); + + for (DWORD i = 0; i < 199; ++i) + { + hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL); + NativeAssert::ValidReturnCode(hr, S_OK); + } + ::Sleep(FULLWAIT); + + hr = FileFromString(sczFile, 0, L"contents3", FILE_ENCODING_UTF16_WITH_BOM); + NativeAssert::ValidReturnCode(hr, S_OK); + + ::Sleep(FULLWAIT); + Assert::Equal(3, pResults->cDirectories); + } + finally + { + ReleaseStr(sczBaseDir); + ReleaseStr(sczDir); + ReleaseStr(sczFile); + } + } + + [Fact(Skip = "Test demonstrates failure")] + void MonUtilTest() + { + HRESULT hr = S_OK; + MON_HANDLE handle = NULL; + List^ gcHandles = gcnew List(); + Results *pResults = (Results *)MemAlloc(sizeof(Results), TRUE); + Assert::True(NULL != pResults); + + try + { + // These ensure the function pointers we send point to this thread's appdomain, which helps with assembly binding when running tests within msbuild + MonGeneralDelegate^ fpMonGeneral = gcnew MonGeneralDelegate(MonGeneral); + GCHandle gchMonGeneral = GCHandle::Alloc(fpMonGeneral); + gcHandles->Add(gchMonGeneral); + IntPtr ipMonGeneral = Marshal::GetFunctionPointerForDelegate(fpMonGeneral); + + MonDriveStatusDelegate^ fpMonDriveStatus = gcnew MonDriveStatusDelegate(MonDriveStatus); + GCHandle gchMonDriveStatus = GCHandle::Alloc(fpMonDriveStatus); + gcHandles->Add(gchMonDriveStatus); + IntPtr ipMonDriveStatus = Marshal::GetFunctionPointerForDelegate(fpMonDriveStatus); + + MonDirectoryDelegate^ fpMonDirectory = gcnew MonDirectoryDelegate(MonDirectory); + GCHandle gchMonDirectory = GCHandle::Alloc(fpMonDirectory); + gcHandles->Add(gchMonDirectory); + IntPtr ipMonDirectory = Marshal::GetFunctionPointerForDelegate(fpMonDirectory); + + MonRegKeyDelegate^ fpMonRegKey = gcnew MonRegKeyDelegate(MonRegKey); + GCHandle gchMonRegKey = GCHandle::Alloc(fpMonRegKey); + gcHandles->Add(gchMonRegKey); + IntPtr ipMonRegKey = Marshal::GetFunctionPointerForDelegate(fpMonRegKey); + + // "Silence period" is 100 ms + hr = MonCreate(&handle, static_cast(ipMonGeneral.ToPointer()), static_cast(ipMonDriveStatus.ToPointer()), static_cast(ipMonDirectory.ToPointer()), static_cast(ipMonRegKey.ToPointer()), pResults); + NativeAssert::ValidReturnCode(hr, S_OK); + + hr = RegInitialize(); + NativeAssert::ValidReturnCode(hr, S_OK); + + TestDirectory(handle, pResults); + ClearResults(pResults); + TestRegKey(handle, pResults); + ClearResults(pResults); + TestMoreThan64(handle, pResults); + ClearResults(pResults); + } + finally + { + ReleaseMon(handle); + + for each (GCHandle gcHandle in gcHandles) + { + gcHandle.Free(); + } + + ReleaseMem(pResults->rgDirectories); + ReleaseMem(pResults->rgRegKeys); + ReleaseMem(pResults); + } + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp new file mode 100644 index 00000000..5a1f06fd --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp @@ -0,0 +1,80 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class PathUtil + { + public: + [Fact] + void PathGetHierarchyArrayTest() + { + HRESULT hr = S_OK; + LPWSTR *rgsczPaths = NULL; + UINT cPaths = 0; + + try + { + hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\a.txt", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular file path"); + Assert::Equal(5, cPaths); + NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\a.txt", rgsczPaths[4]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular directory path"); + Assert::Equal(4, cPaths); + NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]); + NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\file.txt", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC file path"); + Assert::Equal(3, cPaths); + NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\file.txt", rgsczPaths[2]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); + Assert::Equal(2, cPaths); + NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\ValueName", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); + Assert::Equal(4, cPaths); + NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]); + NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\ValueName", rgsczPaths[3]); + ReleaseNullStrArray(rgsczPaths, cPaths); + + hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\", &rgsczPaths, &cPaths); + NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); + Assert::Equal(3, cPaths); + NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]); + NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]); + NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]); + ReleaseNullStrArray(rgsczPaths, cPaths); + } + finally + { + ReleaseStrArray(rgsczPaths, cPaths); + } + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp new file mode 100644 index 00000000..75b9222a --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp @@ -0,0 +1,488 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#include +#include + +using namespace System; +using namespace Xunit; +using namespace WixTest; + +#define ASSIGN_INDEX_STRUCT(a, b, c) {a.wzName = c; a.rgColumns = b; a.cColumns = countof(b);}; + +namespace DutilTests +{ + enum TABLES + { + TABLE_A, + TABLE_COUNT + }; + + enum TABLE_A_COLUMNS + { + TABLE_A_KEY, + TABLE_A_BINARY, + TABLE_A_DWORD, + TABLE_A_QWORD, + TABLE_A_BOOL, + TABLE_A_STRING, + TABLE_A_DWORD_NULLABLE, + TABLE_A_INITIAL_COLUMNS, + + TABLE_A_EXTRA_STRING = TABLE_A_INITIAL_COLUMNS, + TABLE_A_FINAL_COLUMNS + }; + + struct TableARowValue + { + DWORD dwAutoGenKey; + + BYTE *pbBinary; + DWORD cBinary; + + DWORD dw; + DWORD64 qw; + BOOL f; + LPWSTR scz; + + BOOL fNullablePresent; + DWORD dwNullable; + + BOOL fSchemaV2; + LPWSTR sczExtra; + }; + + public ref class SceUtil + { + public: + void ReleaseSceSchema(SCE_DATABASE_SCHEMA *pdsSchema) + { + DWORD dwTable; + + for (dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) + { + ReleaseNullMem(pdsSchema->rgTables[dwTable].rgColumns); + ReleaseNullMem(pdsSchema->rgTables[dwTable].rgIndexes); + } + + ReleaseMem(pdsSchema->rgTables); + + return; + } + + void SetupSchema(SCE_DATABASE_SCHEMA *pSchema, BOOL fIncludeExtended) + { + pSchema->cTables = TABLE_COUNT; + pSchema->rgTables = static_cast(MemAlloc(TABLE_COUNT * sizeof(SCE_TABLE_SCHEMA), TRUE)); + NativeAssert::True(pSchema->rgTables != NULL); + + pSchema->rgTables[TABLE_A].wzName = L"TableA"; + pSchema->rgTables[TABLE_A].cColumns = fIncludeExtended ? TABLE_A_FINAL_COLUMNS : TABLE_A_INITIAL_COLUMNS; + pSchema->rgTables[TABLE_A].cIndexes = 2; + + for (DWORD i = 0; i < pSchema->cTables; ++i) + { + pSchema->rgTables[i].rgColumns = static_cast(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cColumns, TRUE)); + NativeAssert::True(pSchema->rgTables[i].rgColumns != NULL); + + pSchema->rgTables[i].rgIndexes = static_cast(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cIndexes, TRUE)); + NativeAssert::True(pSchema->rgTables[i].rgIndexes != NULL); + } + + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].wzName = L"Key"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].dbtColumnType = DBTYPE_I4; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fPrimaryKey = TRUE; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fAutoIncrement = TRUE; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].wzName = L"Binary"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].dbtColumnType = DBTYPE_BYTES; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].wzName = L"Dword"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].dbtColumnType = DBTYPE_I4; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].wzName = L"Qword"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].dbtColumnType = DBTYPE_I8; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].wzName = L"Bool"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].dbtColumnType = DBTYPE_BOOL; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].wzName = L"String"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].dbtColumnType = DBTYPE_WSTR; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].wzName = L"Nullable"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].dbtColumnType = DBTYPE_I4; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].fNullable = TRUE; + + if (fIncludeExtended) + { + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].wzName = L"ExtraString"; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].dbtColumnType = DBTYPE_WSTR; + pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].fNullable = TRUE; + } + + static DWORD rgdwTableA_Index1[] = { TABLE_A_DWORD, TABLE_A_STRING, TABLE_A_QWORD }; + static DWORD rgdwTableA_Index2[] = { TABLE_A_DWORD, TABLE_A_STRING }; + + ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[0], rgdwTableA_Index1, L"Dword_String_Qword"); + ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[1], rgdwTableA_Index2, L"Dword_String"); + } + + void SetStructValues(TableARowValue *pValue, BYTE *pbBinary, DWORD cBinary, DWORD dw, DWORD64 qw, BOOL f, LPWSTR scz, DWORD *pdw, LPWSTR sczExtra) + { + pValue->pbBinary = pbBinary; + pValue->cBinary = cBinary; + pValue->dw = dw; + pValue->qw = qw; + pValue->f = f; + pValue->scz = scz; + + if (pdw) + { + pValue->fNullablePresent = TRUE; + pValue->dwNullable = *pdw; + } + else + { + pValue->fNullablePresent = FALSE; + } + + if (sczExtra) + { + pValue->fSchemaV2 = TRUE; + pValue->sczExtra = sczExtra; + } + else + { + pValue->fSchemaV2 = FALSE; + } + } + + void AssertStructValuesSame(TableARowValue *pValueExpected, TableARowValue *pValueOther) + { + NativeAssert::Equal(pValueExpected->cBinary, pValueOther->cBinary); + NativeAssert::True(0 == memcmp(pValueExpected->pbBinary, pValueOther->pbBinary, pValueOther->cBinary)); + + NativeAssert::Equal(pValueExpected->dw, pValueOther->dw); + NativeAssert::Equal(pValueExpected->qw, pValueOther->qw); + NativeAssert::Equal(pValueExpected->f, pValueOther->f); + NativeAssert::True(0 == wcscmp(pValueExpected->scz, pValueOther->scz)); + + NativeAssert::Equal(pValueExpected->fNullablePresent, pValueOther->fNullablePresent); + if (pValueExpected->fNullablePresent) + { + NativeAssert::Equal(pValueExpected->dwNullable, pValueOther->dwNullable); + } + + NativeAssert::Equal(pValueExpected->fSchemaV2, pValueOther->fSchemaV2); + if (pValueExpected->fSchemaV2) + { + NativeAssert::True(0 == wcscmp(pValueExpected->sczExtra, pValueOther->sczExtra)); + } + } + + void InsertRow(SCE_DATABASE *pDatabase, TableARowValue *pValue, BOOL fRollback) + { + HRESULT hr = S_OK; + SCE_ROW_HANDLE sceRow = NULL; + + hr = SceBeginTransaction(pDatabase); + NativeAssert::Succeeded(hr, "Failed to begin transaction"); + + hr = ScePrepareInsert(pDatabase, TABLE_A, &sceRow); + NativeAssert::Succeeded(hr, "Failed to prepare to insert row"); + + hr = SceSetColumnBinary(sceRow, TABLE_A_BINARY, pValue->pbBinary, pValue->cBinary); + NativeAssert::Succeeded(hr, "Failed to set binary value"); + + hr = SceSetColumnDword(sceRow, TABLE_A_DWORD, pValue->dw); + NativeAssert::Succeeded(hr, "Failed to set dword value"); + + hr = SceSetColumnQword(sceRow, TABLE_A_QWORD, pValue->qw); + NativeAssert::Succeeded(hr, "Failed to set qword value"); + + hr = SceSetColumnBool(sceRow, TABLE_A_BOOL, pValue->f); + NativeAssert::Succeeded(hr, "Failed to set bool value"); + + hr = SceSetColumnString(sceRow, TABLE_A_STRING, pValue->scz); + NativeAssert::Succeeded(hr, "Failed to set string value"); + + if (pValue->fNullablePresent) + { + hr = SceSetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, pValue->dwNullable); + NativeAssert::Succeeded(hr, "Failed to set dword value"); + } + else + { + hr = SceSetColumnNull(sceRow, TABLE_A_DWORD_NULLABLE); + NativeAssert::Succeeded(hr, "Failed to set null value"); + } + + if (pValue->fSchemaV2) + { + hr = SceSetColumnString(sceRow, TABLE_A_EXTRA_STRING, pValue->sczExtra); + NativeAssert::Succeeded(hr, "Failed to set extra string value"); + } + + hr = SceFinishUpdate(sceRow); + NativeAssert::Succeeded(hr, "Failed to finish insert"); + + if (fRollback) + { + hr = SceRollbackTransaction(pDatabase); + NativeAssert::Succeeded(hr, "Failed to rollback transaction"); + } + else + { + hr = SceCommitTransaction(pDatabase); + NativeAssert::Succeeded(hr, "Failed to commit transaction"); + + hr = SceGetColumnDword(sceRow, TABLE_A_KEY, &pValue->dwAutoGenKey); + NativeAssert::Succeeded(hr, "Failed to get autogen key after insert"); + + NativeAssert::True(pValue->dwAutoGenKey != 0); + } + + ReleaseSceRow(sceRow); + } + + void VerifyRow(TableARowValue *pExpectedValue, SCE_ROW_HANDLE sceRow) + { + HRESULT hr = S_OK; + TableARowValue value = {}; + + hr = SceGetColumnBinary(sceRow, TABLE_A_BINARY, &value.pbBinary, &value.cBinary); + NativeAssert::Succeeded(hr, "Failed to get binary value from result row"); + + hr = SceGetColumnDword(sceRow, TABLE_A_DWORD, &value.dw); + NativeAssert::Succeeded(hr, "Failed to get dword value from result row"); + + hr = SceGetColumnQword(sceRow, TABLE_A_QWORD, &value.qw); + NativeAssert::Succeeded(hr, "Failed to get qword value from result row"); + + hr = SceGetColumnBool(sceRow, TABLE_A_BOOL, &value.f); + NativeAssert::Succeeded(hr, "Failed to get bool value from result row"); + + hr = SceGetColumnString(sceRow, TABLE_A_STRING, &value.scz); + NativeAssert::Succeeded(hr, "Failed to get string value from result row"); + + hr = SceGetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, &value.dwNullable); + if (hr == E_NOTFOUND) + { + value.fNullablePresent = FALSE; + hr = S_OK; + } + else + { + NativeAssert::Succeeded(hr, "Failed to get string value from result row"); + value.fNullablePresent = TRUE; + } + + if (pExpectedValue->fSchemaV2) + { + value.fSchemaV2 = TRUE; + hr = SceGetColumnString(sceRow, TABLE_A_EXTRA_STRING, &value.sczExtra); + NativeAssert::Succeeded(hr, "Failed to get extra string value from result row"); + } + + AssertStructValuesSame(pExpectedValue, &value); + + ReleaseNullMem(value.pbBinary); + ReleaseNullStr(value.scz); + } + + void VerifyQuery(TableARowValue **rgExpectedValues, DWORD cExpectedValues, SCE_QUERY_RESULTS_HANDLE queryResults) + { + HRESULT hr = S_OK; + SCE_ROW_HANDLE sceRow = NULL; + + for (DWORD i = 0; i < cExpectedValues; ++i) + { + hr = SceGetNextResultRow(queryResults, &sceRow); + NativeAssert::Succeeded(hr, "Failed to get next result row"); + + VerifyRow(rgExpectedValues[i], sceRow); + ReleaseNullSceRow(sceRow); + } + + // No more results + NativeAssert::True(NULL == queryResults || FAILED(SceGetNextResultRow(queryResults, &sceRow))); + } + + void TestIndex(SCE_DATABASE *pDatabase) + { + HRESULT hr = S_OK; + BYTE binary1[50] = { 0x80, 0x70 }; + BYTE binary2[40] = { 0x90, 0xAB }; + BYTE binary3[40] = { 0x85, 0x88 }; + DWORD dwValue1 = 0x55555555, dwValue2 = 0x88888888; + TableARowValue value1 = {}, value2 = {}, value3 = {}, value4 = {}, value5 = {}; + SCE_QUERY_HANDLE query = NULL; + SCE_QUERY_RESULTS_HANDLE results = NULL; + + SetStructValues(&value1, static_cast(binary1), sizeof(binary1), 3, 1, TRUE, L"zzz", &dwValue1, NULL); + SetStructValues(&value2, static_cast(binary2), sizeof(binary2), 3, 2, TRUE, L"yyy", &dwValue2, NULL); + SetStructValues(&value3, static_cast(binary3), sizeof(binary3), 3, 3, TRUE, L"xxx", NULL, NULL); + SetStructValues(&value4, static_cast(binary2), sizeof(binary2), 4, 4, TRUE, L"xyz", &dwValue2, NULL); + SetStructValues(&value5, static_cast(binary3), sizeof(binary3), 3, 1, TRUE, L"yyy", &dwValue2, NULL); + + // Rollback an insert to confirm the insert doesn't happen and database can still be interacted with normally afterwards + InsertRow(pDatabase, &value1, TRUE); + + InsertRow(pDatabase, &value1, FALSE); + InsertRow(pDatabase, &value2, FALSE); + InsertRow(pDatabase, &value3, FALSE); + InsertRow(pDatabase, &value4, FALSE); + InsertRow(pDatabase, &value5, FALSE); + + NativeAssert::True(value1.dwAutoGenKey != value2.dwAutoGenKey); + + // Test setting 1 column + hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 3); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryRange(&query, &results); + NativeAssert::Succeeded(hr, "Failed to run query"); + NativeAssert::True(query == NULL); + + TableARowValue *sortedAfterQuery1[] = { &value3, &value5, &value2, &value1 }; + VerifyQuery(sortedAfterQuery1, _countof(sortedAfterQuery1), results); + ReleaseNullSceQueryResults(results); + + // Test setting 2 columns, third column is unspecified so results are sorted by it + hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 3); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceSetQueryColumnString(query, L"yyy"); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryRange(&query, &results); + NativeAssert::Succeeded(hr, "Failed to run query"); + NativeAssert::True(query == NULL); + + TableARowValue *sortedAfterQuery2[] = { &value5, &value2 }; + VerifyQuery(sortedAfterQuery2, _countof(sortedAfterQuery2), results); + ReleaseNullSceQueryResults(results); + + // Test setting 2 columns, third column of index is unspecified so results are sorted by it + hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 3); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceSetQueryColumnString(query, L"yyy"); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryRange(&query, &results); + NativeAssert::Succeeded(hr, "Failed to run query"); + NativeAssert::True(query == NULL); + + TableARowValue *sortedAfterQuery3[] = { &value5, &value2 }; + VerifyQuery(sortedAfterQuery3, _countof(sortedAfterQuery3), results); + ReleaseNullSceQueryResults(results); + + // Test setting 2 columns in a different (2 column) index, so there is no 3rd column in index to sort by + hr = SceBeginQuery(pDatabase, TABLE_A, 1, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 3); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceSetQueryColumnString(query, L"yyy"); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryRange(&query, &results); + NativeAssert::Succeeded(hr, "Failed to run query"); + NativeAssert::True(query == NULL); + + TableARowValue *sortedAfterQuery4[] = { &value2, &value5 }; + VerifyQuery(sortedAfterQuery4, _countof(sortedAfterQuery4), results); + ReleaseNullSceQueryResults(results); + } + + void TestReadWriteSchemaV2(SCE_DATABASE *pDatabase) + { + HRESULT hr = S_OK; + BYTE binary1[40] = { 0x55, 0x44 }; + DWORD dwValue1 = 58; + TableARowValue value1 = {}; + SCE_QUERY_HANDLE query = NULL; + SCE_ROW_HANDLE row = NULL; + + SetStructValues(&value1, static_cast(binary1), sizeof(binary1), 5, 1, TRUE, L"zzz", &dwValue1, L"newextrastring"); + + InsertRow(pDatabase, &value1, FALSE); + + // Test setting 1 column + hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); + NativeAssert::Succeeded(hr, "Failed to begin query"); + + hr = SceSetQueryColumnDword(query, 5); + NativeAssert::Succeeded(hr, "Failed to set query column dword"); + + hr = SceRunQueryExact(&query, &row); + NativeAssert::Succeeded(hr, "Failed to run query exact"); + + VerifyRow(&value1, row); + } + + [Fact] + void SceUtilTest() + { + HRESULT hr = S_OK; + BOOL fComInitialized = FALSE; + LPWSTR sczDbPath = NULL; + SCE_DATABASE *pDatabase = NULL; + SCE_DATABASE_SCHEMA schema1 = {}; + SCE_DATABASE_SCHEMA schema2 = {}; + + try + { + hr = ::CoInitialize(0); + NativeAssert::Succeeded(hr, "Failed to initialize COM"); + fComInitialized = TRUE; + + SetupSchema(&schema1, FALSE); + SetupSchema(&schema2, TRUE); + + hr = PathExpand(&sczDbPath, L"%TEMP%\\SceUtilTest\\UnitTest.sdf", PATH_EXPAND_ENVIRONMENT); + NativeAssert::Succeeded(hr, "Failed to get path to test database"); + + FileEnsureDelete(sczDbPath); + + hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema1, &pDatabase); + NativeAssert::Succeeded(hr, "Failed to ensure database schema"); + + TestIndex(pDatabase); + + hr = SceCloseDatabase(pDatabase); + pDatabase = NULL; + NativeAssert::Succeeded(hr, "Failed to close database"); + + // Add column to schema + hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema2, &pDatabase); + NativeAssert::Succeeded(hr, "Failed to ensure database schema"); + + TestReadWriteSchemaV2(pDatabase); + } + finally + { + ReleaseSceSchema(&schema1); + ReleaseSceSchema(&schema2); + + if (NULL != pDatabase) + { + hr = SceCloseDatabase(pDatabase); + NativeAssert::Succeeded(hr, "Failed to close database"); + } + ReleaseStr(sczDbPath); + + if (fComInitialized) + { + ::CoUninitialize(); + } + } + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp new file mode 100644 index 00000000..94fee280 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp @@ -0,0 +1,192 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class StrUtil + { + public: + [Fact] + void StrUtilFormattedTest() + { + HRESULT hr = S_OK; + LPWSTR sczText = NULL; + + try + { + hr = StrAllocFormatted(&sczText, L"%hs - %ls - %u", "ansi string", L"unicode string", 1234); + NativeAssert::Succeeded(hr, "Failed to format string."); + NativeAssert::StringEqual(L"ansi string - unicode string - 1234", sczText); + + ReleaseNullStr(sczText); + + hr = StrAllocString(&sczText, L"repeat", 0); + NativeAssert::Succeeded(hr, "Failed to allocate string."); + + hr = StrAllocFormatted(&sczText, L"%ls and %ls", sczText, sczText); + NativeAssert::Succeeded(hr, "Failed to format string unto itself."); + NativeAssert::StringEqual(L"repeat and repeat", sczText); + } + finally + { + ReleaseStr(sczText); + } + } + + [Fact] + void StrUtilTrimTest() + { + TestTrim(L"", L""); + TestTrim(L"Blah", L"Blah"); + TestTrim(L"\t\t\tBlah", L"Blah"); + TestTrim(L"\t Blah ", L"Blah"); + TestTrim(L"Blah ", L"Blah"); + TestTrim(L"\t Spaces \t Between \t", L"Spaces \t Between"); + TestTrim(L" \t\t\t ", L""); + + TestTrimAnsi("", ""); + TestTrimAnsi("Blah", "Blah"); + TestTrimAnsi("\t\t\tBlah", "Blah"); + TestTrimAnsi(" Blah ", "Blah"); + TestTrimAnsi("Blah ", "Blah"); + TestTrimAnsi("\t Spaces \t Between \t", "Spaces \t Between"); + TestTrimAnsi(" \t\t\t ", ""); + } + + [Fact] + void StrUtilConvertTest() + { + char a[] = { 'a', 'b', 'C', 'd', '\0', '\0' }; + + TestStrAllocStringAnsi(a, 5, L"abCd"); + TestStrAllocStringAnsi(a, 4, L"abCd"); + TestStrAllocStringAnsi(a, 3, L"abC"); + TestStrAllocStringAnsi(a, 2, L"ab"); + TestStrAllocStringAnsi(a, 1, L"a"); + TestStrAllocStringAnsi(a, 0, L"abCd"); + + wchar_t b[] = { L'a', L'b', L'C', L'd', L'\0', L'\0' }; + + TestStrAnsiAllocString(b, 5, "abCd"); + TestStrAnsiAllocString(b, 4, "abCd"); + TestStrAnsiAllocString(b, 3, "abC"); + TestStrAnsiAllocString(b, 2, "ab"); + TestStrAnsiAllocString(b, 1, "a"); + TestStrAnsiAllocString(b, 0, "abCd"); + } + + private: + void TestTrim(LPCWSTR wzInput, LPCWSTR wzExpectedResult) + { + HRESULT hr = S_OK; + LPWSTR sczOutput = NULL; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = StrTrimWhitespace(&sczOutput, wzInput); + NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: {0}", wzInput); + + if (0 != wcscmp(wzExpectedResult, sczOutput)) + { + hr = E_FAIL; + ExitOnFailure(hr, "Trimmed string \"%ls\", expected result \"%ls\", actual result \"%ls\"", wzInput, wzExpectedResult, sczOutput); + } + } + finally + { + ReleaseStr(sczOutput); + } + + LExit: + DutilUninitialize(); + } + + void TestTrimAnsi(LPCSTR szInput, LPCSTR szExpectedResult) + { + HRESULT hr = S_OK; + LPSTR sczOutput = NULL; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = StrAnsiTrimWhitespace(&sczOutput, szInput); + NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: \"{0}\"", szInput); + + if (0 != strcmp(szExpectedResult, sczOutput)) + { + hr = E_FAIL; + ExitOnFailure(hr, "Trimmed string \"%hs\", expected result \"%hs\", actual result \"%hs\"", szInput, szExpectedResult, sczOutput); + } + } + finally + { + ReleaseStr(sczOutput); + } + + LExit: + DutilUninitialize(); + } + + void TestStrAllocStringAnsi(LPCSTR szSource, DWORD cchSource, LPCWSTR wzExpectedResult) + { + HRESULT hr = S_OK; + LPWSTR sczOutput = NULL; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = StrAllocStringAnsi(&sczOutput, szSource, cchSource, CP_UTF8); + NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", szSource); + + if (0 != wcscmp(sczOutput, wzExpectedResult)) + { + hr = E_FAIL; + ExitOnFailure(hr, "String doesn't match, expected result \"%ls\", actual result \"%ls\"", wzExpectedResult, sczOutput); + } + } + finally + { + ReleaseStr(sczOutput); + } + + LExit: + DutilUninitialize(); + } + + void TestStrAnsiAllocString(LPWSTR wzSource, DWORD cchSource, LPCSTR szExpectedResult) + { + HRESULT hr = S_OK; + LPSTR sczOutput = NULL; + + DutilInitialize(&DutilTestTraceError); + + try + { + hr = StrAnsiAllocString(&sczOutput, wzSource, cchSource, CP_UTF8); + NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", wzSource); + + if (0 != strcmp(sczOutput, szExpectedResult)) + { + hr = E_FAIL; + ExitOnFailure(hr, "String doesn't match, expected result \"%hs\", actual result \"%hs\"", szExpectedResult, sczOutput); + } + } + finally + { + ReleaseStr(sczOutput); + } + + LExit: + DutilUninitialize(); + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml b/src/libs/dutil/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml new file mode 100644 index 00000000..d9f961fe --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml @@ -0,0 +1,68 @@ + + + + + + BundleB v2.0 + Bundle Subtitle. + 1116353B-7C6E-4C29-BFA1-D4A972CD421D + 2014-07-14T12:39:00.000Z + http://localhost:9999/wix4/BundleB/feed + + manual build + + Bundle v2.0 + v2.0 + + Bundle_Author + http://mycompany.com/software + Bundle_Author@mycompany.com + + + + + <p>Change list:</p><ul> + <li>Updated release.</li> + </ul> + + 2.0.0.0 + 2014-11-10T12:39:00.000Z + + + Bundle v1.0 + v1.0 + + Bundle_Author + http://mycompany.com/software + Bundle_Author@mycompany.com + + + + + <p>Change list:</p><ul> + <li>Initial release.</li> + </ul> + + + 1.0.0.0 + 2014-11-09T12:39:00.000Z + + + Bundle v1.0-preview + v1.0-preview + + Bundle_Author + http://mycompany.com/software + Bundle_Author@mycompany.com + + + + + <p>Change list:</p><ul> + <li>Initial release.</li> + </ul> + + 1.0.0.0 + 2014-11-09T12:39:00.000Z + + diff --git a/src/libs/dutil/test/DUtilUnitTest/UnitTest.rc b/src/libs/dutil/test/DUtilUnitTest/UnitTest.rc new file mode 100644 index 00000000..14cebe1a --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/UnitTest.rc @@ -0,0 +1,6 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define VER_APP +#define VER_ORIGINAL_FILENAME "UnitTest.dll" +#define VER_INTERNAL_NAME "setup" +#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests" diff --git a/src/libs/dutil/test/DUtilUnitTest/UriUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/UriUtilTest.cpp new file mode 100644 index 00000000..b3bf87a2 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/UriUtilTest.cpp @@ -0,0 +1,98 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace System::Text; +using namespace System::Collections::Generic; +using namespace Xunit; + +namespace CfgTests +{ + public ref class UriUtil + { + public: + [Fact] + void UriProtocolTest() + { + HRESULT hr = S_OK; + + DutilInitialize(&DutilTestTraceError); + + LPCWSTR uri = L"https://localhost/"; + URI_PROTOCOL uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); + + uri = L"HTTPS://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); + + uri = L"HtTpS://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); + + uri = L"HTTP://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); + + uri = L"http://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); + + uri = L"HtTp://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); + + uri = L"file://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); + + uri = L"FILE://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); + + uri = L"FiLe://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); + + uri = L"FTP://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); + + uri = L"ftp://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); + + uri = L"FtP://localhost/"; + uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; + hr = UriProtocol(uri, &uriProtocol); + ExitOnFailure(hr, "Failed to determine UriProtocol"); + Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); + + LExit: + DutilUninitialize(); + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/VerUtilTests.cpp b/src/libs/dutil/test/DUtilUnitTest/VerUtilTests.cpp new file mode 100644 index 00000000..8f24ad1a --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/VerUtilTests.cpp @@ -0,0 +1,933 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace Xunit; +using namespace WixBuildTools::TestSupport; + +namespace DutilTests +{ + public ref class VerUtil + { + public: + [Fact] + void VerCompareVersionsTreatsMissingRevisionAsZero() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + LPCWSTR wzVersion1 = L"1.2.3.4"; + LPCWSTR wzVersion2 = L"1.2.3"; + LPCWSTR wzVersion3 = L"1.2.3.0"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(2, pVersion1->dwMinor); + Assert::Equal(3, pVersion1->dwPatch); + Assert::Equal(4, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(7, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(2, pVersion2->dwMinor); + Assert::Equal(3, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(5, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(1, pVersion3->dwMajor); + Assert::Equal(2, pVersion3->dwMinor); + Assert::Equal(3, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(7, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + TestVerutilCompareParsedVersions(pVersion3, pVersion2, 0); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + } + } + + [Fact] + void VerCompareVersionsTreatsNumericReleaseLabelsAsNumbers() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + LPCWSTR wzVersion1 = L"1.0-2.0"; + LPCWSTR wzVersion2 = L"1.0-19"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(0, pVersion1->dwMinor); + Assert::Equal(0, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(2, pVersion1->cReleaseLabels); + + Assert::Equal(TRUE, pVersion1->rgReleaseLabels[0].fNumeric); + Assert::Equal(2, pVersion1->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); + Assert::Equal(4, pVersion1->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(TRUE, pVersion1->rgReleaseLabels[1].fNumeric); + Assert::Equal(0, pVersion1->rgReleaseLabels[1].dwValue); + Assert::Equal(1, pVersion1->rgReleaseLabels[1].cchLabel); + Assert::Equal(6, pVersion1->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(7, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(1, pVersion2->cReleaseLabels); + + Assert::Equal(TRUE, pVersion2->rgReleaseLabels[0].fNumeric); + Assert::Equal(19, pVersion2->rgReleaseLabels[0].dwValue); + Assert::Equal(2, pVersion2->rgReleaseLabels[0].cchLabel); + Assert::Equal(4, pVersion2->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(6, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, -1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + } + } + + [Fact] + void VerCompareVersionsHandlesNormallyInvalidVersions() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + VERUTIL_VERSION* pVersion4 = NULL; + VERUTIL_VERSION* pVersion5 = NULL; + VERUTIL_VERSION* pVersion6 = NULL; + LPCWSTR wzVersion1 = L"10.-4.0"; + LPCWSTR wzVersion2 = L"10.-2.0"; + LPCWSTR wzVersion3 = L"0"; + LPCWSTR wzVersion4 = L""; + LPCWSTR wzVersion5 = L"10-2"; + LPCWSTR wzVersion6 = L"10-4.@"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); + + hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5); + + hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(10, pVersion1->dwMajor); + Assert::Equal(0, pVersion1->dwMinor); + Assert::Equal(0, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(3, pVersion1->cchMetadataOffset); + Assert::Equal(TRUE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(10, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(3, pVersion2->cchMetadataOffset); + Assert::Equal(TRUE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(0, pVersion3->dwMajor); + Assert::Equal(0, pVersion3->dwMinor); + Assert::Equal(0, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(1, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); + Assert::Equal(0, pVersion4->dwMajor); + Assert::Equal(0, pVersion4->dwMinor); + Assert::Equal(0, pVersion4->dwPatch); + Assert::Equal(0, pVersion4->dwRevision); + Assert::Equal(0, pVersion4->cReleaseLabels); + Assert::Equal(0, pVersion4->cchMetadataOffset); + Assert::Equal(TRUE, pVersion4->fInvalid); + + NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion); + Assert::Equal(10, pVersion5->dwMajor); + Assert::Equal(0, pVersion5->dwMinor); + Assert::Equal(0, pVersion5->dwPatch); + Assert::Equal(0, pVersion5->dwRevision); + Assert::Equal(1, pVersion5->cReleaseLabels); + + Assert::Equal(TRUE, pVersion5->rgReleaseLabels[0].fNumeric); + Assert::Equal(2, pVersion5->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion5->rgReleaseLabels[0].cchLabel); + Assert::Equal(3, pVersion5->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(4, pVersion5->cchMetadataOffset); + Assert::Equal(FALSE, pVersion5->fInvalid); + + NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion); + Assert::Equal(10, pVersion6->dwMajor); + Assert::Equal(0, pVersion6->dwMinor); + Assert::Equal(0, pVersion6->dwPatch); + Assert::Equal(0, pVersion6->dwRevision); + Assert::Equal(1, pVersion6->cReleaseLabels); + + Assert::Equal(TRUE, pVersion6->rgReleaseLabels[0].fNumeric); + Assert::Equal(4, pVersion6->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion6->rgReleaseLabels[0].cchLabel); + Assert::Equal(3, pVersion6->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(5, pVersion6->cchMetadataOffset); + Assert::Equal(TRUE, pVersion6->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + TestVerutilCompareParsedVersions(pVersion3, pVersion4, 1); + TestVerutilCompareParsedVersions(pVersion5, pVersion6, -1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + ReleaseVerutilVersion(pVersion4); + ReleaseVerutilVersion(pVersion5); + ReleaseVerutilVersion(pVersion6); + } + } + + [Fact] + void VerCompareVersionsTreatsHyphenAsVersionSeparator() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + LPCWSTR wzVersion1 = L"0.0.1-a"; + LPCWSTR wzVersion2 = L"0-2"; + LPCWSTR wzVersion3 = L"1-2"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(0, pVersion1->dwMajor); + Assert::Equal(0, pVersion1->dwMinor); + Assert::Equal(1, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(1, pVersion1->cReleaseLabels); + + Assert::Equal(FALSE, pVersion1->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); + Assert::Equal(6, pVersion1->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(7, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(0, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(1, pVersion2->cReleaseLabels); + + Assert::Equal(TRUE, pVersion2->rgReleaseLabels[0].fNumeric); + Assert::Equal(2, pVersion2->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion2->rgReleaseLabels[0].cchLabel); + Assert::Equal(2, pVersion2->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(3, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(1, pVersion3->dwMajor); + Assert::Equal(0, pVersion3->dwMinor); + Assert::Equal(0, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(1, pVersion3->cReleaseLabels); + + Assert::Equal(TRUE, pVersion3->rgReleaseLabels[0].fNumeric); + Assert::Equal(2, pVersion3->rgReleaseLabels[0].dwValue); + Assert::Equal(1, pVersion3->rgReleaseLabels[0].cchLabel); + Assert::Equal(2, pVersion3->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(3, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + TestVerutilCompareParsedVersions(pVersion1, pVersion3, -1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + } + } + + [Fact] + void VerCompareVersionsIgnoresLeadingZeroes() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + VERUTIL_VERSION* pVersion4 = NULL; + LPCWSTR wzVersion1 = L"0.01-a.1"; + LPCWSTR wzVersion2 = L"0.1.0-a.1"; + LPCWSTR wzVersion3 = L"0.1-a.b.0"; + LPCWSTR wzVersion4 = L"0.1.0-a.b.000"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(0, pVersion1->dwMajor); + Assert::Equal(1, pVersion1->dwMinor); + Assert::Equal(0, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(2, pVersion1->cReleaseLabels); + + Assert::Equal(FALSE, pVersion1->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); + Assert::Equal(5, pVersion1->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(TRUE, pVersion1->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pVersion1->rgReleaseLabels[1].dwValue); + Assert::Equal(1, pVersion1->rgReleaseLabels[1].cchLabel); + Assert::Equal(7, pVersion1->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(8, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(0, pVersion2->dwMajor); + Assert::Equal(1, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(2, pVersion2->cReleaseLabels); + + Assert::Equal(FALSE, pVersion2->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion2->rgReleaseLabels[0].cchLabel); + Assert::Equal(6, pVersion2->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(TRUE, pVersion2->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pVersion2->rgReleaseLabels[1].dwValue); + Assert::Equal(1, pVersion2->rgReleaseLabels[1].cchLabel); + Assert::Equal(8, pVersion2->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(9, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(0, pVersion3->dwMajor); + Assert::Equal(1, pVersion3->dwMinor); + Assert::Equal(0, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(3, pVersion3->cReleaseLabels); + + Assert::Equal(FALSE, pVersion3->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion3->rgReleaseLabels[0].cchLabel); + Assert::Equal(4, pVersion3->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(FALSE, pVersion3->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pVersion3->rgReleaseLabels[1].cchLabel); + Assert::Equal(6, pVersion3->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(TRUE, pVersion3->rgReleaseLabels[2].fNumeric); + Assert::Equal(0, pVersion3->rgReleaseLabels[2].dwValue); + Assert::Equal(1, pVersion3->rgReleaseLabels[2].cchLabel); + Assert::Equal(8, pVersion3->rgReleaseLabels[2].cchLabelOffset); + + Assert::Equal(9, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); + Assert::Equal(0, pVersion4->dwMajor); + Assert::Equal(1, pVersion4->dwMinor); + Assert::Equal(0, pVersion4->dwPatch); + Assert::Equal(0, pVersion4->dwRevision); + Assert::Equal(3, pVersion4->cReleaseLabels); + + Assert::Equal(FALSE, pVersion4->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion4->rgReleaseLabels[0].cchLabel); + Assert::Equal(6, pVersion4->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(FALSE, pVersion4->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pVersion4->rgReleaseLabels[1].cchLabel); + Assert::Equal(8, pVersion4->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(TRUE, pVersion4->rgReleaseLabels[2].fNumeric); + Assert::Equal(0, pVersion4->rgReleaseLabels[2].dwValue); + Assert::Equal(3, pVersion4->rgReleaseLabels[2].cchLabel); + Assert::Equal(10, pVersion4->rgReleaseLabels[2].cchLabelOffset); + + Assert::Equal(13, pVersion4->cchMetadataOffset); + Assert::Equal(FALSE, pVersion4->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); + TestVerutilCompareParsedVersions(pVersion3, pVersion4, 0); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + ReleaseVerutilVersion(pVersion4); + } + } + + [Fact] + void VerCompareVersionsTreatsUnexpectedContentAsMetadata() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + LPCWSTR wzVersion1 = L"1.2.3+abcd"; + LPCWSTR wzVersion2 = L"1.2.3.abcd"; + LPCWSTR wzVersion3 = L"1.2.3.-abcd"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(2, pVersion1->dwMinor); + Assert::Equal(3, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(6, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(2, pVersion2->dwMinor); + Assert::Equal(3, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(6, pVersion2->cchMetadataOffset); + Assert::Equal(TRUE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(1, pVersion3->dwMajor); + Assert::Equal(2, pVersion3->dwMinor); + Assert::Equal(3, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(6, pVersion3->cchMetadataOffset); + Assert::Equal(TRUE, pVersion3->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + TestVerutilCompareParsedVersions(pVersion1, pVersion3, 1); + TestVerutilCompareParsedVersions(pVersion2, pVersion3, -1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + } + } + + [Fact] + void VerCompareVersionsIgnoresLeadingV() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + LPCWSTR wzVersion1 = L"10.20.30.40"; + LPCWSTR wzVersion2 = L"v10.20.30.40"; + LPCWSTR wzVersion3 = L"V10.20.30.40"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(10, pVersion1->dwMajor); + Assert::Equal(20, pVersion1->dwMinor); + Assert::Equal(30, pVersion1->dwPatch); + Assert::Equal(40, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(11, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion1, pVersion2->sczVersion); + Assert::Equal(10, pVersion2->dwMajor); + Assert::Equal(20, pVersion2->dwMinor); + Assert::Equal(30, pVersion2->dwPatch); + Assert::Equal(40, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(11, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion1, pVersion3->sczVersion); + Assert::Equal(10, pVersion3->dwMajor); + Assert::Equal(20, pVersion3->dwMinor); + Assert::Equal(30, pVersion3->dwPatch); + Assert::Equal(40, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(11, pVersion3->cchMetadataOffset); + Assert::Equal(FALSE, pVersion3->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); + TestVerutilCompareParsedVersions(pVersion1, pVersion3, 0); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + } + } + + [Fact] + void VerCompareVersionsHandlesTooLargeNumbers() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + LPCWSTR wzVersion1 = L"4294967295.4294967295.4294967295.4294967295"; + LPCWSTR wzVersion2 = L"4294967296.4294967296.4294967296.4294967296"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(4294967295, pVersion1->dwMajor); + Assert::Equal(4294967295, pVersion1->dwMinor); + Assert::Equal(4294967295, pVersion1->dwPatch); + Assert::Equal(4294967295, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(43, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(0, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(0, pVersion2->cchMetadataOffset); + Assert::Equal(TRUE, pVersion2->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + } + } + + [Fact] + void VerCompareVersionsIgnoresMetadataForValidVersions() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + LPCWSTR wzVersion1 = L"1.2.3+abc"; + LPCWSTR wzVersion2 = L"1.2.3+xyz"; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(2, pVersion1->dwMinor); + Assert::Equal(3, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(6, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(2, pVersion2->dwMinor); + Assert::Equal(3, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(6, pVersion2->cchMetadataOffset); + Assert::Equal(FALSE, pVersion2->fInvalid); + + TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + } + } + + [Fact] + void VerCopyVersionCopiesVersion() + { + HRESULT hr = S_OK; + LPCWSTR wzVersion = L"1.2.3.4+abc123"; + VERUTIL_VERSION* pSource = NULL; + VERUTIL_VERSION* pCopy = NULL; + int nResult = 0; + + try + { + hr = VerParseVersion(wzVersion, 0, FALSE, &pSource); + NativeAssert::Succeeded(hr, "VerParseVersion failed"); + + NativeAssert::StringEqual(wzVersion, pSource->sczVersion); + Assert::Equal(1, pSource->dwMajor); + Assert::Equal(2, pSource->dwMinor); + Assert::Equal(3, pSource->dwPatch); + Assert::Equal(4, pSource->dwRevision); + Assert::Equal(0, pSource->cReleaseLabels); + + Assert::Equal(8, pSource->cchMetadataOffset); + Assert::Equal(FALSE, pSource->fInvalid); + + hr = VerCopyVersion(pSource, &pCopy); + NativeAssert::Succeeded(hr, "VerCopyVersion failed"); + + Assert::False(pSource == pCopy); + Assert::False(pSource->sczVersion == pCopy->sczVersion); + + hr = VerCompareParsedVersions(pSource, pCopy, &nResult); + NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed"); + + Assert::Equal(nResult, 0); + } + finally + { + ReleaseVerutilVersion(pCopy); + ReleaseVerutilVersion(pSource); + } + } + + [Fact] + void VerCopyVersionCopiesPrereleaseVersion() + { + HRESULT hr = S_OK; + LPCWSTR wzVersion = L"1.2.3.4-a.b.c.d.5.+abc123"; + VERUTIL_VERSION* pSource = NULL; + VERUTIL_VERSION* pCopy = NULL; + int nResult = 0; + + try + { + hr = VerParseVersion(wzVersion, 0, FALSE, &pSource); + NativeAssert::Succeeded(hr, "VerParseVersion failed"); + + NativeAssert::StringEqual(wzVersion, pSource->sczVersion); + Assert::Equal(1, pSource->dwMajor); + Assert::Equal(2, pSource->dwMinor); + Assert::Equal(3, pSource->dwPatch); + Assert::Equal(4, pSource->dwRevision); + Assert::Equal(5, pSource->cReleaseLabels); + + Assert::Equal(FALSE, pSource->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pSource->rgReleaseLabels[0].cchLabel); + Assert::Equal(8, pSource->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(FALSE, pSource->rgReleaseLabels[1].fNumeric); + Assert::Equal(1, pSource->rgReleaseLabels[1].cchLabel); + Assert::Equal(10, pSource->rgReleaseLabels[1].cchLabelOffset); + + Assert::Equal(FALSE, pSource->rgReleaseLabels[2].fNumeric); + Assert::Equal(1, pSource->rgReleaseLabels[2].cchLabel); + Assert::Equal(12, pSource->rgReleaseLabels[2].cchLabelOffset); + + Assert::Equal(FALSE, pSource->rgReleaseLabels[3].fNumeric); + Assert::Equal(1, pSource->rgReleaseLabels[3].cchLabel); + Assert::Equal(14, pSource->rgReleaseLabels[3].cchLabelOffset); + + Assert::Equal(TRUE, pSource->rgReleaseLabels[4].fNumeric); + Assert::Equal(5, pSource->rgReleaseLabels[4].dwValue); + Assert::Equal(1, pSource->rgReleaseLabels[4].cchLabel); + Assert::Equal(16, pSource->rgReleaseLabels[4].cchLabelOffset); + + Assert::Equal(18, pSource->cchMetadataOffset); + Assert::Equal(TRUE, pSource->fInvalid); + + hr = VerCopyVersion(pSource, &pCopy); + NativeAssert::Succeeded(hr, "VerCopyVersion failed"); + + Assert::False(pSource == pCopy); + Assert::False(pSource->sczVersion == pCopy->sczVersion); + Assert::False(pSource->rgReleaseLabels == pCopy->rgReleaseLabels); + + hr = VerCompareParsedVersions(pSource, pCopy, &nResult); + NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed"); + + Assert::Equal(nResult, 0); + } + finally + { + ReleaseVerutilVersion(pCopy); + ReleaseVerutilVersion(pSource); + } + } + + [Fact] + void VerParseVersionTreatsTrailingDotsAsInvalid() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + VERUTIL_VERSION* pVersion2 = NULL; + VERUTIL_VERSION* pVersion3 = NULL; + VERUTIL_VERSION* pVersion4 = NULL; + VERUTIL_VERSION* pVersion5 = NULL; + VERUTIL_VERSION* pVersion6 = NULL; + VERUTIL_VERSION* pVersion7 = NULL; + LPCWSTR wzVersion1 = L"."; + LPCWSTR wzVersion2 = L"1."; + LPCWSTR wzVersion3 = L"2.1."; + LPCWSTR wzVersion4 = L"3.2.1."; + LPCWSTR wzVersion5 = L"4.3.2.1."; + LPCWSTR wzVersion6 = L"5-."; + LPCWSTR wzVersion7 = L"6-a."; + + try + { + hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); + + hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); + + hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); + + hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); + + hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5); + + hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6); + + hr = VerParseVersion(wzVersion7, 0, FALSE, &pVersion7); + NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion7); + + NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); + Assert::Equal(0, pVersion1->dwMajor); + Assert::Equal(0, pVersion1->dwMinor); + Assert::Equal(0, pVersion1->dwPatch); + Assert::Equal(0, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(0, pVersion1->cchMetadataOffset); + Assert::Equal(TRUE, pVersion1->fInvalid); + + NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); + Assert::Equal(1, pVersion2->dwMajor); + Assert::Equal(0, pVersion2->dwMinor); + Assert::Equal(0, pVersion2->dwPatch); + Assert::Equal(0, pVersion2->dwRevision); + Assert::Equal(0, pVersion2->cReleaseLabels); + Assert::Equal(2, pVersion2->cchMetadataOffset); + Assert::Equal(TRUE, pVersion2->fInvalid); + + NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); + Assert::Equal(2, pVersion3->dwMajor); + Assert::Equal(1, pVersion3->dwMinor); + Assert::Equal(0, pVersion3->dwPatch); + Assert::Equal(0, pVersion3->dwRevision); + Assert::Equal(0, pVersion3->cReleaseLabels); + Assert::Equal(4, pVersion3->cchMetadataOffset); + Assert::Equal(TRUE, pVersion3->fInvalid); + + NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); + Assert::Equal(3, pVersion4->dwMajor); + Assert::Equal(2, pVersion4->dwMinor); + Assert::Equal(1, pVersion4->dwPatch); + Assert::Equal(0, pVersion4->dwRevision); + Assert::Equal(0, pVersion4->cReleaseLabels); + Assert::Equal(6, pVersion4->cchMetadataOffset); + Assert::Equal(TRUE, pVersion4->fInvalid); + + NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion); + Assert::Equal(4, pVersion5->dwMajor); + Assert::Equal(3, pVersion5->dwMinor); + Assert::Equal(2, pVersion5->dwPatch); + Assert::Equal(1, pVersion5->dwRevision); + Assert::Equal(0, pVersion5->cReleaseLabels); + Assert::Equal(8, pVersion5->cchMetadataOffset); + Assert::Equal(TRUE, pVersion5->fInvalid); + + NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion); + Assert::Equal(5, pVersion6->dwMajor); + Assert::Equal(0, pVersion6->dwMinor); + Assert::Equal(0, pVersion6->dwPatch); + Assert::Equal(0, pVersion6->dwRevision); + Assert::Equal(0, pVersion6->cReleaseLabels); + Assert::Equal(2, pVersion6->cchMetadataOffset); + Assert::Equal(TRUE, pVersion6->fInvalid); + + NativeAssert::StringEqual(wzVersion7, pVersion7->sczVersion); + Assert::Equal(6, pVersion7->dwMajor); + Assert::Equal(0, pVersion7->dwMinor); + Assert::Equal(0, pVersion7->dwPatch); + Assert::Equal(0, pVersion7->dwRevision); + Assert::Equal(1, pVersion7->cReleaseLabels); + + Assert::Equal(FALSE, pVersion7->rgReleaseLabels[0].fNumeric); + Assert::Equal(1, pVersion7->rgReleaseLabels[0].cchLabel); + Assert::Equal(2, pVersion7->rgReleaseLabels[0].cchLabelOffset); + + Assert::Equal(4, pVersion7->cchMetadataOffset); + Assert::Equal(TRUE, pVersion7->fInvalid); + } + finally + { + ReleaseVerutilVersion(pVersion1); + ReleaseVerutilVersion(pVersion2); + ReleaseVerutilVersion(pVersion3); + ReleaseVerutilVersion(pVersion4); + ReleaseVerutilVersion(pVersion5); + ReleaseVerutilVersion(pVersion6); + ReleaseVerutilVersion(pVersion7); + } + } + + [Fact] + void VerVersionFromQwordCreatesVersion() + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion1 = NULL; + + try + { + hr = VerVersionFromQword(MAKEQWORDVERSION(1, 2, 3, 4), &pVersion1); + NativeAssert::Succeeded(hr, "VerVersionFromQword failed"); + + NativeAssert::StringEqual(L"1.2.3.4", pVersion1->sczVersion); + Assert::Equal(1, pVersion1->dwMajor); + Assert::Equal(2, pVersion1->dwMinor); + Assert::Equal(3, pVersion1->dwPatch); + Assert::Equal(4, pVersion1->dwRevision); + Assert::Equal(0, pVersion1->cReleaseLabels); + Assert::Equal(7, pVersion1->cchMetadataOffset); + Assert::Equal(FALSE, pVersion1->fInvalid); + } + finally + { + ReleaseVerutilVersion(pVersion1); + } + } + + private: + void TestVerutilCompareParsedVersions(VERUTIL_VERSION* pVersion1, VERUTIL_VERSION* pVersion2, int nExpectedResult) + { + HRESULT hr = S_OK; + int nResult = 0; + + hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult); + NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion); + + Assert::Equal(nExpectedResult, nResult); + + hr = VerCompareParsedVersions(pVersion2, pVersion1, &nResult); + NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion); + + Assert::Equal(nExpectedResult, -nResult); + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/error.cpp b/src/libs/dutil/test/DUtilUnitTest/error.cpp new file mode 100644 index 00000000..e51971c3 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/error.cpp @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +const int ERROR_STRING_BUFFER = 1024; + +static char szMsg[ERROR_STRING_BUFFER]; +static WCHAR wzMsg[ERROR_STRING_BUFFER]; + +void CALLBACK DutilTestTraceError( + __in_z LPCSTR /*szFile*/, + __in int /*iLine*/, + __in REPORT_LEVEL /*rl*/, + __in UINT source, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + if (DUTIL_SOURCE_EXTERNAL == source) + { + ::StringCchPrintfA(szMsg, countof(szMsg), szFormat, args); + MultiByteToWideChar(CP_ACP, 0, szMsg, -1, wzMsg, countof(wzMsg)); + throw gcnew System::Exception(System::String::Format("hr = 0x{0:X8}, message = {1}", hrError, gcnew System::String(wzMsg))); + } +} diff --git a/src/libs/dutil/test/DUtilUnitTest/error.h b/src/libs/dutil/test/DUtilUnitTest/error.h new file mode 100644 index 00000000..b973acaf --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/error.h @@ -0,0 +1,14 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL + +void CALLBACK DutilTestTraceError( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in UINT source, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); diff --git a/src/libs/dutil/test/DUtilUnitTest/packages.config b/src/libs/dutil/test/DUtilUnitTest/packages.config new file mode 100644 index 00000000..a4fef2bf --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/packages.config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libs/dutil/test/DUtilUnitTest/precomp.cpp b/src/libs/dutil/test/DUtilUnitTest/precomp.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/precomp.cpp @@ -0,0 +1,3 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" diff --git a/src/libs/dutil/test/DUtilUnitTest/precomp.h b/src/libs/dutil/test/DUtilUnitTest/precomp.h new file mode 100644 index 00000000..e9f8770b --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/precomp.h @@ -0,0 +1,32 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include + +// Include error.h before dutil.h +#include +#include "error.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // NOTE: this must come after atomutil.h and rssutil.h since it uses them. +#include +#include + +#pragma managed +#include diff --git a/src/libs/dutil/test/DUtilUnitTest/resource.h b/src/libs/dutil/test/DUtilUnitTest/resource.h new file mode 100644 index 00000000..bdf252f6 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/resource.h @@ -0,0 +1,2 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + diff --git a/src/signing.json b/src/signing.json new file mode 100644 index 00000000..fe1c8c9b --- /dev/null +++ b/src/signing.json @@ -0,0 +1,13 @@ +{ + "SignClient": { + "AzureAd": { + "AADInstance": "https://login.microsoftonline.com/", + "ClientId": "c248d68a-ba6f-4aa9-8a68-71fe872063f8", + "TenantId": "16076fdc-fcc1-4a15-b1ca-32c9a255900e" + }, + "Service": { + "Url": "https://codesign.dotnetfoundation.org/", + "ResourceId": "https://SignService/3c30251f-36f3-490b-a955-520addb85001" + } + } +} diff --git a/src/test/DUtilUnitTest/ApupUtilTests.cpp b/src/test/DUtilUnitTest/ApupUtilTests.cpp deleted file mode 100644 index 30a45f5a..00000000 --- a/src/test/DUtilUnitTest/ApupUtilTests.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class ApupUtil - { - public: - [Fact] - void AllocChainFromAtomSortsDescending() - { - HRESULT hr = S_OK; - ATOM_FEED* pFeed = NULL; - APPLICATION_UPDATE_CHAIN* pChain = NULL; - - DutilInitialize(&DutilTestTraceError); - - try - { - XmlInitialize(); - NativeAssert::Succeeded(hr, "Failed to initialize Xml."); - - pin_ptr feedFilePath = PtrToStringChars(TestData::Get("TestData", "ApupUtilTests", "FeedBv2.0.xml")); - hr = AtomParseFromFile(feedFilePath, &pFeed); - NativeAssert::Succeeded(hr, "Failed to parse feed: {0}", feedFilePath); - - hr = ApupAllocChainFromAtom(pFeed, &pChain); - NativeAssert::Succeeded(hr, "Failed to get chain from feed."); - - Assert::Equal(3ul, pChain->cEntries); - NativeAssert::StringEqual(L"Bundle v2.0", pChain->rgEntries[0].wzTitle); - NativeAssert::StringEqual(L"Bundle v1.0", pChain->rgEntries[1].wzTitle); - NativeAssert::StringEqual(L"Bundle v1.0-preview", pChain->rgEntries[2].wzTitle); - } - finally - { - DutilUninitialize(); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/AssemblyInfo.cpp b/src/test/DUtilUnitTest/AssemblyInfo.cpp deleted file mode 100644 index 2d527910..00000000 --- a/src/test/DUtilUnitTest/AssemblyInfo.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System::Reflection; -using namespace System::Runtime::CompilerServices; -using namespace System::Runtime::InteropServices; - -[assembly: AssemblyTitleAttribute("Windows Installer XML Dutil unit tests")]; -[assembly: AssemblyDescriptionAttribute("Dutil unit tests")]; -[assembly: AssemblyCultureAttribute("")]; -[assembly: ComVisible(false)]; diff --git a/src/test/DUtilUnitTest/DUtilTests.cpp b/src/test/DUtilUnitTest/DUtilTests.cpp deleted file mode 100644 index 55e81d46..00000000 --- a/src/test/DUtilUnitTest/DUtilTests.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class DUtil - { - public: - [Fact] - void DUtilTraceErrorSourceFiltersOnTraceLevel() - { - DutilInitialize(&DutilTestTraceError); - - CallDutilTraceErrorSource(); - - Dutil_TraceSetLevel(REPORT_DEBUG, FALSE); - - Action^ action = gcnew Action(this, &DUtil::CallDutilTraceErrorSource); - Assert::Throws(action); - - DutilUninitialize(); - } - - private: - void CallDutilTraceErrorSource() - { - Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_DEBUG, DUTIL_SOURCE_EXTERNAL, E_FAIL, "Error message"); - } - }; -} diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj deleted file mode 100644 index 18410e5d..00000000 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - Debug - Win32 - - - Release - Win32 - - - - - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} - {AB7EE608-E5FB-42A5-831F-0DEEEA141223} - DUtilUnitTests - ManagedCProj - DynamicLibrary - Unicode - true - false - - - - - - - ..\..\dutil\inc - rpcrt4.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib - - - - - - - - - - - - - - - - - Create - - 4564;4691 - - - - - - - - - - - - - - - - - - - - - - - - - - ..\..\..\packages\WixBuildTools.TestSupport.4.0.47\lib\net472\WixBuildTools.TestSupport.dll - - - ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\lib\net472\WixBuildTools.TestSupport.Native.dll - - - - - - {1244E671-F108-4334-BA52-8A7517F26ECD} - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters deleted file mode 100644 index 4df7af89..00000000 --- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters +++ /dev/null @@ -1,80 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Resource Files - - - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/src/test/DUtilUnitTest/DictUtilTest.cpp b/src/test/DUtilUnitTest/DictUtilTest.cpp deleted file mode 100644 index 4b4777d7..00000000 --- a/src/test/DUtilUnitTest/DictUtilTest.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -const DWORD numIterations = 100000; - -namespace DutilTests -{ - struct Value - { - DWORD dwNum; - LPWSTR sczKey; - }; - - public ref class DictUtil - { - public: - [Fact] - void DictUtilTest() - { - DutilInitialize(&DutilTestTraceError); - - EmbeddedKeyTestHelper(DICT_FLAG_NONE, numIterations); - - EmbeddedKeyTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations); - - StringListTestHelper(DICT_FLAG_NONE, numIterations); - - StringListTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations); - - DutilUninitialize(); - } - - private: - void EmbeddedKeyTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations) - { - HRESULT hr = S_OK; - Value *rgValues = NULL; - Value *valueFound = NULL; - DWORD cValues = 0; - LPWSTR sczExpectedKey = NULL; - STRINGDICT_HANDLE sdValues = NULL; - - try - { - hr = DictCreateWithEmbeddedKey(&sdValues, 0, (void **)&rgValues, offsetof(Value, sczKey), dfFlags); - NativeAssert::Succeeded(hr, "Failed to create dictionary of values"); - - for (DWORD i = 0; i < dwNumIterations; ++i) - { - cValues++; - - hr = MemEnsureArraySize((void **)&rgValues, cValues, sizeof(Value), 5); - NativeAssert::Succeeded(hr, "Failed to grow value array"); - - hr = StrAllocFormatted(&rgValues[i].sczKey, L"%u_a_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i); - - hr = DictAddValue(sdValues, rgValues + i); - NativeAssert::Succeeded(hr, "Failed to add item {0} to dict", i); - } - - for (DWORD i = 0; i < dwNumIterations; ++i) - { - hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i); - - hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); - NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); - - NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey); - - hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i); - - hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); - - if (dfFlags & DICT_FLAG_CASEINSENSITIVE) - { - NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); - - NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey, TRUE); - } - else - { - if (E_NOTFOUND != hr) - { - hr = E_FAIL; - ExitOnFailure(hr, "This embedded key is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey); - } - } - - hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i); - - hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound); - if (E_NOTFOUND != hr) - { - hr = E_FAIL; - ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey); - } - } - } - finally - { - for (DWORD i = 0; i < cValues; ++i) - { - ReleaseStr(rgValues[i].sczKey); - } - ReleaseMem(rgValues); - ReleaseStr(sczExpectedKey); - ReleaseDict(sdValues); - } - - LExit: - return; - } - - void StringListTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations) - { - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - LPWSTR sczExpectedKey = NULL; - STRINGDICT_HANDLE sdValues = NULL; - - try - { - hr = DictCreateStringList(&sdValues, 0, dfFlags); - NativeAssert::Succeeded(hr, "Failed to create dictionary of keys"); - - for (DWORD i = 0; i < dwNumIterations; ++i) - { - hr = StrAllocFormatted(&sczKey, L"%u_a_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i); - - hr = DictAddKey(sdValues, sczKey); - NativeAssert::Succeeded(hr, "Failed to add key {0} to dict", i); - } - - for (DWORD i = 0; i < dwNumIterations; ++i) - { - hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i); - - hr = DictKeyExists(sdValues, sczExpectedKey); - NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); - - hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i); - - hr = DictKeyExists(sdValues, sczExpectedKey); - if (dfFlags & DICT_FLAG_CASEINSENSITIVE) - { - NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey); - } - else - { - if (E_NOTFOUND != hr) - { - hr = E_FAIL; - ExitOnFailure(hr, "This stringlist dict is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey); - } - } - - hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i); - NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i); - - hr = DictKeyExists(sdValues, sczExpectedKey); - if (E_NOTFOUND != hr) - { - hr = E_FAIL; - ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey); - } - } - } - finally - { - ReleaseStr(sczKey); - ReleaseStr(sczExpectedKey); - ReleaseDict(sdValues); - } - - LExit: - return; - } - }; -} diff --git a/src/test/DUtilUnitTest/DirUtilTests.cpp b/src/test/DUtilUnitTest/DirUtilTests.cpp deleted file mode 100644 index 7643366f..00000000 --- a/src/test/DUtilUnitTest/DirUtilTests.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class DirUtil - { - public: - [Fact] - void DirUtilTest() - { - HRESULT hr = S_OK; - LPWSTR sczCurrentDir = NULL; - LPWSTR sczGuid = NULL; - LPWSTR sczFolder = NULL; - LPWSTR sczSubFolder = NULL; - - try - { - hr = GuidCreate(&sczGuid); - NativeAssert::Succeeded(hr, "Failed to create guid."); - - hr = DirGetCurrent(&sczCurrentDir); - NativeAssert::Succeeded(hr, "Failed to get current directory."); - - hr = PathConcat(sczCurrentDir, sczGuid, &sczFolder); - NativeAssert::Succeeded(hr, "Failed to combine current directory: '{0}' with Guid: '{1}'", sczCurrentDir, sczGuid); - - BOOL fExists = DirExists(sczFolder, NULL); - Assert::False(fExists == TRUE); - - hr = PathConcat(sczFolder, L"foo", &sczSubFolder); - NativeAssert::Succeeded(hr, "Failed to combine folder: '%ls' with subfolder: 'foo'", sczFolder); - - hr = DirEnsureExists(sczSubFolder, NULL); - NativeAssert::Succeeded(hr, "Failed to create multiple directories: %ls", sczSubFolder); - - // Test failure to delete non-empty folder. - hr = DirEnsureDelete(sczFolder, FALSE, FALSE); - Assert::Equal(HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY), hr); - - hr = DirEnsureDelete(sczSubFolder, FALSE, FALSE); - NativeAssert::Succeeded(hr, "Failed to delete single directory: %ls", sczSubFolder); - - // Put the directory back and we'll test deleting tree. - hr = DirEnsureExists(sczSubFolder, NULL); - NativeAssert::Succeeded(hr, "Failed to create single directory: %ls", sczSubFolder); - - hr = DirEnsureDelete(sczFolder, FALSE, TRUE); - NativeAssert::Succeeded(hr, "Failed to delete directory tree: %ls", sczFolder); - - // Finally, try to create "C:\" which would normally fail, but we want success - hr = DirEnsureExists(L"C:\\", NULL); - NativeAssert::Succeeded(hr, "Failed to create C:\\"); - } - finally - { - ReleaseStr(sczSubFolder); - ReleaseStr(sczFolder); - ReleaseStr(sczGuid); - ReleaseStr(sczCurrentDir); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/FileUtilTest.cpp b/src/test/DUtilUnitTest/FileUtilTest.cpp deleted file mode 100644 index ac071ef2..00000000 --- a/src/test/DUtilUnitTest/FileUtilTest.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class FileUtil - { - public: - [Fact(Skip="Skipped until we have a good way to reference ANSI.txt.")] - void FileUtilTest() - { - HRESULT hr = S_OK; - LPWSTR sczTempDir = NULL; - LPWSTR sczFileDir = NULL; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = PathExpand(&sczTempDir, L"%TEMP%\\FileUtilTest\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::Succeeded(hr, "Failed to get temp dir"); - - hr = PathExpand(&sczFileDir, L"%WIX_ROOT%\\examples\\data\\TextEncodings\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::Succeeded(hr, "Failed to get path to encodings file dir"); - - hr = DirEnsureExists(sczTempDir, NULL); - NativeAssert::Succeeded(hr, "Failed to ensure directory exists: {0}", sczTempDir); - - TestFile(sczFileDir, sczTempDir, L"ANSI.txt", 32, FILE_ENCODING_UTF8); - // Big endian not supported today! - //TestFile(sczFileDir, L"UnicodeBENoBOM.txt", 34); - //TestFile(sczFileDir, L"UnicodeBEWithBOM.txt", 34); - TestFile(sczFileDir, sczTempDir, L"UnicodeLENoBOM.txt", 34, FILE_ENCODING_UTF16); - TestFile(sczFileDir, sczTempDir, L"UnicodeLEWithBOM.txt", 34, FILE_ENCODING_UTF16_WITH_BOM); - TestFile(sczFileDir, sczTempDir, L"UTF8WithSignature.txt", 34, FILE_ENCODING_UTF8_WITH_BOM); - - hr = DirEnsureDelete(sczTempDir, TRUE, TRUE); - } - finally - { - ReleaseStr(sczTempDir); - ReleaseStr(sczFileDir); - DutilUninitialize(); - } - } - - private: - void TestFile(LPWSTR wzDir, LPCWSTR wzTempDir, LPWSTR wzFileName, size_t cbExpectedStringLength, FILE_ENCODING feExpectedEncoding) - { - HRESULT hr = S_OK; - LPWSTR sczFullPath = NULL; - LPWSTR sczContents = NULL; - LPWSTR sczOutputPath = NULL; - FILE_ENCODING feEncodingFound = FILE_ENCODING_UNSPECIFIED; - BYTE *pbFile1 = NULL; - DWORD cbFile1 = 0; - BYTE *pbFile2 = NULL; - DWORD cbFile2 = 0; - size_t cbActualStringLength = 0; - - try - { - hr = PathConcat(wzDir, wzFileName, &sczFullPath); - NativeAssert::Succeeded(hr, "Failed to create path to test file: {0}", sczFullPath); - - hr = FileToString(sczFullPath, &sczContents, &feEncodingFound); - hr = E_FAIL; - NativeAssert::Succeeded(hr, "Failed to read text from file: {0}", sczFullPath); - - if (!sczContents) - { - hr = E_FAIL; - NativeAssert::Succeeded(hr, "FileToString() returned NULL for file: {0}", sczFullPath); - } - - hr = ::StringCchLengthW(sczContents, STRSAFE_MAX_CCH, &cbActualStringLength); - NativeAssert::Succeeded(hr, "Failed to get length of text from file: {0}", sczFullPath); - - if (cbActualStringLength != cbExpectedStringLength) - { - hr = E_FAIL; - ExitOnFailure(hr, "FileToString() returned wrong size for file: %ls (expected size %Iu, found size %Iu)", sczFullPath, cbExpectedStringLength, cbActualStringLength); - } - - if (feEncodingFound != feExpectedEncoding) - { - hr = E_FAIL; - ExitOnFailure(hr, "FileToString() returned unexpected encoding type for file: %ls (expected type %u, found type %u)", sczFullPath, feExpectedEncoding, feEncodingFound); - } - - hr = PathConcat(wzTempDir, wzFileName, &sczOutputPath); - NativeAssert::Succeeded(hr, "Failed to get output path"); - - hr = FileFromString(sczOutputPath, 0, sczContents, feExpectedEncoding); - NativeAssert::Succeeded(hr, "Failed to write contents of file back out to disk"); - - hr = FileRead(&pbFile1, &cbFile1, sczFullPath); - NativeAssert::Succeeded(hr, "Failed to read input file as binary"); - - hr = FileRead(&pbFile2, &cbFile2, sczOutputPath); - NativeAssert::Succeeded(hr, "Failed to read output file as binary"); - - if (cbFile1 != cbFile2 || 0 != memcmp(pbFile1, pbFile2, cbFile1)) - { - hr = E_FAIL; - ExitOnFailure(hr, "Outputted file doesn't match input file: \"%ls\" and \"%ls\"", sczFullPath, sczOutputPath); - } - } - finally - { - ReleaseStr(sczOutputPath); - ReleaseStr(sczFullPath); - ReleaseStr(sczContents); - } - - LExit: - return; - } - }; -} diff --git a/src/test/DUtilUnitTest/GuidUtilTest.cpp b/src/test/DUtilUnitTest/GuidUtilTest.cpp deleted file mode 100644 index a6e27a09..00000000 --- a/src/test/DUtilUnitTest/GuidUtilTest.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class GuidUtil - { - public: - [Fact] - void GuidCreateTest() - { - HRESULT hr = S_OK; - WCHAR wzGuid1[GUID_STRING_LENGTH]; - WCHAR wzGuid2[GUID_STRING_LENGTH]; - - hr = GuidFixedCreate(wzGuid1); - NativeAssert::Succeeded(hr, "Failed to create first guid."); - Guid firstGuid = Guid::Parse(gcnew String(wzGuid1)); - - hr = GuidFixedCreate(wzGuid2); - NativeAssert::Succeeded(hr, "Failed to create second guid."); - Guid secondGuid = Guid::Parse(gcnew String(wzGuid2)); - - NativeAssert::NotStringEqual(wzGuid1, wzGuid2); - NativeAssert::NotEqual(firstGuid, secondGuid); - } - - [Fact] - void GuidCreateSczTest() - { - HRESULT hr = S_OK; - LPWSTR sczGuid1 = NULL; - LPWSTR sczGuid2 = NULL; - - try - { - hr = GuidCreate(&sczGuid1); - NativeAssert::Succeeded(hr, "Failed to create first guid."); - Guid firstGuid = Guid::Parse(gcnew String(sczGuid1)); - - hr = GuidCreate(&sczGuid2); - NativeAssert::Succeeded(hr, "Failed to create second guid."); - Guid secondGuid = Guid::Parse(gcnew String(sczGuid2)); - - NativeAssert::NotStringEqual(sczGuid1, sczGuid2); - NativeAssert::NotEqual(firstGuid, secondGuid); - } - finally - { - ReleaseStr(sczGuid1); - ReleaseStr(sczGuid2); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/IniUtilTest.cpp b/src/test/DUtilUnitTest/IniUtilTest.cpp deleted file mode 100644 index 946f19c5..00000000 --- a/src/test/DUtilUnitTest/IniUtilTest.cpp +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -typedef HRESULT (__clrcall *IniFormatParameters)( - INI_HANDLE - ); - -namespace DutilTests -{ - public ref class IniUtil - { - public: - [Fact] - void IniUtilTest() - { - HRESULT hr = S_OK; - LPWSTR sczTempIniFilePath = NULL; - LPWSTR sczTempIniFileDir = NULL; - LPWSTR wzIniContents = L" PlainValue = \t Blah \r\n;CommentHere\r\n[Section1]\r\n ;Another Comment With = Equal Sign\r\nSection1ValueA=Foo\r\n\r\nSection1ValueB=Bar\r\n[Section2]\r\nSection2ValueA=Cha\r\nArray[0]=Arr\r\n"; - LPWSTR wzScriptContents = L"setf ~PlainValue Blah\r\n;CommentHere\r\n\r\nsetf ~Section1\\Section1ValueA Foo\r\n\r\nsetf ~Section1\\Section1ValueB Bar\r\nsetf ~Section2\\Section2ValueA Cha\r\nsetf ~Section2\\Array[0] Arr\r\n"; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = PathExpand(&sczTempIniFilePath, L"%TEMP%\\IniUtilTest\\Test.ini", PATH_EXPAND_ENVIRONMENT); - NativeAssert::Succeeded(hr, "Failed to get path to temp INI file"); - - hr = PathGetDirectory(sczTempIniFilePath, &sczTempIniFileDir); - NativeAssert::Succeeded(hr, "Failed to get directory to temp INI file"); - - hr = DirEnsureDelete(sczTempIniFileDir, TRUE, TRUE); - if (E_PATHNOTFOUND == hr) - { - hr = S_OK; - } - NativeAssert::Succeeded(hr, "Failed to delete IniUtilTest directory: {0}", sczTempIniFileDir); - - hr = DirEnsureExists(sczTempIniFileDir, NULL); - NativeAssert::Succeeded(hr, "Failed to ensure temp directory exists: {0}", sczTempIniFileDir); - - // Tests parsing, then modifying a regular INI file - TestReadThenWrite(sczTempIniFilePath, StandardIniFormat, wzIniContents); - - // Tests programmatically creating from scratch, then parsing an INI file - TestWriteThenRead(sczTempIniFilePath, StandardIniFormat); - - // Tests parsing, then modifying a regular INI file - TestReadThenWrite(sczTempIniFilePath, ScriptFormat, wzScriptContents); - - // Tests programmatically creating from scratch, then parsing an INI file - TestWriteThenRead(sczTempIniFilePath, ScriptFormat); - } - finally - { - ReleaseStr(sczTempIniFilePath); - ReleaseStr(sczTempIniFileDir); - DutilUninitialize(); - } - } - - private: - void AssertValue(INI_HANDLE iniHandle, LPCWSTR wzValueName, LPCWSTR wzValue) - { - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - try - { - hr = IniGetValue(iniHandle, wzValueName, &sczValue); - NativeAssert::Succeeded(hr, "Failed to get ini value: {0}", wzValueName); - - if (0 != wcscmp(sczValue, wzValue)) - { - hr = E_FAIL; - ExitOnFailure(hr, "Expected to find value in INI: '%ls'='%ls' - but found value '%ls' instead", wzValueName, wzValue, sczValue); - } - } - finally - { - ReleaseStr(sczValue); - } - - LExit: - return; - } - - void AssertNoValue(INI_HANDLE iniHandle, LPCWSTR wzValueName) - { - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - try - { - hr = IniGetValue(iniHandle, wzValueName, &sczValue); - if (E_NOTFOUND != hr) - { - if (SUCCEEDED(hr)) - { - hr = E_FAIL; - } - ExitOnFailure(hr, "INI value shouldn't have been found: %ls", wzValueName); - } - } - finally - { - ReleaseStr(sczValue); - } - - LExit: - return; - } - - static HRESULT StandardIniFormat(__inout INI_HANDLE iniHandle) - { - HRESULT hr = S_OK; - - hr = IniSetOpenTag(iniHandle, L"[", L"]"); - NativeAssert::Succeeded(hr, "Failed to set open tag settings on ini handle"); - - hr = IniSetValueStyle(iniHandle, NULL, L"="); - NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle"); - - hr = IniSetCommentStyle(iniHandle, L";"); - NativeAssert::Succeeded(hr, "Failed to set comment style setting on ini handle"); - - return hr; - } - - static HRESULT ScriptFormat(__inout INI_HANDLE iniHandle) - { - HRESULT hr = S_OK; - - hr = IniSetValueStyle(iniHandle, L"setf ~", L" "); - NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle"); - - return hr; - } - - void TestReadThenWrite(LPWSTR wzIniFilePath, IniFormatParameters SetFormat, LPCWSTR wzContents) - { - HRESULT hr = S_OK; - INI_HANDLE iniHandle = NULL; - INI_HANDLE iniHandle2 = NULL; - INI_VALUE *rgValues = NULL; - DWORD cValues = 0; - - try - { - hr = FileWrite(wzIniFilePath, 0, reinterpret_cast(wzContents), lstrlenW(wzContents) * sizeof(WCHAR), NULL); - NativeAssert::Succeeded(hr, "Failed to write out INI file"); - - hr = IniInitialize(&iniHandle); - NativeAssert::Succeeded(hr, "Failed to initialize INI object"); - - hr = SetFormat(iniHandle); - NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); - - hr = IniParse(iniHandle, wzIniFilePath, NULL); - NativeAssert::Succeeded(hr, "Failed to parse INI file"); - - hr = IniGetValueList(iniHandle, &rgValues, &cValues); - NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); - - NativeAssert::Equal(5, cValues); - - AssertValue(iniHandle, L"PlainValue", L"Blah"); - AssertNoValue(iniHandle, L"PlainValue2"); - AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo"); - AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar"); - AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha"); - AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist"); - AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr"); - - hr = IniSetValue(iniHandle, L"PlainValue2", L"Blah2"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Section1\\CreatedValue", L"Woo"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arrmod"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniGetValueList(iniHandle, &rgValues, &cValues); - NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); - - NativeAssert::Equal(7, cValues); - - AssertValue(iniHandle, L"PlainValue", L"Blah"); - AssertValue(iniHandle, L"PlainValue2", L"Blah2"); - AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo"); - AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar"); - AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha"); - AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist"); - AssertValue(iniHandle, L"Section1\\CreatedValue", L"Woo"); - AssertValue(iniHandle, L"Section2\\Array[0]", L"Arrmod"); - - // Try deleting a value as well - hr = IniSetValue(iniHandle, L"Section1\\Section1ValueB", NULL); - NativeAssert::Succeeded(hr, "Failed to kill value in INI"); - - hr = IniWriteFile(iniHandle, NULL, FILE_ENCODING_UNSPECIFIED); - NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk"); - - ReleaseNullIni(iniHandle); - // Now re-parse the INI we just wrote and make sure it matches the values we expect - hr = IniInitialize(&iniHandle2); - NativeAssert::Succeeded(hr, "Failed to initialize INI object"); - - hr = SetFormat(iniHandle2); - NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); - - hr = IniParse(iniHandle2, wzIniFilePath, NULL); - NativeAssert::Succeeded(hr, "Failed to parse INI file"); - - hr = IniGetValueList(iniHandle2, &rgValues, &cValues); - NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); - - NativeAssert::Equal(6, cValues); - - AssertValue(iniHandle2, L"PlainValue", L"Blah"); - AssertValue(iniHandle2, L"PlainValue2", L"Blah2"); - AssertValue(iniHandle2, L"Section1\\Section1ValueA", L"Foo"); - AssertNoValue(iniHandle2, L"Section1\\Section1ValueB"); - AssertValue(iniHandle2, L"Section2\\Section2ValueA", L"Cha"); - AssertNoValue(iniHandle2, L"Section1\\ValueDoesntExist"); - AssertValue(iniHandle2, L"Section1\\CreatedValue", L"Woo"); - AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arrmod"); - } - finally - { - ReleaseIni(iniHandle); - ReleaseIni(iniHandle2); - } - } - - void TestWriteThenRead(LPWSTR wzIniFilePath, IniFormatParameters SetFormat) - { - HRESULT hr = S_OK; - INI_HANDLE iniHandle = NULL; - INI_HANDLE iniHandle2 = NULL; - INI_VALUE *rgValues = NULL; - DWORD cValues = 0; - - try - { - hr = FileEnsureDelete(wzIniFilePath); - NativeAssert::Succeeded(hr, "Failed to ensure file is deleted"); - - hr = IniInitialize(&iniHandle); - NativeAssert::Succeeded(hr, "Failed to initialize INI object"); - - hr = SetFormat(iniHandle); - NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); - - hr = IniGetValueList(iniHandle, &rgValues, &cValues); - NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); - - NativeAssert::Equal(0, cValues); - - hr = IniSetValue(iniHandle, L"Value1", L"BlahTypo"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Value2", L"Blah2"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Section1\\Value1", L"Section1Value1"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Section1\\Value2", L"Section1Value2"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Section2\\Value1", L"Section2Value1"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arr"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Value3", L"Blah3"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Value4", L"Blah4"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Value4", NULL); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniSetValue(iniHandle, L"Value1", L"Blah1"); - NativeAssert::Succeeded(hr, "Failed to set value in INI"); - - hr = IniGetValueList(iniHandle, &rgValues, &cValues); - NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); - - NativeAssert::Equal(8, cValues); - - AssertValue(iniHandle, L"Value1", L"Blah1"); - AssertValue(iniHandle, L"Value2", L"Blah2"); - AssertValue(iniHandle, L"Value3", L"Blah3"); - AssertNoValue(iniHandle, L"Value4"); - AssertValue(iniHandle, L"Section1\\Value1", L"Section1Value1"); - AssertValue(iniHandle, L"Section1\\Value2", L"Section1Value2"); - AssertValue(iniHandle, L"Section2\\Value1", L"Section2Value1"); - AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr"); - - hr = IniWriteFile(iniHandle, wzIniFilePath, FILE_ENCODING_UNSPECIFIED); - NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk"); - - ReleaseNullIni(iniHandle); - // Now re-parse the INI we just wrote and make sure it matches the values we expect - hr = IniInitialize(&iniHandle2); - NativeAssert::Succeeded(hr, "Failed to initialize INI object"); - - hr = SetFormat(iniHandle2); - NativeAssert::Succeeded(hr, "Failed to set parameters for INI file"); - - hr = IniParse(iniHandle2, wzIniFilePath, NULL); - NativeAssert::Succeeded(hr, "Failed to parse INI file"); - - hr = IniGetValueList(iniHandle2, &rgValues, &cValues); - NativeAssert::Succeeded(hr, "Failed to get list of values in INI"); - - NativeAssert::Equal(7, cValues); - - AssertValue(iniHandle2, L"Value1", L"Blah1"); - AssertValue(iniHandle2, L"Value2", L"Blah2"); - AssertValue(iniHandle2, L"Value3", L"Blah3"); - AssertNoValue(iniHandle2, L"Value4"); - AssertValue(iniHandle2, L"Section1\\Value1", L"Section1Value1"); - AssertValue(iniHandle2, L"Section1\\Value2", L"Section1Value2"); - AssertValue(iniHandle2, L"Section2\\Value1", L"Section2Value1"); - AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arr"); - } - finally - { - ReleaseIni(iniHandle); - ReleaseIni(iniHandle2); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/MemUtilTest.cpp b/src/test/DUtilUnitTest/MemUtilTest.cpp deleted file mode 100644 index 09692bfb..00000000 --- a/src/test/DUtilUnitTest/MemUtilTest.cpp +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - struct ArrayValue - { - DWORD dwNum; - void *pvNull1; - LPWSTR sczString; - void *pvNull2; - }; - - public ref class MemUtil - { - public: - [Fact] - void MemUtilAppendTest() - { - HRESULT hr = S_OK; - DWORD dwSize; - ArrayValue *rgValues = NULL; - DWORD cValues = 0; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 1"); - ++cValues; - SetItem(rgValues + 0, 0); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 2"); - ++cValues; - SetItem(rgValues + 1, 1); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 3"); - ++cValues; - SetItem(rgValues + 2, 2); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 4"); - ++cValues; - SetItem(rgValues + 3, 3); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 5"); - ++cValues; - SetItem(rgValues + 4, 4); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 6"); - ++cValues; - SetItem(rgValues + 5, 5); - - // OK, we used growth size 5, so let's try ensuring we have space for 6 (5 + first item) items - // and make sure it doesn't grow since we already have enough space - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to ensure array size matches what it should already be"); - dwSize = MemSize(rgValues); - if (dwSize != 6 * sizeof(ArrayValue)) - { - hr = E_FAIL; - ExitOnFailure(hr, "MemEnsureArraySize is growing an array that is already big enough!"); - } - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); - ++cValues; - SetItem(rgValues + 6, 6); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); - ++cValues; - SetItem(rgValues + 7, 7); - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); - ++cValues; - SetItem(rgValues + 8, 8); - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - } - finally - { - ReleaseMem(rgValues); - } - - LExit: - DutilUninitialize(); - } - - [Fact] - void MemUtilInsertTest() - { - HRESULT hr = S_OK; - ArrayValue *rgValues = NULL; - DWORD cValues = 0; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of empty array"); - ++cValues; - CheckNullItem(rgValues + 0); - SetItem(rgValues + 0, 5); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert at end of array"); - ++cValues; - CheckNullItem(rgValues + 1); - SetItem(rgValues + 1, 6); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); - ++cValues; - CheckNullItem(rgValues + 0); - SetItem(rgValues + 0, 4); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); - ++cValues; - CheckNullItem(rgValues + 0); - SetItem(rgValues + 0, 3); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); - ++cValues; - CheckNullItem(rgValues + 0); - SetItem(rgValues + 0, 1); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); - ++cValues; - CheckNullItem(rgValues + 1); - SetItem(rgValues + 1, 2); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); - ++cValues; - CheckNullItem(rgValues + 0); - SetItem(rgValues + 0, 0); - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to grow array size to 7"); - ++cValues; - CheckNullItem(rgValues + 7); - SetItem(rgValues + 7, 7); - - hr = MemInsertIntoArray(reinterpret_cast(&rgValues), 8, 1, cValues + 1, sizeof(ArrayValue), 5); - NativeAssert::Succeeded(hr, "Failed to insert into beginning of array"); - ++cValues; - CheckNullItem(rgValues + 8); - SetItem(rgValues + 8, 8); - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - } - finally - { - ReleaseMem(rgValues); - DutilUninitialize(); - } - } - - [Fact] - void MemUtilRemovePreserveOrderTest() - { - HRESULT hr = S_OK; - ArrayValue *rgValues = NULL; - DWORD cValues = 0; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); - NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); - - cValues = 10; - for (DWORD i = 0; i < cValues; ++i) - { - SetItem(rgValues + i, i); - } - - // Remove last item - MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), TRUE); - --cValues; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - // Remove last two items - MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), TRUE); - cValues -= 2; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - // Remove first item - MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), TRUE); - --cValues; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i + 1); - } - - - // Remove first two items - MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE); - cValues -= 2; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i + 3); - } - - // Remove middle two items - MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), TRUE); - cValues -= 2; - - CheckItem(rgValues, 3); - CheckItem(rgValues + 1, 6); - - // Remove last 2 items to ensure we don't crash - MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE); - cValues -= 2; - } - finally - { - ReleaseMem(rgValues); - DutilUninitialize(); - } - } - - [Fact] - void MemUtilRemoveFastTest() - { - HRESULT hr = S_OK; - ArrayValue *rgValues = NULL; - DWORD cValues = 0; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); - NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); - - cValues = 10; - for (DWORD i = 0; i < cValues; ++i) - { - SetItem(rgValues + i, i); - } - - // Remove last item - MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), FALSE); - --cValues; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - // Remove last two items - MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), FALSE); - cValues -= 2; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - // Remove first item - MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), FALSE); - --cValues; - - CheckItem(rgValues, 6); - CheckItem(rgValues + 1, 1); - CheckItem(rgValues + 2, 2); - CheckItem(rgValues + 3, 3); - CheckItem(rgValues + 4, 4); - CheckItem(rgValues + 5, 5); - - // Remove first two items - MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE); - cValues -= 2; - - CheckItem(rgValues, 4); - CheckItem(rgValues + 1, 5); - CheckItem(rgValues + 2, 2); - CheckItem(rgValues + 3, 3); - - - // Remove middle two items - MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), FALSE); - cValues -= 2; - - CheckItem(rgValues, 4); - CheckItem(rgValues + 1, 3); - - // Remove last 2 items to ensure we don't crash - MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE); - cValues -= 2; - } - finally - { - ReleaseMem(rgValues); - DutilUninitialize(); - } - } - - [Fact] - void MemUtilSwapTest() - { - HRESULT hr = S_OK; - ArrayValue *rgValues = NULL; - DWORD cValues = 0; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = MemEnsureArraySize(reinterpret_cast(&rgValues), 10, sizeof(ArrayValue), 10); - NativeAssert::Succeeded(hr, "Failed to grow array size to 10"); - - cValues = 10; - for (DWORD i = 0; i < cValues; ++i) - { - SetItem(rgValues + i, i); - } - - // Swap first two - MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); - --cValues; - - CheckItem(rgValues, 1); - CheckItem(rgValues + 1, 0); - for (DWORD i = 2; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - // Swap them back - MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); - --cValues; - - for (DWORD i = 0; i < cValues; ++i) - { - CheckItem(rgValues + i, i); - } - - // Swap first and last items (index 0 and 9) - MemArraySwapItems(rgValues, 0, 9, sizeof(ArrayValue)); - --cValues; - - CheckItem(rgValues, 9); - CheckItem(rgValues + 9, 0); - for (DWORD i = 1; i < cValues - 1; ++i) - { - CheckItem(rgValues + i, i); - } - - // Swap index 1 and 8 - MemArraySwapItems(rgValues, 1, 8, sizeof(ArrayValue)); - --cValues; - - CheckItem(rgValues, 9); - CheckItem(rgValues + 1, 8); - CheckItem(rgValues + 8, 1); - CheckItem(rgValues + 9, 0); - for (DWORD i = 2; i < cValues - 2; ++i) - { - CheckItem(rgValues + i, i); - } - - // Swap index 2 and 7 - MemArraySwapItems(rgValues, 2, 7, sizeof(ArrayValue)); - --cValues; - - CheckItem(rgValues, 9); - CheckItem(rgValues + 1, 8); - CheckItem(rgValues + 2, 7); - CheckItem(rgValues + 7, 2); - CheckItem(rgValues + 8, 1); - CheckItem(rgValues + 9, 0); - for (DWORD i = 3; i < cValues - 3; ++i) - { - CheckItem(rgValues + i, i); - } - - // Swap index 0 and 1 - MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue)); - --cValues; - - CheckItem(rgValues, 8); - CheckItem(rgValues + 1, 9); - CheckItem(rgValues + 2, 7); - CheckItem(rgValues + 7, 2); - CheckItem(rgValues + 8, 1); - CheckItem(rgValues + 9, 0); - for (DWORD i = 3; i < cValues - 3; ++i) - { - CheckItem(rgValues + i, i); - } - } - finally - { - ReleaseMem(rgValues); - DutilUninitialize(); - } - } - - private: - void SetItem(ArrayValue *pValue, DWORD dwValue) - { - HRESULT hr = S_OK; - pValue->dwNum = dwValue; - - hr = StrAllocFormatted(&pValue->sczString, L"%u", dwValue); - NativeAssert::Succeeded(hr, "Failed to allocate string"); - } - - void CheckItem(ArrayValue *pValue, DWORD dwValue) - { - HRESULT hr = S_OK; - LPWSTR sczTemp = NULL; - - try - { - NativeAssert::Equal(dwValue, pValue->dwNum); - - hr = StrAllocFormatted(&sczTemp, L"%u", dwValue); - NativeAssert::Succeeded(hr, "Failed to allocate temp string"); - - NativeAssert::StringEqual(sczTemp, pValue->sczString, TRUE); - - if (pValue->pvNull1 || pValue->pvNull2) - { - hr = E_FAIL; - ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!"); - } - } - finally - { - ReleaseStr(sczTemp); - } - - LExit: - return; - } - - void CheckNullItem(ArrayValue *pValue) - { - HRESULT hr = S_OK; - - NativeAssert::Equal(0, pValue->dwNum); - - if (pValue->sczString) - { - hr = E_FAIL; - ExitOnFailure(hr, "Item found isn't NULL!"); - } - - if (pValue->pvNull1 || pValue->pvNull2) - { - hr = E_FAIL; - ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!"); - } - - LExit: - return; - } - }; -} diff --git a/src/test/DUtilUnitTest/MonUtilTest.cpp b/src/test/DUtilUnitTest/MonUtilTest.cpp deleted file mode 100644 index 273f2eb6..00000000 --- a/src/test/DUtilUnitTest/MonUtilTest.cpp +++ /dev/null @@ -1,487 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" -#undef RemoveDirectory - -using namespace System; -using namespace System::Collections::Generic; -using namespace System::Runtime::InteropServices; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - const int PREWAIT = 20; - const int POSTWAIT = 480; - const int FULLWAIT = 500; - const int SILENCEPERIOD = 100; - - struct RegKey - { - HRESULT hr; - HKEY hkRoot; - LPCWSTR wzSubKey; - REG_KEY_BITNESS kbKeyBitness; - BOOL fRecursive; - }; - struct Directory - { - HRESULT hr; - LPCWSTR wzPath; - BOOL fRecursive; - }; - struct Results - { - RegKey *rgRegKeys; - DWORD cRegKeys; - Directory *rgDirectories; - DWORD cDirectories; - }; - - public delegate void MonGeneralDelegate(HRESULT, LPVOID); - - public delegate void MonDriveStatusDelegate(WCHAR, BOOL, LPVOID); - - public delegate void MonDirectoryDelegate(HRESULT, LPCWSTR, BOOL, LPVOID, LPVOID); - - public delegate void MonRegKeyDelegate(HRESULT, HKEY, LPCWSTR, REG_KEY_BITNESS, BOOL, LPVOID, LPVOID); - - static void MonGeneral( - __in HRESULT /*hrResult*/, - __in_opt LPVOID /*pvContext*/ - ) - { - Assert::True(false); - } - - static void MonDriveStatus( - __in WCHAR /*chDrive*/, - __in BOOL /*fArriving*/, - __in_opt LPVOID /*pvContext*/ - ) - { - } - - static void MonDirectory( - __in HRESULT hrResult, - __in_z LPCWSTR wzPath, - __in_z BOOL fRecursive, - __in_opt LPVOID pvContext, - __in_opt LPVOID pvDirectoryContext - ) - { - Assert::Equal(S_OK, hrResult); - Assert::Equal(0, reinterpret_cast(pvDirectoryContext)); - - HRESULT hr = S_OK; - Results *pResults = reinterpret_cast(pvContext); - - hr = MemEnsureArraySize(reinterpret_cast(&pResults->rgDirectories), pResults->cDirectories + 1, sizeof(Directory), 5); - NativeAssert::ValidReturnCode(hr, S_OK); - ++pResults->cDirectories; - - pResults->rgDirectories[pResults->cDirectories - 1].hr = hrResult; - pResults->rgDirectories[pResults->cDirectories - 1].wzPath = wzPath; - pResults->rgDirectories[pResults->cDirectories - 1].fRecursive = fRecursive; - } - - static void MonRegKey( - __in HRESULT hrResult, - __in HKEY hkRoot, - __in_z LPCWSTR wzSubKey, - __in REG_KEY_BITNESS kbKeyBitness, - __in_z BOOL fRecursive, - __in_opt LPVOID pvContext, - __in_opt LPVOID pvRegKeyContext - ) - { - Assert::Equal(S_OK, hrResult); - Assert::Equal(0, reinterpret_cast(pvRegKeyContext)); - - HRESULT hr = S_OK; - Results *pResults = reinterpret_cast(pvContext); - - hr = MemEnsureArraySize(reinterpret_cast(&pResults->rgRegKeys), pResults->cRegKeys + 1, sizeof(RegKey), 5); - NativeAssert::ValidReturnCode(hr, S_OK); - ++pResults->cRegKeys; - - pResults->rgRegKeys[pResults->cRegKeys - 1].hr = hrResult; - pResults->rgRegKeys[pResults->cRegKeys - 1].hkRoot = hkRoot; - pResults->rgRegKeys[pResults->cRegKeys - 1].wzSubKey = wzSubKey; - pResults->rgRegKeys[pResults->cRegKeys - 1].kbKeyBitness = kbKeyBitness; - pResults->rgRegKeys[pResults->cRegKeys - 1].fRecursive = fRecursive; - } - - public ref class MonUtil - { - public: - void ClearResults(Results *pResults) - { - ReleaseNullMem(pResults->rgDirectories); - pResults->cDirectories = 0; - ReleaseNullMem(pResults->rgRegKeys); - pResults->cRegKeys = 0; - } - - void RemoveDirectory(LPCWSTR wzPath) - { - DWORD dwRetryCount = 0; - const DWORD c_dwMaxRetryCount = 100; - const DWORD c_dwRetryInterval = 50; - - HRESULT hr = DirEnsureDelete(wzPath, TRUE, TRUE); - - // Monitoring a directory opens a handle to that directory, which means delete requests for that directory will succeed - // (and deletion will be "pending" until our monitor handle is closed) - // but deletion of the directory containing that directory cannot complete until the handle is closed. This means DirEnsureDelete() - // can sometimes encounter HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) failures, which just means it needs to retry a bit later - // (after the waiter thread wakes up, it will release the handle) - while (HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) == hr && c_dwMaxRetryCount > dwRetryCount) - { - ::Sleep(c_dwRetryInterval); - ++dwRetryCount; - hr = DirEnsureDelete(wzPath, TRUE, TRUE); - } - - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); - } - - void TestDirectory(MON_HANDLE handle, Results *pResults) - { - HRESULT hr = S_OK; - LPWSTR sczShallowPath = NULL; - LPWSTR sczParentPath = NULL; - LPWSTR sczDeepPath = NULL; - LPWSTR sczChildPath = NULL; - LPWSTR sczChildFilePath = NULL; - - try - { - hr = PathExpand(&sczShallowPath, L"%TEMP%\\MonUtilTest\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = PathExpand(&sczParentPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = PathExpand(&sczDeepPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = PathExpand(&sczChildPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = PathExpand(&sczChildFilePath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\file.txt", PATH_EXPAND_ENVIRONMENT); - NativeAssert::ValidReturnCode(hr, S_OK); - - RemoveDirectory(sczShallowPath); - - hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = DirEnsureExists(sczParentPath, NULL); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - // Make sure creating the parent directory does nothing, even after silence period - ::Sleep(FULLWAIT); - Assert::Equal(0, pResults->cDirectories); - - // Now create the target path, no notification until after the silence period - hr = DirEnsureExists(sczDeepPath, NULL); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ::Sleep(PREWAIT); - Assert::Equal(0, pResults->cDirectories); - - // Now after the full silence period, it should have triggered - ::Sleep(POSTWAIT); - Assert::Equal(1, pResults->cDirectories); - NativeAssert::ValidReturnCode(pResults->rgDirectories[0].hr, S_OK); - - // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists. - RemoveDirectory(sczShallowPath); - - ::Sleep(FULLWAIT); - Assert::Equal(2, pResults->cDirectories); - NativeAssert::ValidReturnCode(pResults->rgDirectories[1].hr, S_OK); - - // Create the parent directory again, still should be nothing even after full silence period - hr = DirEnsureExists(sczParentPath, NULL); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ::Sleep(FULLWAIT); - Assert::Equal(2, pResults->cDirectories); - - hr = DirEnsureExists(sczChildPath, NULL); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ::Sleep(PREWAIT); - Assert::Equal(2, pResults->cDirectories); - - ::Sleep(POSTWAIT); - Assert::Equal(3, pResults->cDirectories); - NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK); - - // Write a file to a deep child subfolder, and make sure it's detected - hr = FileFromString(sczChildFilePath, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM); - NativeAssert::ValidReturnCode(hr, S_OK); - ::Sleep(PREWAIT); - Assert::Equal(3, pResults->cDirectories); - - ::Sleep(POSTWAIT); - Assert::Equal(4, pResults->cDirectories); - NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK); - - RemoveDirectory(sczParentPath); - - ::Sleep(FULLWAIT); - Assert::Equal(5, pResults->cDirectories); - NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK); - - // Now remove the directory from the list of things to monitor, and confirm changes are no longer tracked - hr = MonRemoveDirectory(handle, sczDeepPath, TRUE); - NativeAssert::ValidReturnCode(hr, S_OK); - ::Sleep(PREWAIT); - - hr = DirEnsureExists(sczDeepPath, NULL); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ::Sleep(FULLWAIT); - Assert::Equal(5, pResults->cDirectories); - NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK); - - // Finally, add it back so we can test multiple things to monitor at once - hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL); - NativeAssert::ValidReturnCode(hr, S_OK); - } - finally - { - ReleaseStr(sczShallowPath); - ReleaseStr(sczDeepPath); - ReleaseStr(sczParentPath); - } - } - - void TestRegKey(MON_HANDLE handle, Results *pResults) - { - HRESULT hr = S_OK; - LPCWSTR wzShallowRegKey = L"Software\\MonUtilTest\\"; - LPCWSTR wzParentRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\"; - LPCWSTR wzDeepRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\"; - LPCWSTR wzChildRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\"; - HKEY hk = NULL; - - try - { - hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); - - hr = MonAddRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE, SILENCEPERIOD, NULL); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); - ReleaseRegKey(hk); - // Make sure creating the parent key does nothing, even after silence period - ::Sleep(FULLWAIT); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - Assert::Equal(0, pResults->cRegKeys); - - // Now create the target path, no notification until after the silence period - hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ReleaseRegKey(hk); - ::Sleep(PREWAIT); - Assert::Equal(0, pResults->cRegKeys); - - // Now after the full silence period, it should have triggered - ::Sleep(POSTWAIT); - Assert::Equal(1, pResults->cRegKeys); - NativeAssert::ValidReturnCode(pResults->rgRegKeys[0].hr, S_OK); - - // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists. - hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); - ::Sleep(PREWAIT); - Assert::Equal(1, pResults->cRegKeys); - - ::Sleep(FULLWAIT); - Assert::Equal(2, pResults->cRegKeys); - NativeAssert::ValidReturnCode(pResults->rgRegKeys[1].hr, S_OK); - - // Create the parent directory again, still should be nothing even after full silence period - hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ReleaseRegKey(hk); - ::Sleep(FULLWAIT); - Assert::Equal(2, pResults->cRegKeys); - - hr = RegCreate(HKEY_CURRENT_USER, wzChildRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ::Sleep(PREWAIT); - Assert::Equal(2, pResults->cRegKeys); - - ::Sleep(FULLWAIT); - Assert::Equal(3, pResults->cRegKeys); - NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK); - - // Write a registry value to some deep child subkey, and make sure it's detected - hr = RegWriteString(hk, L"valuename", L"testvalue"); - NativeAssert::ValidReturnCode(hr, S_OK); - ReleaseRegKey(hk); - ::Sleep(PREWAIT); - Assert::Equal(3, pResults->cRegKeys); - - ::Sleep(FULLWAIT); - Assert::Equal(4, pResults->cRegKeys); - NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK); - - hr = RegDelete(HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_32BIT, TRUE); - NativeAssert::ValidReturnCode(hr, S_OK); - - ::Sleep(FULLWAIT); - Assert::Equal(5, pResults->cRegKeys); - - // Now remove the regkey from the list of things to monitor, and confirm changes are no longer tracked - hr = MonRemoveRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - ReleaseRegKey(hk); - ::Sleep(FULLWAIT); - Assert::Equal(5, pResults->cRegKeys); - } - finally - { - ReleaseRegKey(hk); - } - } - - void TestMoreThan64(MON_HANDLE handle, Results *pResults) - { - HRESULT hr = S_OK; - LPWSTR sczBaseDir = NULL; - LPWSTR sczDir = NULL; - LPWSTR sczFile = NULL; - - try - { - hr = PathExpand(&sczBaseDir, L"%TEMP%\\ScalabilityTest\\", PATH_EXPAND_ENVIRONMENT); - NativeAssert::ValidReturnCode(hr, S_OK); - - for (DWORD i = 0; i < 200; ++i) - { - hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = DirEnsureExists(sczDir, NULL); - NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); - - hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL); - NativeAssert::ValidReturnCode(hr, S_OK); - } - - hr = PathConcat(sczDir, L"file.txt", &sczFile); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = FileFromString(sczFile, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM); - NativeAssert::ValidReturnCode(hr, S_OK); - - ::Sleep(FULLWAIT); - Assert::Equal(1, pResults->cDirectories); - - for (DWORD i = 0; i < 199; ++i) - { - hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = MonRemoveDirectory(handle, sczDir, FALSE); - NativeAssert::ValidReturnCode(hr, S_OK); - } - ::Sleep(FULLWAIT); - - hr = FileFromString(sczFile, 0, L"contents2", FILE_ENCODING_UTF16_WITH_BOM); - NativeAssert::ValidReturnCode(hr, S_OK); - - ::Sleep(FULLWAIT); - Assert::Equal(2, pResults->cDirectories); - - for (DWORD i = 0; i < 199; ++i) - { - hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL); - NativeAssert::ValidReturnCode(hr, S_OK); - } - ::Sleep(FULLWAIT); - - hr = FileFromString(sczFile, 0, L"contents3", FILE_ENCODING_UTF16_WITH_BOM); - NativeAssert::ValidReturnCode(hr, S_OK); - - ::Sleep(FULLWAIT); - Assert::Equal(3, pResults->cDirectories); - } - finally - { - ReleaseStr(sczBaseDir); - ReleaseStr(sczDir); - ReleaseStr(sczFile); - } - } - - [Fact(Skip = "Test demonstrates failure")] - void MonUtilTest() - { - HRESULT hr = S_OK; - MON_HANDLE handle = NULL; - List^ gcHandles = gcnew List(); - Results *pResults = (Results *)MemAlloc(sizeof(Results), TRUE); - Assert::True(NULL != pResults); - - try - { - // These ensure the function pointers we send point to this thread's appdomain, which helps with assembly binding when running tests within msbuild - MonGeneralDelegate^ fpMonGeneral = gcnew MonGeneralDelegate(MonGeneral); - GCHandle gchMonGeneral = GCHandle::Alloc(fpMonGeneral); - gcHandles->Add(gchMonGeneral); - IntPtr ipMonGeneral = Marshal::GetFunctionPointerForDelegate(fpMonGeneral); - - MonDriveStatusDelegate^ fpMonDriveStatus = gcnew MonDriveStatusDelegate(MonDriveStatus); - GCHandle gchMonDriveStatus = GCHandle::Alloc(fpMonDriveStatus); - gcHandles->Add(gchMonDriveStatus); - IntPtr ipMonDriveStatus = Marshal::GetFunctionPointerForDelegate(fpMonDriveStatus); - - MonDirectoryDelegate^ fpMonDirectory = gcnew MonDirectoryDelegate(MonDirectory); - GCHandle gchMonDirectory = GCHandle::Alloc(fpMonDirectory); - gcHandles->Add(gchMonDirectory); - IntPtr ipMonDirectory = Marshal::GetFunctionPointerForDelegate(fpMonDirectory); - - MonRegKeyDelegate^ fpMonRegKey = gcnew MonRegKeyDelegate(MonRegKey); - GCHandle gchMonRegKey = GCHandle::Alloc(fpMonRegKey); - gcHandles->Add(gchMonRegKey); - IntPtr ipMonRegKey = Marshal::GetFunctionPointerForDelegate(fpMonRegKey); - - // "Silence period" is 100 ms - hr = MonCreate(&handle, static_cast(ipMonGeneral.ToPointer()), static_cast(ipMonDriveStatus.ToPointer()), static_cast(ipMonDirectory.ToPointer()), static_cast(ipMonRegKey.ToPointer()), pResults); - NativeAssert::ValidReturnCode(hr, S_OK); - - hr = RegInitialize(); - NativeAssert::ValidReturnCode(hr, S_OK); - - TestDirectory(handle, pResults); - ClearResults(pResults); - TestRegKey(handle, pResults); - ClearResults(pResults); - TestMoreThan64(handle, pResults); - ClearResults(pResults); - } - finally - { - ReleaseMon(handle); - - for each (GCHandle gcHandle in gcHandles) - { - gcHandle.Free(); - } - - ReleaseMem(pResults->rgDirectories); - ReleaseMem(pResults->rgRegKeys); - ReleaseMem(pResults); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/PathUtilTest.cpp b/src/test/DUtilUnitTest/PathUtilTest.cpp deleted file mode 100644 index 5a1f06fd..00000000 --- a/src/test/DUtilUnitTest/PathUtilTest.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class PathUtil - { - public: - [Fact] - void PathGetHierarchyArrayTest() - { - HRESULT hr = S_OK; - LPWSTR *rgsczPaths = NULL; - UINT cPaths = 0; - - try - { - hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\a.txt", &rgsczPaths, &cPaths); - NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular file path"); - Assert::Equal(5, cPaths); - NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]); - NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]); - NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]); - NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]); - NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\a.txt", rgsczPaths[4]); - ReleaseNullStrArray(rgsczPaths, cPaths); - - hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\", &rgsczPaths, &cPaths); - NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular directory path"); - Assert::Equal(4, cPaths); - NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]); - NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]); - NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]); - NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]); - ReleaseNullStrArray(rgsczPaths, cPaths); - - hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\file.txt", &rgsczPaths, &cPaths); - NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC file path"); - Assert::Equal(3, cPaths); - NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]); - NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]); - NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\file.txt", rgsczPaths[2]); - ReleaseNullStrArray(rgsczPaths, cPaths); - - hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\", &rgsczPaths, &cPaths); - NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); - Assert::Equal(2, cPaths); - NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]); - NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]); - ReleaseNullStrArray(rgsczPaths, cPaths); - - hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\ValueName", &rgsczPaths, &cPaths); - NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); - Assert::Equal(4, cPaths); - NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]); - NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]); - NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]); - NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\ValueName", rgsczPaths[3]); - ReleaseNullStrArray(rgsczPaths, cPaths); - - hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\", &rgsczPaths, &cPaths); - NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path"); - Assert::Equal(3, cPaths); - NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]); - NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]); - NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]); - ReleaseNullStrArray(rgsczPaths, cPaths); - } - finally - { - ReleaseStrArray(rgsczPaths, cPaths); - } - } - }; -} diff --git a/src/test/DUtilUnitTest/SceUtilTest.cpp b/src/test/DUtilUnitTest/SceUtilTest.cpp deleted file mode 100644 index 75b9222a..00000000 --- a/src/test/DUtilUnitTest/SceUtilTest.cpp +++ /dev/null @@ -1,488 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -#include -#include - -using namespace System; -using namespace Xunit; -using namespace WixTest; - -#define ASSIGN_INDEX_STRUCT(a, b, c) {a.wzName = c; a.rgColumns = b; a.cColumns = countof(b);}; - -namespace DutilTests -{ - enum TABLES - { - TABLE_A, - TABLE_COUNT - }; - - enum TABLE_A_COLUMNS - { - TABLE_A_KEY, - TABLE_A_BINARY, - TABLE_A_DWORD, - TABLE_A_QWORD, - TABLE_A_BOOL, - TABLE_A_STRING, - TABLE_A_DWORD_NULLABLE, - TABLE_A_INITIAL_COLUMNS, - - TABLE_A_EXTRA_STRING = TABLE_A_INITIAL_COLUMNS, - TABLE_A_FINAL_COLUMNS - }; - - struct TableARowValue - { - DWORD dwAutoGenKey; - - BYTE *pbBinary; - DWORD cBinary; - - DWORD dw; - DWORD64 qw; - BOOL f; - LPWSTR scz; - - BOOL fNullablePresent; - DWORD dwNullable; - - BOOL fSchemaV2; - LPWSTR sczExtra; - }; - - public ref class SceUtil - { - public: - void ReleaseSceSchema(SCE_DATABASE_SCHEMA *pdsSchema) - { - DWORD dwTable; - - for (dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) - { - ReleaseNullMem(pdsSchema->rgTables[dwTable].rgColumns); - ReleaseNullMem(pdsSchema->rgTables[dwTable].rgIndexes); - } - - ReleaseMem(pdsSchema->rgTables); - - return; - } - - void SetupSchema(SCE_DATABASE_SCHEMA *pSchema, BOOL fIncludeExtended) - { - pSchema->cTables = TABLE_COUNT; - pSchema->rgTables = static_cast(MemAlloc(TABLE_COUNT * sizeof(SCE_TABLE_SCHEMA), TRUE)); - NativeAssert::True(pSchema->rgTables != NULL); - - pSchema->rgTables[TABLE_A].wzName = L"TableA"; - pSchema->rgTables[TABLE_A].cColumns = fIncludeExtended ? TABLE_A_FINAL_COLUMNS : TABLE_A_INITIAL_COLUMNS; - pSchema->rgTables[TABLE_A].cIndexes = 2; - - for (DWORD i = 0; i < pSchema->cTables; ++i) - { - pSchema->rgTables[i].rgColumns = static_cast(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cColumns, TRUE)); - NativeAssert::True(pSchema->rgTables[i].rgColumns != NULL); - - pSchema->rgTables[i].rgIndexes = static_cast(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cIndexes, TRUE)); - NativeAssert::True(pSchema->rgTables[i].rgIndexes != NULL); - } - - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].wzName = L"Key"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].dbtColumnType = DBTYPE_I4; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fPrimaryKey = TRUE; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fAutoIncrement = TRUE; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].wzName = L"Binary"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].dbtColumnType = DBTYPE_BYTES; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].wzName = L"Dword"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].dbtColumnType = DBTYPE_I4; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].wzName = L"Qword"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].dbtColumnType = DBTYPE_I8; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].wzName = L"Bool"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].dbtColumnType = DBTYPE_BOOL; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].wzName = L"String"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].dbtColumnType = DBTYPE_WSTR; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].wzName = L"Nullable"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].dbtColumnType = DBTYPE_I4; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].fNullable = TRUE; - - if (fIncludeExtended) - { - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].wzName = L"ExtraString"; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].dbtColumnType = DBTYPE_WSTR; - pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].fNullable = TRUE; - } - - static DWORD rgdwTableA_Index1[] = { TABLE_A_DWORD, TABLE_A_STRING, TABLE_A_QWORD }; - static DWORD rgdwTableA_Index2[] = { TABLE_A_DWORD, TABLE_A_STRING }; - - ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[0], rgdwTableA_Index1, L"Dword_String_Qword"); - ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[1], rgdwTableA_Index2, L"Dword_String"); - } - - void SetStructValues(TableARowValue *pValue, BYTE *pbBinary, DWORD cBinary, DWORD dw, DWORD64 qw, BOOL f, LPWSTR scz, DWORD *pdw, LPWSTR sczExtra) - { - pValue->pbBinary = pbBinary; - pValue->cBinary = cBinary; - pValue->dw = dw; - pValue->qw = qw; - pValue->f = f; - pValue->scz = scz; - - if (pdw) - { - pValue->fNullablePresent = TRUE; - pValue->dwNullable = *pdw; - } - else - { - pValue->fNullablePresent = FALSE; - } - - if (sczExtra) - { - pValue->fSchemaV2 = TRUE; - pValue->sczExtra = sczExtra; - } - else - { - pValue->fSchemaV2 = FALSE; - } - } - - void AssertStructValuesSame(TableARowValue *pValueExpected, TableARowValue *pValueOther) - { - NativeAssert::Equal(pValueExpected->cBinary, pValueOther->cBinary); - NativeAssert::True(0 == memcmp(pValueExpected->pbBinary, pValueOther->pbBinary, pValueOther->cBinary)); - - NativeAssert::Equal(pValueExpected->dw, pValueOther->dw); - NativeAssert::Equal(pValueExpected->qw, pValueOther->qw); - NativeAssert::Equal(pValueExpected->f, pValueOther->f); - NativeAssert::True(0 == wcscmp(pValueExpected->scz, pValueOther->scz)); - - NativeAssert::Equal(pValueExpected->fNullablePresent, pValueOther->fNullablePresent); - if (pValueExpected->fNullablePresent) - { - NativeAssert::Equal(pValueExpected->dwNullable, pValueOther->dwNullable); - } - - NativeAssert::Equal(pValueExpected->fSchemaV2, pValueOther->fSchemaV2); - if (pValueExpected->fSchemaV2) - { - NativeAssert::True(0 == wcscmp(pValueExpected->sczExtra, pValueOther->sczExtra)); - } - } - - void InsertRow(SCE_DATABASE *pDatabase, TableARowValue *pValue, BOOL fRollback) - { - HRESULT hr = S_OK; - SCE_ROW_HANDLE sceRow = NULL; - - hr = SceBeginTransaction(pDatabase); - NativeAssert::Succeeded(hr, "Failed to begin transaction"); - - hr = ScePrepareInsert(pDatabase, TABLE_A, &sceRow); - NativeAssert::Succeeded(hr, "Failed to prepare to insert row"); - - hr = SceSetColumnBinary(sceRow, TABLE_A_BINARY, pValue->pbBinary, pValue->cBinary); - NativeAssert::Succeeded(hr, "Failed to set binary value"); - - hr = SceSetColumnDword(sceRow, TABLE_A_DWORD, pValue->dw); - NativeAssert::Succeeded(hr, "Failed to set dword value"); - - hr = SceSetColumnQword(sceRow, TABLE_A_QWORD, pValue->qw); - NativeAssert::Succeeded(hr, "Failed to set qword value"); - - hr = SceSetColumnBool(sceRow, TABLE_A_BOOL, pValue->f); - NativeAssert::Succeeded(hr, "Failed to set bool value"); - - hr = SceSetColumnString(sceRow, TABLE_A_STRING, pValue->scz); - NativeAssert::Succeeded(hr, "Failed to set string value"); - - if (pValue->fNullablePresent) - { - hr = SceSetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, pValue->dwNullable); - NativeAssert::Succeeded(hr, "Failed to set dword value"); - } - else - { - hr = SceSetColumnNull(sceRow, TABLE_A_DWORD_NULLABLE); - NativeAssert::Succeeded(hr, "Failed to set null value"); - } - - if (pValue->fSchemaV2) - { - hr = SceSetColumnString(sceRow, TABLE_A_EXTRA_STRING, pValue->sczExtra); - NativeAssert::Succeeded(hr, "Failed to set extra string value"); - } - - hr = SceFinishUpdate(sceRow); - NativeAssert::Succeeded(hr, "Failed to finish insert"); - - if (fRollback) - { - hr = SceRollbackTransaction(pDatabase); - NativeAssert::Succeeded(hr, "Failed to rollback transaction"); - } - else - { - hr = SceCommitTransaction(pDatabase); - NativeAssert::Succeeded(hr, "Failed to commit transaction"); - - hr = SceGetColumnDword(sceRow, TABLE_A_KEY, &pValue->dwAutoGenKey); - NativeAssert::Succeeded(hr, "Failed to get autogen key after insert"); - - NativeAssert::True(pValue->dwAutoGenKey != 0); - } - - ReleaseSceRow(sceRow); - } - - void VerifyRow(TableARowValue *pExpectedValue, SCE_ROW_HANDLE sceRow) - { - HRESULT hr = S_OK; - TableARowValue value = {}; - - hr = SceGetColumnBinary(sceRow, TABLE_A_BINARY, &value.pbBinary, &value.cBinary); - NativeAssert::Succeeded(hr, "Failed to get binary value from result row"); - - hr = SceGetColumnDword(sceRow, TABLE_A_DWORD, &value.dw); - NativeAssert::Succeeded(hr, "Failed to get dword value from result row"); - - hr = SceGetColumnQword(sceRow, TABLE_A_QWORD, &value.qw); - NativeAssert::Succeeded(hr, "Failed to get qword value from result row"); - - hr = SceGetColumnBool(sceRow, TABLE_A_BOOL, &value.f); - NativeAssert::Succeeded(hr, "Failed to get bool value from result row"); - - hr = SceGetColumnString(sceRow, TABLE_A_STRING, &value.scz); - NativeAssert::Succeeded(hr, "Failed to get string value from result row"); - - hr = SceGetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, &value.dwNullable); - if (hr == E_NOTFOUND) - { - value.fNullablePresent = FALSE; - hr = S_OK; - } - else - { - NativeAssert::Succeeded(hr, "Failed to get string value from result row"); - value.fNullablePresent = TRUE; - } - - if (pExpectedValue->fSchemaV2) - { - value.fSchemaV2 = TRUE; - hr = SceGetColumnString(sceRow, TABLE_A_EXTRA_STRING, &value.sczExtra); - NativeAssert::Succeeded(hr, "Failed to get extra string value from result row"); - } - - AssertStructValuesSame(pExpectedValue, &value); - - ReleaseNullMem(value.pbBinary); - ReleaseNullStr(value.scz); - } - - void VerifyQuery(TableARowValue **rgExpectedValues, DWORD cExpectedValues, SCE_QUERY_RESULTS_HANDLE queryResults) - { - HRESULT hr = S_OK; - SCE_ROW_HANDLE sceRow = NULL; - - for (DWORD i = 0; i < cExpectedValues; ++i) - { - hr = SceGetNextResultRow(queryResults, &sceRow); - NativeAssert::Succeeded(hr, "Failed to get next result row"); - - VerifyRow(rgExpectedValues[i], sceRow); - ReleaseNullSceRow(sceRow); - } - - // No more results - NativeAssert::True(NULL == queryResults || FAILED(SceGetNextResultRow(queryResults, &sceRow))); - } - - void TestIndex(SCE_DATABASE *pDatabase) - { - HRESULT hr = S_OK; - BYTE binary1[50] = { 0x80, 0x70 }; - BYTE binary2[40] = { 0x90, 0xAB }; - BYTE binary3[40] = { 0x85, 0x88 }; - DWORD dwValue1 = 0x55555555, dwValue2 = 0x88888888; - TableARowValue value1 = {}, value2 = {}, value3 = {}, value4 = {}, value5 = {}; - SCE_QUERY_HANDLE query = NULL; - SCE_QUERY_RESULTS_HANDLE results = NULL; - - SetStructValues(&value1, static_cast(binary1), sizeof(binary1), 3, 1, TRUE, L"zzz", &dwValue1, NULL); - SetStructValues(&value2, static_cast(binary2), sizeof(binary2), 3, 2, TRUE, L"yyy", &dwValue2, NULL); - SetStructValues(&value3, static_cast(binary3), sizeof(binary3), 3, 3, TRUE, L"xxx", NULL, NULL); - SetStructValues(&value4, static_cast(binary2), sizeof(binary2), 4, 4, TRUE, L"xyz", &dwValue2, NULL); - SetStructValues(&value5, static_cast(binary3), sizeof(binary3), 3, 1, TRUE, L"yyy", &dwValue2, NULL); - - // Rollback an insert to confirm the insert doesn't happen and database can still be interacted with normally afterwards - InsertRow(pDatabase, &value1, TRUE); - - InsertRow(pDatabase, &value1, FALSE); - InsertRow(pDatabase, &value2, FALSE); - InsertRow(pDatabase, &value3, FALSE); - InsertRow(pDatabase, &value4, FALSE); - InsertRow(pDatabase, &value5, FALSE); - - NativeAssert::True(value1.dwAutoGenKey != value2.dwAutoGenKey); - - // Test setting 1 column - hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); - NativeAssert::Succeeded(hr, "Failed to begin query"); - - hr = SceSetQueryColumnDword(query, 3); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceRunQueryRange(&query, &results); - NativeAssert::Succeeded(hr, "Failed to run query"); - NativeAssert::True(query == NULL); - - TableARowValue *sortedAfterQuery1[] = { &value3, &value5, &value2, &value1 }; - VerifyQuery(sortedAfterQuery1, _countof(sortedAfterQuery1), results); - ReleaseNullSceQueryResults(results); - - // Test setting 2 columns, third column is unspecified so results are sorted by it - hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); - NativeAssert::Succeeded(hr, "Failed to begin query"); - - hr = SceSetQueryColumnDword(query, 3); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceSetQueryColumnString(query, L"yyy"); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceRunQueryRange(&query, &results); - NativeAssert::Succeeded(hr, "Failed to run query"); - NativeAssert::True(query == NULL); - - TableARowValue *sortedAfterQuery2[] = { &value5, &value2 }; - VerifyQuery(sortedAfterQuery2, _countof(sortedAfterQuery2), results); - ReleaseNullSceQueryResults(results); - - // Test setting 2 columns, third column of index is unspecified so results are sorted by it - hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); - NativeAssert::Succeeded(hr, "Failed to begin query"); - - hr = SceSetQueryColumnDword(query, 3); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceSetQueryColumnString(query, L"yyy"); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceRunQueryRange(&query, &results); - NativeAssert::Succeeded(hr, "Failed to run query"); - NativeAssert::True(query == NULL); - - TableARowValue *sortedAfterQuery3[] = { &value5, &value2 }; - VerifyQuery(sortedAfterQuery3, _countof(sortedAfterQuery3), results); - ReleaseNullSceQueryResults(results); - - // Test setting 2 columns in a different (2 column) index, so there is no 3rd column in index to sort by - hr = SceBeginQuery(pDatabase, TABLE_A, 1, &query); - NativeAssert::Succeeded(hr, "Failed to begin query"); - - hr = SceSetQueryColumnDword(query, 3); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceSetQueryColumnString(query, L"yyy"); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceRunQueryRange(&query, &results); - NativeAssert::Succeeded(hr, "Failed to run query"); - NativeAssert::True(query == NULL); - - TableARowValue *sortedAfterQuery4[] = { &value2, &value5 }; - VerifyQuery(sortedAfterQuery4, _countof(sortedAfterQuery4), results); - ReleaseNullSceQueryResults(results); - } - - void TestReadWriteSchemaV2(SCE_DATABASE *pDatabase) - { - HRESULT hr = S_OK; - BYTE binary1[40] = { 0x55, 0x44 }; - DWORD dwValue1 = 58; - TableARowValue value1 = {}; - SCE_QUERY_HANDLE query = NULL; - SCE_ROW_HANDLE row = NULL; - - SetStructValues(&value1, static_cast(binary1), sizeof(binary1), 5, 1, TRUE, L"zzz", &dwValue1, L"newextrastring"); - - InsertRow(pDatabase, &value1, FALSE); - - // Test setting 1 column - hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); - NativeAssert::Succeeded(hr, "Failed to begin query"); - - hr = SceSetQueryColumnDword(query, 5); - NativeAssert::Succeeded(hr, "Failed to set query column dword"); - - hr = SceRunQueryExact(&query, &row); - NativeAssert::Succeeded(hr, "Failed to run query exact"); - - VerifyRow(&value1, row); - } - - [Fact] - void SceUtilTest() - { - HRESULT hr = S_OK; - BOOL fComInitialized = FALSE; - LPWSTR sczDbPath = NULL; - SCE_DATABASE *pDatabase = NULL; - SCE_DATABASE_SCHEMA schema1 = {}; - SCE_DATABASE_SCHEMA schema2 = {}; - - try - { - hr = ::CoInitialize(0); - NativeAssert::Succeeded(hr, "Failed to initialize COM"); - fComInitialized = TRUE; - - SetupSchema(&schema1, FALSE); - SetupSchema(&schema2, TRUE); - - hr = PathExpand(&sczDbPath, L"%TEMP%\\SceUtilTest\\UnitTest.sdf", PATH_EXPAND_ENVIRONMENT); - NativeAssert::Succeeded(hr, "Failed to get path to test database"); - - FileEnsureDelete(sczDbPath); - - hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema1, &pDatabase); - NativeAssert::Succeeded(hr, "Failed to ensure database schema"); - - TestIndex(pDatabase); - - hr = SceCloseDatabase(pDatabase); - pDatabase = NULL; - NativeAssert::Succeeded(hr, "Failed to close database"); - - // Add column to schema - hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema2, &pDatabase); - NativeAssert::Succeeded(hr, "Failed to ensure database schema"); - - TestReadWriteSchemaV2(pDatabase); - } - finally - { - ReleaseSceSchema(&schema1); - ReleaseSceSchema(&schema2); - - if (NULL != pDatabase) - { - hr = SceCloseDatabase(pDatabase); - NativeAssert::Succeeded(hr, "Failed to close database"); - } - ReleaseStr(sczDbPath); - - if (fComInitialized) - { - ::CoUninitialize(); - } - } - } - }; -} diff --git a/src/test/DUtilUnitTest/StrUtilTest.cpp b/src/test/DUtilUnitTest/StrUtilTest.cpp deleted file mode 100644 index 94fee280..00000000 --- a/src/test/DUtilUnitTest/StrUtilTest.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class StrUtil - { - public: - [Fact] - void StrUtilFormattedTest() - { - HRESULT hr = S_OK; - LPWSTR sczText = NULL; - - try - { - hr = StrAllocFormatted(&sczText, L"%hs - %ls - %u", "ansi string", L"unicode string", 1234); - NativeAssert::Succeeded(hr, "Failed to format string."); - NativeAssert::StringEqual(L"ansi string - unicode string - 1234", sczText); - - ReleaseNullStr(sczText); - - hr = StrAllocString(&sczText, L"repeat", 0); - NativeAssert::Succeeded(hr, "Failed to allocate string."); - - hr = StrAllocFormatted(&sczText, L"%ls and %ls", sczText, sczText); - NativeAssert::Succeeded(hr, "Failed to format string unto itself."); - NativeAssert::StringEqual(L"repeat and repeat", sczText); - } - finally - { - ReleaseStr(sczText); - } - } - - [Fact] - void StrUtilTrimTest() - { - TestTrim(L"", L""); - TestTrim(L"Blah", L"Blah"); - TestTrim(L"\t\t\tBlah", L"Blah"); - TestTrim(L"\t Blah ", L"Blah"); - TestTrim(L"Blah ", L"Blah"); - TestTrim(L"\t Spaces \t Between \t", L"Spaces \t Between"); - TestTrim(L" \t\t\t ", L""); - - TestTrimAnsi("", ""); - TestTrimAnsi("Blah", "Blah"); - TestTrimAnsi("\t\t\tBlah", "Blah"); - TestTrimAnsi(" Blah ", "Blah"); - TestTrimAnsi("Blah ", "Blah"); - TestTrimAnsi("\t Spaces \t Between \t", "Spaces \t Between"); - TestTrimAnsi(" \t\t\t ", ""); - } - - [Fact] - void StrUtilConvertTest() - { - char a[] = { 'a', 'b', 'C', 'd', '\0', '\0' }; - - TestStrAllocStringAnsi(a, 5, L"abCd"); - TestStrAllocStringAnsi(a, 4, L"abCd"); - TestStrAllocStringAnsi(a, 3, L"abC"); - TestStrAllocStringAnsi(a, 2, L"ab"); - TestStrAllocStringAnsi(a, 1, L"a"); - TestStrAllocStringAnsi(a, 0, L"abCd"); - - wchar_t b[] = { L'a', L'b', L'C', L'd', L'\0', L'\0' }; - - TestStrAnsiAllocString(b, 5, "abCd"); - TestStrAnsiAllocString(b, 4, "abCd"); - TestStrAnsiAllocString(b, 3, "abC"); - TestStrAnsiAllocString(b, 2, "ab"); - TestStrAnsiAllocString(b, 1, "a"); - TestStrAnsiAllocString(b, 0, "abCd"); - } - - private: - void TestTrim(LPCWSTR wzInput, LPCWSTR wzExpectedResult) - { - HRESULT hr = S_OK; - LPWSTR sczOutput = NULL; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = StrTrimWhitespace(&sczOutput, wzInput); - NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: {0}", wzInput); - - if (0 != wcscmp(wzExpectedResult, sczOutput)) - { - hr = E_FAIL; - ExitOnFailure(hr, "Trimmed string \"%ls\", expected result \"%ls\", actual result \"%ls\"", wzInput, wzExpectedResult, sczOutput); - } - } - finally - { - ReleaseStr(sczOutput); - } - - LExit: - DutilUninitialize(); - } - - void TestTrimAnsi(LPCSTR szInput, LPCSTR szExpectedResult) - { - HRESULT hr = S_OK; - LPSTR sczOutput = NULL; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = StrAnsiTrimWhitespace(&sczOutput, szInput); - NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: \"{0}\"", szInput); - - if (0 != strcmp(szExpectedResult, sczOutput)) - { - hr = E_FAIL; - ExitOnFailure(hr, "Trimmed string \"%hs\", expected result \"%hs\", actual result \"%hs\"", szInput, szExpectedResult, sczOutput); - } - } - finally - { - ReleaseStr(sczOutput); - } - - LExit: - DutilUninitialize(); - } - - void TestStrAllocStringAnsi(LPCSTR szSource, DWORD cchSource, LPCWSTR wzExpectedResult) - { - HRESULT hr = S_OK; - LPWSTR sczOutput = NULL; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = StrAllocStringAnsi(&sczOutput, szSource, cchSource, CP_UTF8); - NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", szSource); - - if (0 != wcscmp(sczOutput, wzExpectedResult)) - { - hr = E_FAIL; - ExitOnFailure(hr, "String doesn't match, expected result \"%ls\", actual result \"%ls\"", wzExpectedResult, sczOutput); - } - } - finally - { - ReleaseStr(sczOutput); - } - - LExit: - DutilUninitialize(); - } - - void TestStrAnsiAllocString(LPWSTR wzSource, DWORD cchSource, LPCSTR szExpectedResult) - { - HRESULT hr = S_OK; - LPSTR sczOutput = NULL; - - DutilInitialize(&DutilTestTraceError); - - try - { - hr = StrAnsiAllocString(&sczOutput, wzSource, cchSource, CP_UTF8); - NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", wzSource); - - if (0 != strcmp(sczOutput, szExpectedResult)) - { - hr = E_FAIL; - ExitOnFailure(hr, "String doesn't match, expected result \"%hs\", actual result \"%hs\"", szExpectedResult, sczOutput); - } - } - finally - { - ReleaseStr(sczOutput); - } - - LExit: - DutilUninitialize(); - } - }; -} diff --git a/src/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml b/src/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml deleted file mode 100644 index d9f961fe..00000000 --- a/src/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - BundleB v2.0 - Bundle Subtitle. - 1116353B-7C6E-4C29-BFA1-D4A972CD421D - 2014-07-14T12:39:00.000Z - http://localhost:9999/wix4/BundleB/feed - - manual build - - Bundle v2.0 - v2.0 - - Bundle_Author - http://mycompany.com/software - Bundle_Author@mycompany.com - - - - - <p>Change list:</p><ul> - <li>Updated release.</li> - </ul> - - 2.0.0.0 - 2014-11-10T12:39:00.000Z - - - Bundle v1.0 - v1.0 - - Bundle_Author - http://mycompany.com/software - Bundle_Author@mycompany.com - - - - - <p>Change list:</p><ul> - <li>Initial release.</li> - </ul> - - - 1.0.0.0 - 2014-11-09T12:39:00.000Z - - - Bundle v1.0-preview - v1.0-preview - - Bundle_Author - http://mycompany.com/software - Bundle_Author@mycompany.com - - - - - <p>Change list:</p><ul> - <li>Initial release.</li> - </ul> - - 1.0.0.0 - 2014-11-09T12:39:00.000Z - - diff --git a/src/test/DUtilUnitTest/UnitTest.rc b/src/test/DUtilUnitTest/UnitTest.rc deleted file mode 100644 index 14cebe1a..00000000 --- a/src/test/DUtilUnitTest/UnitTest.rc +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#define VER_APP -#define VER_ORIGINAL_FILENAME "UnitTest.dll" -#define VER_INTERNAL_NAME "setup" -#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests" diff --git a/src/test/DUtilUnitTest/UriUtilTest.cpp b/src/test/DUtilUnitTest/UriUtilTest.cpp deleted file mode 100644 index b3bf87a2..00000000 --- a/src/test/DUtilUnitTest/UriUtilTest.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace System::Text; -using namespace System::Collections::Generic; -using namespace Xunit; - -namespace CfgTests -{ - public ref class UriUtil - { - public: - [Fact] - void UriProtocolTest() - { - HRESULT hr = S_OK; - - DutilInitialize(&DutilTestTraceError); - - LPCWSTR uri = L"https://localhost/"; - URI_PROTOCOL uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); - - uri = L"HTTPS://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); - - uri = L"HtTpS://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol); - - uri = L"HTTP://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); - - uri = L"http://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); - - uri = L"HtTp://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol); - - uri = L"file://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); - - uri = L"FILE://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); - - uri = L"FiLe://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol); - - uri = L"FTP://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); - - uri = L"ftp://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); - - uri = L"FtP://localhost/"; - uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN; - hr = UriProtocol(uri, &uriProtocol); - ExitOnFailure(hr, "Failed to determine UriProtocol"); - Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol); - - LExit: - DutilUninitialize(); - } - }; -} diff --git a/src/test/DUtilUnitTest/VerUtilTests.cpp b/src/test/DUtilUnitTest/VerUtilTests.cpp deleted file mode 100644 index 8f24ad1a..00000000 --- a/src/test/DUtilUnitTest/VerUtilTests.cpp +++ /dev/null @@ -1,933 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System; -using namespace Xunit; -using namespace WixBuildTools::TestSupport; - -namespace DutilTests -{ - public ref class VerUtil - { - public: - [Fact] - void VerCompareVersionsTreatsMissingRevisionAsZero() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - LPCWSTR wzVersion1 = L"1.2.3.4"; - LPCWSTR wzVersion2 = L"1.2.3"; - LPCWSTR wzVersion3 = L"1.2.3.0"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(1, pVersion1->dwMajor); - Assert::Equal(2, pVersion1->dwMinor); - Assert::Equal(3, pVersion1->dwPatch); - Assert::Equal(4, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(7, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(1, pVersion2->dwMajor); - Assert::Equal(2, pVersion2->dwMinor); - Assert::Equal(3, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(5, pVersion2->cchMetadataOffset); - Assert::Equal(FALSE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); - Assert::Equal(1, pVersion3->dwMajor); - Assert::Equal(2, pVersion3->dwMinor); - Assert::Equal(3, pVersion3->dwPatch); - Assert::Equal(0, pVersion3->dwRevision); - Assert::Equal(0, pVersion3->cReleaseLabels); - Assert::Equal(7, pVersion3->cchMetadataOffset); - Assert::Equal(FALSE, pVersion3->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); - TestVerutilCompareParsedVersions(pVersion3, pVersion2, 0); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - } - } - - [Fact] - void VerCompareVersionsTreatsNumericReleaseLabelsAsNumbers() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - LPCWSTR wzVersion1 = L"1.0-2.0"; - LPCWSTR wzVersion2 = L"1.0-19"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(1, pVersion1->dwMajor); - Assert::Equal(0, pVersion1->dwMinor); - Assert::Equal(0, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(2, pVersion1->cReleaseLabels); - - Assert::Equal(TRUE, pVersion1->rgReleaseLabels[0].fNumeric); - Assert::Equal(2, pVersion1->rgReleaseLabels[0].dwValue); - Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); - Assert::Equal(4, pVersion1->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(TRUE, pVersion1->rgReleaseLabels[1].fNumeric); - Assert::Equal(0, pVersion1->rgReleaseLabels[1].dwValue); - Assert::Equal(1, pVersion1->rgReleaseLabels[1].cchLabel); - Assert::Equal(6, pVersion1->rgReleaseLabels[1].cchLabelOffset); - - Assert::Equal(7, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(1, pVersion2->dwMajor); - Assert::Equal(0, pVersion2->dwMinor); - Assert::Equal(0, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(1, pVersion2->cReleaseLabels); - - Assert::Equal(TRUE, pVersion2->rgReleaseLabels[0].fNumeric); - Assert::Equal(19, pVersion2->rgReleaseLabels[0].dwValue); - Assert::Equal(2, pVersion2->rgReleaseLabels[0].cchLabel); - Assert::Equal(4, pVersion2->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(6, pVersion2->cchMetadataOffset); - Assert::Equal(FALSE, pVersion2->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, -1); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - } - } - - [Fact] - void VerCompareVersionsHandlesNormallyInvalidVersions() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - VERUTIL_VERSION* pVersion4 = NULL; - VERUTIL_VERSION* pVersion5 = NULL; - VERUTIL_VERSION* pVersion6 = NULL; - LPCWSTR wzVersion1 = L"10.-4.0"; - LPCWSTR wzVersion2 = L"10.-2.0"; - LPCWSTR wzVersion3 = L"0"; - LPCWSTR wzVersion4 = L""; - LPCWSTR wzVersion5 = L"10-2"; - LPCWSTR wzVersion6 = L"10-4.@"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); - - hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5); - - hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(10, pVersion1->dwMajor); - Assert::Equal(0, pVersion1->dwMinor); - Assert::Equal(0, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(3, pVersion1->cchMetadataOffset); - Assert::Equal(TRUE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(10, pVersion2->dwMajor); - Assert::Equal(0, pVersion2->dwMinor); - Assert::Equal(0, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(3, pVersion2->cchMetadataOffset); - Assert::Equal(TRUE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); - Assert::Equal(0, pVersion3->dwMajor); - Assert::Equal(0, pVersion3->dwMinor); - Assert::Equal(0, pVersion3->dwPatch); - Assert::Equal(0, pVersion3->dwRevision); - Assert::Equal(0, pVersion3->cReleaseLabels); - Assert::Equal(1, pVersion3->cchMetadataOffset); - Assert::Equal(FALSE, pVersion3->fInvalid); - - NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); - Assert::Equal(0, pVersion4->dwMajor); - Assert::Equal(0, pVersion4->dwMinor); - Assert::Equal(0, pVersion4->dwPatch); - Assert::Equal(0, pVersion4->dwRevision); - Assert::Equal(0, pVersion4->cReleaseLabels); - Assert::Equal(0, pVersion4->cchMetadataOffset); - Assert::Equal(TRUE, pVersion4->fInvalid); - - NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion); - Assert::Equal(10, pVersion5->dwMajor); - Assert::Equal(0, pVersion5->dwMinor); - Assert::Equal(0, pVersion5->dwPatch); - Assert::Equal(0, pVersion5->dwRevision); - Assert::Equal(1, pVersion5->cReleaseLabels); - - Assert::Equal(TRUE, pVersion5->rgReleaseLabels[0].fNumeric); - Assert::Equal(2, pVersion5->rgReleaseLabels[0].dwValue); - Assert::Equal(1, pVersion5->rgReleaseLabels[0].cchLabel); - Assert::Equal(3, pVersion5->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(4, pVersion5->cchMetadataOffset); - Assert::Equal(FALSE, pVersion5->fInvalid); - - NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion); - Assert::Equal(10, pVersion6->dwMajor); - Assert::Equal(0, pVersion6->dwMinor); - Assert::Equal(0, pVersion6->dwPatch); - Assert::Equal(0, pVersion6->dwRevision); - Assert::Equal(1, pVersion6->cReleaseLabels); - - Assert::Equal(TRUE, pVersion6->rgReleaseLabels[0].fNumeric); - Assert::Equal(4, pVersion6->rgReleaseLabels[0].dwValue); - Assert::Equal(1, pVersion6->rgReleaseLabels[0].cchLabel); - Assert::Equal(3, pVersion6->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(5, pVersion6->cchMetadataOffset); - Assert::Equal(TRUE, pVersion6->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); - TestVerutilCompareParsedVersions(pVersion3, pVersion4, 1); - TestVerutilCompareParsedVersions(pVersion5, pVersion6, -1); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - ReleaseVerutilVersion(pVersion4); - ReleaseVerutilVersion(pVersion5); - ReleaseVerutilVersion(pVersion6); - } - } - - [Fact] - void VerCompareVersionsTreatsHyphenAsVersionSeparator() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - LPCWSTR wzVersion1 = L"0.0.1-a"; - LPCWSTR wzVersion2 = L"0-2"; - LPCWSTR wzVersion3 = L"1-2"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(0, pVersion1->dwMajor); - Assert::Equal(0, pVersion1->dwMinor); - Assert::Equal(1, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(1, pVersion1->cReleaseLabels); - - Assert::Equal(FALSE, pVersion1->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); - Assert::Equal(6, pVersion1->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(7, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(0, pVersion2->dwMajor); - Assert::Equal(0, pVersion2->dwMinor); - Assert::Equal(0, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(1, pVersion2->cReleaseLabels); - - Assert::Equal(TRUE, pVersion2->rgReleaseLabels[0].fNumeric); - Assert::Equal(2, pVersion2->rgReleaseLabels[0].dwValue); - Assert::Equal(1, pVersion2->rgReleaseLabels[0].cchLabel); - Assert::Equal(2, pVersion2->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(3, pVersion2->cchMetadataOffset); - Assert::Equal(FALSE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); - Assert::Equal(1, pVersion3->dwMajor); - Assert::Equal(0, pVersion3->dwMinor); - Assert::Equal(0, pVersion3->dwPatch); - Assert::Equal(0, pVersion3->dwRevision); - Assert::Equal(1, pVersion3->cReleaseLabels); - - Assert::Equal(TRUE, pVersion3->rgReleaseLabels[0].fNumeric); - Assert::Equal(2, pVersion3->rgReleaseLabels[0].dwValue); - Assert::Equal(1, pVersion3->rgReleaseLabels[0].cchLabel); - Assert::Equal(2, pVersion3->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(3, pVersion3->cchMetadataOffset); - Assert::Equal(FALSE, pVersion3->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); - TestVerutilCompareParsedVersions(pVersion1, pVersion3, -1); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - } - } - - [Fact] - void VerCompareVersionsIgnoresLeadingZeroes() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - VERUTIL_VERSION* pVersion4 = NULL; - LPCWSTR wzVersion1 = L"0.01-a.1"; - LPCWSTR wzVersion2 = L"0.1.0-a.1"; - LPCWSTR wzVersion3 = L"0.1-a.b.0"; - LPCWSTR wzVersion4 = L"0.1.0-a.b.000"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(0, pVersion1->dwMajor); - Assert::Equal(1, pVersion1->dwMinor); - Assert::Equal(0, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(2, pVersion1->cReleaseLabels); - - Assert::Equal(FALSE, pVersion1->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pVersion1->rgReleaseLabels[0].cchLabel); - Assert::Equal(5, pVersion1->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(TRUE, pVersion1->rgReleaseLabels[1].fNumeric); - Assert::Equal(1, pVersion1->rgReleaseLabels[1].dwValue); - Assert::Equal(1, pVersion1->rgReleaseLabels[1].cchLabel); - Assert::Equal(7, pVersion1->rgReleaseLabels[1].cchLabelOffset); - - Assert::Equal(8, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(0, pVersion2->dwMajor); - Assert::Equal(1, pVersion2->dwMinor); - Assert::Equal(0, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(2, pVersion2->cReleaseLabels); - - Assert::Equal(FALSE, pVersion2->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pVersion2->rgReleaseLabels[0].cchLabel); - Assert::Equal(6, pVersion2->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(TRUE, pVersion2->rgReleaseLabels[1].fNumeric); - Assert::Equal(1, pVersion2->rgReleaseLabels[1].dwValue); - Assert::Equal(1, pVersion2->rgReleaseLabels[1].cchLabel); - Assert::Equal(8, pVersion2->rgReleaseLabels[1].cchLabelOffset); - - Assert::Equal(9, pVersion2->cchMetadataOffset); - Assert::Equal(FALSE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); - Assert::Equal(0, pVersion3->dwMajor); - Assert::Equal(1, pVersion3->dwMinor); - Assert::Equal(0, pVersion3->dwPatch); - Assert::Equal(0, pVersion3->dwRevision); - Assert::Equal(3, pVersion3->cReleaseLabels); - - Assert::Equal(FALSE, pVersion3->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pVersion3->rgReleaseLabels[0].cchLabel); - Assert::Equal(4, pVersion3->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(FALSE, pVersion3->rgReleaseLabels[1].fNumeric); - Assert::Equal(1, pVersion3->rgReleaseLabels[1].cchLabel); - Assert::Equal(6, pVersion3->rgReleaseLabels[1].cchLabelOffset); - - Assert::Equal(TRUE, pVersion3->rgReleaseLabels[2].fNumeric); - Assert::Equal(0, pVersion3->rgReleaseLabels[2].dwValue); - Assert::Equal(1, pVersion3->rgReleaseLabels[2].cchLabel); - Assert::Equal(8, pVersion3->rgReleaseLabels[2].cchLabelOffset); - - Assert::Equal(9, pVersion3->cchMetadataOffset); - Assert::Equal(FALSE, pVersion3->fInvalid); - - NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); - Assert::Equal(0, pVersion4->dwMajor); - Assert::Equal(1, pVersion4->dwMinor); - Assert::Equal(0, pVersion4->dwPatch); - Assert::Equal(0, pVersion4->dwRevision); - Assert::Equal(3, pVersion4->cReleaseLabels); - - Assert::Equal(FALSE, pVersion4->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pVersion4->rgReleaseLabels[0].cchLabel); - Assert::Equal(6, pVersion4->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(FALSE, pVersion4->rgReleaseLabels[1].fNumeric); - Assert::Equal(1, pVersion4->rgReleaseLabels[1].cchLabel); - Assert::Equal(8, pVersion4->rgReleaseLabels[1].cchLabelOffset); - - Assert::Equal(TRUE, pVersion4->rgReleaseLabels[2].fNumeric); - Assert::Equal(0, pVersion4->rgReleaseLabels[2].dwValue); - Assert::Equal(3, pVersion4->rgReleaseLabels[2].cchLabel); - Assert::Equal(10, pVersion4->rgReleaseLabels[2].cchLabelOffset); - - Assert::Equal(13, pVersion4->cchMetadataOffset); - Assert::Equal(FALSE, pVersion4->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); - TestVerutilCompareParsedVersions(pVersion3, pVersion4, 0); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - ReleaseVerutilVersion(pVersion4); - } - } - - [Fact] - void VerCompareVersionsTreatsUnexpectedContentAsMetadata() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - LPCWSTR wzVersion1 = L"1.2.3+abcd"; - LPCWSTR wzVersion2 = L"1.2.3.abcd"; - LPCWSTR wzVersion3 = L"1.2.3.-abcd"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(1, pVersion1->dwMajor); - Assert::Equal(2, pVersion1->dwMinor); - Assert::Equal(3, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(6, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(1, pVersion2->dwMajor); - Assert::Equal(2, pVersion2->dwMinor); - Assert::Equal(3, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(6, pVersion2->cchMetadataOffset); - Assert::Equal(TRUE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); - Assert::Equal(1, pVersion3->dwMajor); - Assert::Equal(2, pVersion3->dwMinor); - Assert::Equal(3, pVersion3->dwPatch); - Assert::Equal(0, pVersion3->dwRevision); - Assert::Equal(0, pVersion3->cReleaseLabels); - Assert::Equal(6, pVersion3->cchMetadataOffset); - Assert::Equal(TRUE, pVersion3->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); - TestVerutilCompareParsedVersions(pVersion1, pVersion3, 1); - TestVerutilCompareParsedVersions(pVersion2, pVersion3, -1); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - } - } - - [Fact] - void VerCompareVersionsIgnoresLeadingV() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - LPCWSTR wzVersion1 = L"10.20.30.40"; - LPCWSTR wzVersion2 = L"v10.20.30.40"; - LPCWSTR wzVersion3 = L"V10.20.30.40"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(10, pVersion1->dwMajor); - Assert::Equal(20, pVersion1->dwMinor); - Assert::Equal(30, pVersion1->dwPatch); - Assert::Equal(40, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(11, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion1, pVersion2->sczVersion); - Assert::Equal(10, pVersion2->dwMajor); - Assert::Equal(20, pVersion2->dwMinor); - Assert::Equal(30, pVersion2->dwPatch); - Assert::Equal(40, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(11, pVersion2->cchMetadataOffset); - Assert::Equal(FALSE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion1, pVersion3->sczVersion); - Assert::Equal(10, pVersion3->dwMajor); - Assert::Equal(20, pVersion3->dwMinor); - Assert::Equal(30, pVersion3->dwPatch); - Assert::Equal(40, pVersion3->dwRevision); - Assert::Equal(0, pVersion3->cReleaseLabels); - Assert::Equal(11, pVersion3->cchMetadataOffset); - Assert::Equal(FALSE, pVersion3->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); - TestVerutilCompareParsedVersions(pVersion1, pVersion3, 0); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - } - } - - [Fact] - void VerCompareVersionsHandlesTooLargeNumbers() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - LPCWSTR wzVersion1 = L"4294967295.4294967295.4294967295.4294967295"; - LPCWSTR wzVersion2 = L"4294967296.4294967296.4294967296.4294967296"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(4294967295, pVersion1->dwMajor); - Assert::Equal(4294967295, pVersion1->dwMinor); - Assert::Equal(4294967295, pVersion1->dwPatch); - Assert::Equal(4294967295, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(43, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(0, pVersion2->dwMajor); - Assert::Equal(0, pVersion2->dwMinor); - Assert::Equal(0, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(0, pVersion2->cchMetadataOffset); - Assert::Equal(TRUE, pVersion2->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - } - } - - [Fact] - void VerCompareVersionsIgnoresMetadataForValidVersions() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - LPCWSTR wzVersion1 = L"1.2.3+abc"; - LPCWSTR wzVersion2 = L"1.2.3+xyz"; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(1, pVersion1->dwMajor); - Assert::Equal(2, pVersion1->dwMinor); - Assert::Equal(3, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(6, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(1, pVersion2->dwMajor); - Assert::Equal(2, pVersion2->dwMinor); - Assert::Equal(3, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(6, pVersion2->cchMetadataOffset); - Assert::Equal(FALSE, pVersion2->fInvalid); - - TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - } - } - - [Fact] - void VerCopyVersionCopiesVersion() - { - HRESULT hr = S_OK; - LPCWSTR wzVersion = L"1.2.3.4+abc123"; - VERUTIL_VERSION* pSource = NULL; - VERUTIL_VERSION* pCopy = NULL; - int nResult = 0; - - try - { - hr = VerParseVersion(wzVersion, 0, FALSE, &pSource); - NativeAssert::Succeeded(hr, "VerParseVersion failed"); - - NativeAssert::StringEqual(wzVersion, pSource->sczVersion); - Assert::Equal(1, pSource->dwMajor); - Assert::Equal(2, pSource->dwMinor); - Assert::Equal(3, pSource->dwPatch); - Assert::Equal(4, pSource->dwRevision); - Assert::Equal(0, pSource->cReleaseLabels); - - Assert::Equal(8, pSource->cchMetadataOffset); - Assert::Equal(FALSE, pSource->fInvalid); - - hr = VerCopyVersion(pSource, &pCopy); - NativeAssert::Succeeded(hr, "VerCopyVersion failed"); - - Assert::False(pSource == pCopy); - Assert::False(pSource->sczVersion == pCopy->sczVersion); - - hr = VerCompareParsedVersions(pSource, pCopy, &nResult); - NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed"); - - Assert::Equal(nResult, 0); - } - finally - { - ReleaseVerutilVersion(pCopy); - ReleaseVerutilVersion(pSource); - } - } - - [Fact] - void VerCopyVersionCopiesPrereleaseVersion() - { - HRESULT hr = S_OK; - LPCWSTR wzVersion = L"1.2.3.4-a.b.c.d.5.+abc123"; - VERUTIL_VERSION* pSource = NULL; - VERUTIL_VERSION* pCopy = NULL; - int nResult = 0; - - try - { - hr = VerParseVersion(wzVersion, 0, FALSE, &pSource); - NativeAssert::Succeeded(hr, "VerParseVersion failed"); - - NativeAssert::StringEqual(wzVersion, pSource->sczVersion); - Assert::Equal(1, pSource->dwMajor); - Assert::Equal(2, pSource->dwMinor); - Assert::Equal(3, pSource->dwPatch); - Assert::Equal(4, pSource->dwRevision); - Assert::Equal(5, pSource->cReleaseLabels); - - Assert::Equal(FALSE, pSource->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pSource->rgReleaseLabels[0].cchLabel); - Assert::Equal(8, pSource->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(FALSE, pSource->rgReleaseLabels[1].fNumeric); - Assert::Equal(1, pSource->rgReleaseLabels[1].cchLabel); - Assert::Equal(10, pSource->rgReleaseLabels[1].cchLabelOffset); - - Assert::Equal(FALSE, pSource->rgReleaseLabels[2].fNumeric); - Assert::Equal(1, pSource->rgReleaseLabels[2].cchLabel); - Assert::Equal(12, pSource->rgReleaseLabels[2].cchLabelOffset); - - Assert::Equal(FALSE, pSource->rgReleaseLabels[3].fNumeric); - Assert::Equal(1, pSource->rgReleaseLabels[3].cchLabel); - Assert::Equal(14, pSource->rgReleaseLabels[3].cchLabelOffset); - - Assert::Equal(TRUE, pSource->rgReleaseLabels[4].fNumeric); - Assert::Equal(5, pSource->rgReleaseLabels[4].dwValue); - Assert::Equal(1, pSource->rgReleaseLabels[4].cchLabel); - Assert::Equal(16, pSource->rgReleaseLabels[4].cchLabelOffset); - - Assert::Equal(18, pSource->cchMetadataOffset); - Assert::Equal(TRUE, pSource->fInvalid); - - hr = VerCopyVersion(pSource, &pCopy); - NativeAssert::Succeeded(hr, "VerCopyVersion failed"); - - Assert::False(pSource == pCopy); - Assert::False(pSource->sczVersion == pCopy->sczVersion); - Assert::False(pSource->rgReleaseLabels == pCopy->rgReleaseLabels); - - hr = VerCompareParsedVersions(pSource, pCopy, &nResult); - NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed"); - - Assert::Equal(nResult, 0); - } - finally - { - ReleaseVerutilVersion(pCopy); - ReleaseVerutilVersion(pSource); - } - } - - [Fact] - void VerParseVersionTreatsTrailingDotsAsInvalid() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - VERUTIL_VERSION* pVersion2 = NULL; - VERUTIL_VERSION* pVersion3 = NULL; - VERUTIL_VERSION* pVersion4 = NULL; - VERUTIL_VERSION* pVersion5 = NULL; - VERUTIL_VERSION* pVersion6 = NULL; - VERUTIL_VERSION* pVersion7 = NULL; - LPCWSTR wzVersion1 = L"."; - LPCWSTR wzVersion2 = L"1."; - LPCWSTR wzVersion3 = L"2.1."; - LPCWSTR wzVersion4 = L"3.2.1."; - LPCWSTR wzVersion5 = L"4.3.2.1."; - LPCWSTR wzVersion6 = L"5-."; - LPCWSTR wzVersion7 = L"6-a."; - - try - { - hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1); - - hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2); - - hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3); - - hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4); - - hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5); - - hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6); - - hr = VerParseVersion(wzVersion7, 0, FALSE, &pVersion7); - NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion7); - - NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion); - Assert::Equal(0, pVersion1->dwMajor); - Assert::Equal(0, pVersion1->dwMinor); - Assert::Equal(0, pVersion1->dwPatch); - Assert::Equal(0, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(0, pVersion1->cchMetadataOffset); - Assert::Equal(TRUE, pVersion1->fInvalid); - - NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion); - Assert::Equal(1, pVersion2->dwMajor); - Assert::Equal(0, pVersion2->dwMinor); - Assert::Equal(0, pVersion2->dwPatch); - Assert::Equal(0, pVersion2->dwRevision); - Assert::Equal(0, pVersion2->cReleaseLabels); - Assert::Equal(2, pVersion2->cchMetadataOffset); - Assert::Equal(TRUE, pVersion2->fInvalid); - - NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion); - Assert::Equal(2, pVersion3->dwMajor); - Assert::Equal(1, pVersion3->dwMinor); - Assert::Equal(0, pVersion3->dwPatch); - Assert::Equal(0, pVersion3->dwRevision); - Assert::Equal(0, pVersion3->cReleaseLabels); - Assert::Equal(4, pVersion3->cchMetadataOffset); - Assert::Equal(TRUE, pVersion3->fInvalid); - - NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion); - Assert::Equal(3, pVersion4->dwMajor); - Assert::Equal(2, pVersion4->dwMinor); - Assert::Equal(1, pVersion4->dwPatch); - Assert::Equal(0, pVersion4->dwRevision); - Assert::Equal(0, pVersion4->cReleaseLabels); - Assert::Equal(6, pVersion4->cchMetadataOffset); - Assert::Equal(TRUE, pVersion4->fInvalid); - - NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion); - Assert::Equal(4, pVersion5->dwMajor); - Assert::Equal(3, pVersion5->dwMinor); - Assert::Equal(2, pVersion5->dwPatch); - Assert::Equal(1, pVersion5->dwRevision); - Assert::Equal(0, pVersion5->cReleaseLabels); - Assert::Equal(8, pVersion5->cchMetadataOffset); - Assert::Equal(TRUE, pVersion5->fInvalid); - - NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion); - Assert::Equal(5, pVersion6->dwMajor); - Assert::Equal(0, pVersion6->dwMinor); - Assert::Equal(0, pVersion6->dwPatch); - Assert::Equal(0, pVersion6->dwRevision); - Assert::Equal(0, pVersion6->cReleaseLabels); - Assert::Equal(2, pVersion6->cchMetadataOffset); - Assert::Equal(TRUE, pVersion6->fInvalid); - - NativeAssert::StringEqual(wzVersion7, pVersion7->sczVersion); - Assert::Equal(6, pVersion7->dwMajor); - Assert::Equal(0, pVersion7->dwMinor); - Assert::Equal(0, pVersion7->dwPatch); - Assert::Equal(0, pVersion7->dwRevision); - Assert::Equal(1, pVersion7->cReleaseLabels); - - Assert::Equal(FALSE, pVersion7->rgReleaseLabels[0].fNumeric); - Assert::Equal(1, pVersion7->rgReleaseLabels[0].cchLabel); - Assert::Equal(2, pVersion7->rgReleaseLabels[0].cchLabelOffset); - - Assert::Equal(4, pVersion7->cchMetadataOffset); - Assert::Equal(TRUE, pVersion7->fInvalid); - } - finally - { - ReleaseVerutilVersion(pVersion1); - ReleaseVerutilVersion(pVersion2); - ReleaseVerutilVersion(pVersion3); - ReleaseVerutilVersion(pVersion4); - ReleaseVerutilVersion(pVersion5); - ReleaseVerutilVersion(pVersion6); - ReleaseVerutilVersion(pVersion7); - } - } - - [Fact] - void VerVersionFromQwordCreatesVersion() - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion1 = NULL; - - try - { - hr = VerVersionFromQword(MAKEQWORDVERSION(1, 2, 3, 4), &pVersion1); - NativeAssert::Succeeded(hr, "VerVersionFromQword failed"); - - NativeAssert::StringEqual(L"1.2.3.4", pVersion1->sczVersion); - Assert::Equal(1, pVersion1->dwMajor); - Assert::Equal(2, pVersion1->dwMinor); - Assert::Equal(3, pVersion1->dwPatch); - Assert::Equal(4, pVersion1->dwRevision); - Assert::Equal(0, pVersion1->cReleaseLabels); - Assert::Equal(7, pVersion1->cchMetadataOffset); - Assert::Equal(FALSE, pVersion1->fInvalid); - } - finally - { - ReleaseVerutilVersion(pVersion1); - } - } - - private: - void TestVerutilCompareParsedVersions(VERUTIL_VERSION* pVersion1, VERUTIL_VERSION* pVersion2, int nExpectedResult) - { - HRESULT hr = S_OK; - int nResult = 0; - - hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult); - NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion); - - Assert::Equal(nExpectedResult, nResult); - - hr = VerCompareParsedVersions(pVersion2, pVersion1, &nResult); - NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion); - - Assert::Equal(nExpectedResult, -nResult); - } - }; -} diff --git a/src/test/DUtilUnitTest/error.cpp b/src/test/DUtilUnitTest/error.cpp deleted file mode 100644 index e51971c3..00000000 --- a/src/test/DUtilUnitTest/error.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -const int ERROR_STRING_BUFFER = 1024; - -static char szMsg[ERROR_STRING_BUFFER]; -static WCHAR wzMsg[ERROR_STRING_BUFFER]; - -void CALLBACK DutilTestTraceError( - __in_z LPCSTR /*szFile*/, - __in int /*iLine*/, - __in REPORT_LEVEL /*rl*/, - __in UINT source, - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ) -{ - if (DUTIL_SOURCE_EXTERNAL == source) - { - ::StringCchPrintfA(szMsg, countof(szMsg), szFormat, args); - MultiByteToWideChar(CP_ACP, 0, szMsg, -1, wzMsg, countof(wzMsg)); - throw gcnew System::Exception(System::String::Format("hr = 0x{0:X8}, message = {1}", hrError, gcnew System::String(wzMsg))); - } -} diff --git a/src/test/DUtilUnitTest/error.h b/src/test/DUtilUnitTest/error.h deleted file mode 100644 index b973acaf..00000000 --- a/src/test/DUtilUnitTest/error.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL - -void CALLBACK DutilTestTraceError( - __in_z LPCSTR szFile, - __in int iLine, - __in REPORT_LEVEL rl, - __in UINT source, - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ); diff --git a/src/test/DUtilUnitTest/packages.config b/src/test/DUtilUnitTest/packages.config deleted file mode 100644 index a4fef2bf..00000000 --- a/src/test/DUtilUnitTest/packages.config +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/DUtilUnitTest/precomp.cpp b/src/test/DUtilUnitTest/precomp.cpp deleted file mode 100644 index 37664a1c..00000000 --- a/src/test/DUtilUnitTest/precomp.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" diff --git a/src/test/DUtilUnitTest/precomp.h b/src/test/DUtilUnitTest/precomp.h deleted file mode 100644 index e9f8770b..00000000 --- a/src/test/DUtilUnitTest/precomp.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include -#include - -// Include error.h before dutil.h -#include -#include "error.h" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // NOTE: this must come after atomutil.h and rssutil.h since it uses them. -#include -#include - -#pragma managed -#include diff --git a/src/test/DUtilUnitTest/resource.h b/src/test/DUtilUnitTest/resource.h deleted file mode 100644 index bdf252f6..00000000 --- a/src/test/DUtilUnitTest/resource.h +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - diff --git a/src/version.json b/src/version.json new file mode 100644 index 00000000..5f857771 --- /dev/null +++ b/src/version.json @@ -0,0 +1,11 @@ +{ + "version": "4.0", + "publicReleaseRefSpec": [ + "^refs/heads/master$" + ], + "cloudBuild": { + "buildNumber": { + "enabled": true + } + } +} -- cgit v1.2.3-55-g6feb