diff options
| author | Sean Hall <rseanhall@gmail.com> | 2017-09-03 17:25:02 -0500 |
|---|---|---|
| committer | Sean Hall <rseanhall@gmail.com> | 2017-09-03 18:10:41 -0500 |
| commit | 7df142a4586bcede1442feb38029ff15556ccb46 (patch) | |
| tree | 4a52b55f2f0ff7bd511bd01cf08b67479d64cd76 /src | |
| parent | a3073f2da5160b71dc4b89fd6c4cef1008521aa4 (diff) | |
| download | wix-7df142a4586bcede1442feb38029ff15556ccb46.tar.gz wix-7df142a4586bcede1442feb38029ff15556ccb46.tar.bz2 wix-7df142a4586bcede1442feb38029ff15556ccb46.zip | |
Initialize repo
Diffstat (limited to 'src')
| -rw-r--r-- | src/Cpp.Build.props | 100 | ||||
| -rw-r--r-- | src/Directory.Build.props | 21 | ||||
| -rw-r--r-- | src/NativeMultiTargeting.Build.props | 10 | ||||
| -rw-r--r-- | src/wcautil/build/WixToolset.WcaUtil.props | 23 | ||||
| -rw-r--r-- | src/wcautil/custommsierrors.h | 130 | ||||
| -rw-r--r-- | src/wcautil/exbinary.cpp | 142 | ||||
| -rw-r--r-- | src/wcautil/inc/wcalog.h | 14 | ||||
| -rw-r--r-- | src/wcautil/inc/wcautil.h | 384 | ||||
| -rw-r--r-- | src/wcautil/inc/wcawow64.h | 20 | ||||
| -rw-r--r-- | src/wcautil/inc/wcawrapquery.h | 130 | ||||
| -rw-r--r-- | src/wcautil/packages.config | 5 | ||||
| -rw-r--r-- | src/wcautil/precomp.h | 19 | ||||
| -rw-r--r-- | src/wcautil/qtexec.cpp | 340 | ||||
| -rw-r--r-- | src/wcautil/wcalog.cpp | 251 | ||||
| -rw-r--r-- | src/wcautil/wcascript.cpp | 447 | ||||
| -rw-r--r-- | src/wcautil/wcautil.cpp | 216 | ||||
| -rw-r--r-- | src/wcautil/wcautil.nuspec | 23 | ||||
| -rw-r--r-- | src/wcautil/wcautil.vcxproj | 79 | ||||
| -rw-r--r-- | src/wcautil/wcawow64.cpp | 169 | ||||
| -rw-r--r-- | src/wcautil/wcawrap.cpp | 1643 | ||||
| -rw-r--r-- | src/wcautil/wcawrapquery.cpp | 717 |
21 files changed, 4883 insertions, 0 deletions
diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props new file mode 100644 index 00000000..1e4d4cbc --- /dev/null +++ b/src/Cpp.Build.props | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 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. --> | ||
| 3 | |||
| 4 | <Project> | ||
| 5 | <PropertyGroup> | ||
| 6 | <BaseOutputPath>$(OutputPath)</BaseOutputPath> | ||
| 7 | <IntDir>$(BaseIntermediateOutputPath)$(Platform)\</IntDir> | ||
| 8 | <OutDir>$(OutputPath)$(Platform)\</OutDir> | ||
| 9 | </PropertyGroup> | ||
| 10 | |||
| 11 | <ItemDefinitionGroup> | ||
| 12 | <ClCompile> | ||
| 13 | <DisableSpecificWarnings>$(DisableSpecificCompilerWarnings)</DisableSpecificWarnings> | ||
| 14 | <WarningLevel>Level4</WarningLevel> | ||
| 15 | <AdditionalIncludeDirectories>$(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||
| 16 | <PreprocessorDefinitions>WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> | ||
| 17 | <PrecompiledHeader>Use</PrecompiledHeader> | ||
| 18 | <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile> | ||
| 19 | <CallingConvention>StdCall</CallingConvention> | ||
| 20 | <TreatWarningAsError>true</TreatWarningAsError> | ||
| 21 | <ExceptionHandling>false</ExceptionHandling> | ||
| 22 | <AdditionalOptions>-YlprecompDefine</AdditionalOptions> | ||
| 23 | <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/Zc:threadSafeInit- %(AdditionalOptions)</AdditionalOptions> | ||
| 24 | <MultiProcessorCompilation Condition=" $(NUMBER_OF_PROCESSORS) > 4 ">true</MultiProcessorCompilation> | ||
| 25 | </ClCompile> | ||
| 26 | <ResourceCompile> | ||
| 27 | <PreprocessorDefinitions>$(ArmPreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions> | ||
| 28 | <AdditionalIncludeDirectories>$(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||
| 29 | </ResourceCompile> | ||
| 30 | <Lib> | ||
| 31 | <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||
| 32 | </Lib> | ||
| 33 | <Link> | ||
| 34 | <SubSystem>$(ProjectSubSystem)</SubSystem> | ||
| 35 | <ModuleDefinitionFile>$(ProjectModuleDefinitionFile)</ModuleDefinitionFile> | ||
| 36 | <NoEntryPoint>$(ResourceOnlyDll)</NoEntryPoint> | ||
| 37 | <GenerateDebugInformation>true</GenerateDebugInformation> | ||
| 38 | <AdditionalDependencies>$(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||
| 39 | <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||
| 40 | <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/IGNORE:4099 %(AdditionalOptions)</AdditionalOptions> | ||
| 41 | </Link> | ||
| 42 | </ItemDefinitionGroup> | ||
| 43 | |||
| 44 | <ItemDefinitionGroup Condition=" '$(Platform)'=='Win32' and '$(PlatformToolset)'!='v100'"> | ||
| 45 | <ClCompile> | ||
| 46 | <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet> | ||
| 47 | </ClCompile> | ||
| 48 | </ItemDefinitionGroup> | ||
| 49 | <ItemDefinitionGroup Condition=" '$(Platform)'=='arm' "> | ||
| 50 | <ClCompile> | ||
| 51 | <CallingConvention>CDecl</CallingConvention> | ||
| 52 | </ClCompile> | ||
| 53 | </ItemDefinitionGroup> | ||
| 54 | <ItemDefinitionGroup Condition=" '$(ConfigurationType)'=='StaticLibrary' "> | ||
| 55 | <ClCompile> | ||
| 56 | <DebugInformationFormat>OldStyle</DebugInformationFormat> | ||
| 57 | <OmitDefaultLibName>true</OmitDefaultLibName> | ||
| 58 | <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries> | ||
| 59 | </ClCompile> | ||
| 60 | </ItemDefinitionGroup> | ||
| 61 | <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' "> | ||
| 62 | <ClCompile> | ||
| 63 | <Optimization>Disabled</Optimization> | ||
| 64 | <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> | ||
| 65 | <PreprocessorDefinitions>_DEBUG;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> | ||
| 66 | <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> | ||
| 67 | </ClCompile> | ||
| 68 | </ItemDefinitionGroup> | ||
| 69 | <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' and '$(CLRSupport)'=='true' "> | ||
| 70 | <ClCompile> | ||
| 71 | <BasicRuntimeChecks></BasicRuntimeChecks> | ||
| 72 | <RuntimeLibrary>MultiThreadedDebugDll</RuntimeLibrary> | ||
| 73 | </ClCompile> | ||
| 74 | </ItemDefinitionGroup> | ||
| 75 | <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' "> | ||
| 76 | <ClCompile> | ||
| 77 | <Optimization>MinSpace</Optimization> | ||
| 78 | <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> | ||
| 79 | <FunctionLevelLinking>true</FunctionLevelLinking> | ||
| 80 | <IntrinsicFunctions>true</IntrinsicFunctions> | ||
| 81 | <RuntimeLibrary>MultiThreaded</RuntimeLibrary> | ||
| 82 | </ClCompile> | ||
| 83 | <Link> | ||
| 84 | <EnableCOMDATFolding>true</EnableCOMDATFolding> | ||
| 85 | <OptimizeReferences>true</OptimizeReferences> | ||
| 86 | </Link> | ||
| 87 | </ItemDefinitionGroup> | ||
| 88 | <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' and '$(CLRSupport)'=='true' "> | ||
| 89 | <ClCompile> | ||
| 90 | <BasicRuntimeChecks></BasicRuntimeChecks> | ||
| 91 | <RuntimeLibrary>MultiThreadedDll</RuntimeLibrary> | ||
| 92 | </ClCompile> | ||
| 93 | </ItemDefinitionGroup> | ||
| 94 | <ItemDefinitionGroup Condition=" '$(CLRSupport)'=='true' "> | ||
| 95 | <Link> | ||
| 96 | <KeyFile>$(LinkKeyFile)</KeyFile> | ||
| 97 | <DelaySign>$(LinkDelaySign)</DelaySign> | ||
| 98 | </Link> | ||
| 99 | </ItemDefinitionGroup> | ||
| 100 | </Project> | ||
diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 00000000..48ba462d --- /dev/null +++ b/src/Directory.Build.props | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 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. --> | ||
| 3 | |||
| 4 | <Project> | ||
| 5 | <PropertyGroup> | ||
| 6 | <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||
| 7 | <BaseIntermediateOutputPath>$(MSBuildThisFileDirectory)..\build\obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath> | ||
| 8 | <OutputPath>$(MSBuildThisFileDirectory)..\build\$(Configuration)\</OutputPath> | ||
| 9 | |||
| 10 | <Authors>WiX Toolset Team</Authors> | ||
| 11 | <Company>WiX Toolset</Company> | ||
| 12 | <Copyright>Copyright (c) .NET Foundation and contributors. All rights reserved.</Copyright> | ||
| 13 | </PropertyGroup> | ||
| 14 | |||
| 15 | <PropertyGroup> | ||
| 16 | <WixToolsetRootFolder>$(MSBuildThisFileDirectory)..\..\</WixToolsetRootFolder> | ||
| 17 | </PropertyGroup> | ||
| 18 | |||
| 19 | <Import Project="Cpp.Build.props" Condition=" '$(MSBuildProjectExtension)'=='.vcxproj' " /> | ||
| 20 | <Import Project="Custom.Build.props" Condition=" Exists('Custom.Build.props') " /> | ||
| 21 | </Project> | ||
diff --git a/src/NativeMultiTargeting.Build.props b/src/NativeMultiTargeting.Build.props new file mode 100644 index 00000000..98b1933e --- /dev/null +++ b/src/NativeMultiTargeting.Build.props | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 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. --> | ||
| 3 | |||
| 4 | <Project> | ||
| 5 | <!-- Overrides the standard Cpp.Build.props to include the PlatformToolset in the output path. --> | ||
| 6 | <PropertyGroup> | ||
| 7 | <IntDir>$(BaseIntermediateOutputPath)$(PlatformToolset)\$(PlatformTarget)\</IntDir> | ||
| 8 | <OutDir>$(OutputPath)$(PlatformToolset)\$(PlatformTarget)\</OutDir> | ||
| 9 | </PropertyGroup> | ||
| 10 | </Project> | ||
diff --git a/src/wcautil/build/WixToolset.WcaUtil.props b/src/wcautil/build/WixToolset.WcaUtil.props new file mode 100644 index 00000000..71a9743e --- /dev/null +++ b/src/wcautil/build/WixToolset.WcaUtil.props | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 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. --> | ||
| 3 | |||
| 4 | <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
| 5 | <ItemDefinitionGroup> | ||
| 6 | <ClCompile> | ||
| 7 | <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||
| 8 | </ClCompile> | ||
| 9 | <ResourceCompile> | ||
| 10 | <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||
| 11 | </ResourceCompile> | ||
| 12 | </ItemDefinitionGroup> | ||
| 13 | <ItemDefinitionGroup Condition=" $(PlatformToolset.ToLower().StartsWith('v140')) "> | ||
| 14 | <Link> | ||
| 15 | <AdditionalDependencies>$(MSBuildThisFileDirectory)native\lib\v140\$(PlatformTarget)\wcautil.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||
| 16 | </Link> | ||
| 17 | </ItemDefinitionGroup> | ||
| 18 | <ItemDefinitionGroup Condition=" $(PlatformToolset.ToLower().StartsWith('v141')) "> | ||
| 19 | <Link> | ||
| 20 | <AdditionalDependencies>$(MSBuildThisFileDirectory)native\lib\v141\$(PlatformTarget)\wcautil.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||
| 21 | </Link> | ||
| 22 | </ItemDefinitionGroup> | ||
| 23 | </Project> | ||
diff --git a/src/wcautil/custommsierrors.h b/src/wcautil/custommsierrors.h new file mode 100644 index 00000000..f149fb31 --- /dev/null +++ b/src/wcautil/custommsierrors.h | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | #pragma once | ||
| 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. | ||
| 3 | |||
| 4 | |||
| 5 | #define GLOBAL_ERROR_BASE 25501 | ||
| 6 | |||
| 7 | #define msierrSecureObjectsFailedCreateSD 25520 | ||
| 8 | #define msierrSecureObjectsFailedSet 25521 | ||
| 9 | #define msierrSecureObjectsUnknownType 25522 | ||
| 10 | |||
| 11 | #define msierrXmlFileFailedRead 25530 | ||
| 12 | #define msierrXmlFileFailedOpen 25531 | ||
| 13 | #define msierrXmlFileFailedSelect 25532 | ||
| 14 | #define msierrXmlFileFailedSave 25533 | ||
| 15 | |||
| 16 | #define msierrXmlConfigFailedRead 25540 | ||
| 17 | #define msierrXmlConfigFailedOpen 25541 | ||
| 18 | #define msierrXmlConfigFailedSelect 25542 | ||
| 19 | #define msierrXmlConfigFailedSave 25543 | ||
| 20 | |||
| 21 | #define msierrFirewallCannotConnect 25580 | ||
| 22 | |||
| 23 | //--------------------------------------------------------------------------- | ||
| 24 | // Server CustomAction Errors | ||
| 25 | // SERVER range: 26001-26100 | ||
| 26 | #define SERVER_ERROR_BASE 26000 | ||
| 27 | |||
| 28 | #define msierrIISCannotConnect 26001 | ||
| 29 | #define msierrIISFailedReadWebSite 26002 | ||
| 30 | #define msierrIISFailedReadWebDirs 26003 | ||
| 31 | #define msierrIISFailedReadVDirs 26004 | ||
| 32 | #define msierrIISFailedReadFilters 26005 | ||
| 33 | #define msierrIISFailedReadAppPool 26006 | ||
| 34 | #define msierrIISFailedReadMimeMap 26007 | ||
| 35 | #define msierrIISFailedReadProp 26008 | ||
| 36 | #define msierrIISFailedReadWebSvcExt 26009 | ||
| 37 | #define msierrIISFailedReadWebError 26010 | ||
| 38 | #define msierrIISFailedReadHttpHeader 26011 | ||
| 39 | |||
| 40 | #define msierrIISFailedSchedTransaction 26031 | ||
| 41 | #define msierrIISFailedSchedInstallWebs 26032 | ||
| 42 | #define msierrIISFailedSchedInstallWebDirs 26033 | ||
| 43 | #define msierrIISFailedSchedInstallVDirs 26034 | ||
| 44 | #define msierrIISFailedSchedInstallFilters 26035 | ||
| 45 | #define msierrIISFailedSchedInstallAppPool 26036 | ||
| 46 | #define msierrIISFailedSchedInstallProp 26037 | ||
| 47 | #define msierrIISFailedSchedInstallWebSvcExt 26038 | ||
| 48 | |||
| 49 | #define msierrIISFailedSchedUninstallWebs 26051 | ||
| 50 | #define msierrIISFailedSchedUninstallWebDirs 26052 | ||
| 51 | #define msierrIISFailedSchedUninstallVDirs 26053 | ||
| 52 | #define msierrIISFailedSchedUninstallFilters 26054 | ||
| 53 | #define msierrIISFailedSchedUninstallAppPool 26055 | ||
| 54 | #define msierrIISFailedSchedUninstallProp 26056 | ||
| 55 | #define msierrIISFailedSchedUninstallWebSvcExt 26057 | ||
| 56 | |||
| 57 | #define msierrIISFailedStartTransaction 26101 | ||
| 58 | #define msierrIISFailedOpenKey 26102 | ||
| 59 | #define msierrIISFailedCreateKey 26103 | ||
| 60 | #define msierrIISFailedWriteData 26104 | ||
| 61 | #define msierrIISFailedCreateApp 26105 | ||
| 62 | #define msierrIISFailedDeleteKey 26106 | ||
| 63 | #define msierrIISFailedDeleteApp 26107 | ||
| 64 | #define msierrIISFailedDeleteValue 26108 | ||
| 65 | #define msierrIISFailedCommitInUse 26109 | ||
| 66 | |||
| 67 | #define msierrSQLFailedCreateDatabase 26201 | ||
| 68 | #define msierrSQLFailedDropDatabase 26202 | ||
| 69 | #define msierrSQLFailedConnectDatabase 26203 | ||
| 70 | #define msierrSQLFailedExecString 26204 | ||
| 71 | #define msierrSQLDatabaseAlreadyExists 26205 | ||
| 72 | |||
| 73 | #define msierrPERFMONFailedRegisterDLL 26251 | ||
| 74 | #define msierrPERFMONFailedUnregisterDLL 26252 | ||
| 75 | #define msierrInstallPerfCounterData 26253 | ||
| 76 | #define msierrUninstallPerfCounterData 26254 | ||
| 77 | |||
| 78 | #define msierrSMBFailedCreate 26301 | ||
| 79 | #define msierrSMBFailedDrop 26302 | ||
| 80 | |||
| 81 | #define msierrCERTFailedOpen 26351 | ||
| 82 | #define msierrCERTFailedAdd 26352 | ||
| 83 | |||
| 84 | #define msierrUSRFailedUserCreate 26401 | ||
| 85 | #define msierrUSRFailedUserCreatePswd 26402 | ||
| 86 | #define msierrUSRFailedUserGroupAdd 26403 | ||
| 87 | #define msierrUSRFailedUserCreateExists 26404 | ||
| 88 | #define msierrUSRFailedGrantLogonAsService 26405 | ||
| 89 | |||
| 90 | #define msierrDependencyMissingDependencies 26451 | ||
| 91 | #define msierrDependencyHasDependents 26452 | ||
| 92 | |||
| 93 | //-------------------------------------------------------------------------- | ||
| 94 | // Managed code CustomAction Errors | ||
| 95 | // MANAGED range: 27000-27100 | ||
| 96 | #define MANAGED_ERROR_BASE 27000 | ||
| 97 | |||
| 98 | #define msierrDotNetRuntimeRequired 27000 | ||
| 99 | //--------------------------------------------------------------------------- | ||
| 100 | // Public CustomAction Errors | ||
| 101 | // PUBLIC range: 28001-28100 | ||
| 102 | #define PUBLIC_ERROR_BASE 28000 | ||
| 103 | |||
| 104 | #define msierrComPlusCannotConnect 28001 | ||
| 105 | #define msierrComPlusPartitionReadFailed 28002 | ||
| 106 | #define msierrComPlusPartitionRoleReadFailed 28003 | ||
| 107 | #define msierrComPlusUserInPartitionRoleReadFailed 28004 | ||
| 108 | #define msierrComPlusPartitionUserReadFailed 28005 | ||
| 109 | #define msierrComPlusApplicationReadFailed 28006 | ||
| 110 | #define msierrComPlusApplicationRoleReadFailed 28007 | ||
| 111 | #define msierrComPlusUserInApplicationRoleReadFailed 28008 | ||
| 112 | #define msierrComPlusAssembliesReadFailed 28009 | ||
| 113 | #define msierrComPlusSubscriptionReadFailed 28010 | ||
| 114 | #define msierrComPlusPartitionDependency 28011 | ||
| 115 | #define msierrComPlusPartitionNotFound 28012 | ||
| 116 | #define msierrComPlusPartitionIdConflict 28013 | ||
| 117 | #define msierrComPlusPartitionNameConflict 28014 | ||
| 118 | #define msierrComPlusApplicationDependency 28015 | ||
| 119 | #define msierrComPlusApplicationNotFound 28016 | ||
| 120 | #define msierrComPlusApplicationIdConflict 28017 | ||
| 121 | #define msierrComPlusApplicationNameConflict 28018 | ||
| 122 | #define msierrComPlusApplicationRoleDependency 28019 | ||
| 123 | #define msierrComPlusApplicationRoleNotFound 28020 | ||
| 124 | #define msierrComPlusApplicationRoleConflict 28021 | ||
| 125 | #define msierrComPlusAssemblyDependency 28022 | ||
| 126 | #define msierrComPlusSubscriptionIdConflict 28023 | ||
| 127 | #define msierrComPlusSubscriptionNameConflict 28024 | ||
| 128 | #define msierrComPlusFailedLookupNames 28025 | ||
| 129 | |||
| 130 | #define msierrMsmqCannotConnect 28101 | ||
diff --git a/src/wcautil/exbinary.cpp b/src/wcautil/exbinary.cpp new file mode 100644 index 00000000..5ff24212 --- /dev/null +++ b/src/wcautil/exbinary.cpp | |||
| @@ -0,0 +1,142 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | #include "precomp.h" | ||
| 4 | |||
| 5 | // | ||
| 6 | // Extracts the data from the Binary table row with the given ID into a buffer. | ||
| 7 | // | ||
| 8 | HRESULT WIXAPI WcaExtractBinaryToBuffer( | ||
| 9 | __in LPCWSTR wzBinaryId, | ||
| 10 | __out BYTE** pbData, | ||
| 11 | __out DWORD* pcbData | ||
| 12 | ) | ||
| 13 | { | ||
| 14 | HRESULT hr = S_OK; | ||
| 15 | LPWSTR pwzSql = NULL; | ||
| 16 | PMSIHANDLE hView; | ||
| 17 | PMSIHANDLE hRec; | ||
| 18 | |||
| 19 | // make sure we're not horked from the get-go | ||
| 20 | hr = WcaTableExists(L"Binary"); | ||
| 21 | if (S_OK != hr) | ||
| 22 | { | ||
| 23 | if (SUCCEEDED(hr)) | ||
| 24 | { | ||
| 25 | hr = E_UNEXPECTED; | ||
| 26 | } | ||
| 27 | ExitOnFailure(hr, "There is no Binary table."); | ||
| 28 | } | ||
| 29 | |||
| 30 | ExitOnNull(wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be null"); | ||
| 31 | ExitOnNull(*wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be empty string"); | ||
| 32 | |||
| 33 | hr = StrAllocFormatted(&pwzSql, L"SELECT `Data` FROM `Binary` WHERE `Name`=\'%ls\'", wzBinaryId); | ||
| 34 | ExitOnFailure(hr, "Failed to allocate Binary table query."); | ||
| 35 | |||
| 36 | hr = WcaOpenExecuteView(pwzSql, &hView); | ||
| 37 | ExitOnFailure(hr, "Failed to open view on Binary table"); | ||
| 38 | |||
| 39 | hr = WcaFetchSingleRecord(hView, &hRec); | ||
| 40 | ExitOnFailure(hr, "Failed to retrieve request from Binary table"); | ||
| 41 | |||
| 42 | hr = WcaGetRecordStream(hRec, 1, pbData, pcbData); | ||
| 43 | ExitOnFailure(hr, "Failed to read Binary.Data."); | ||
| 44 | |||
| 45 | LExit: | ||
| 46 | ReleaseStr(pwzSql); | ||
| 47 | |||
| 48 | return hr; | ||
| 49 | } | ||
| 50 | |||
| 51 | // | ||
| 52 | // Extracts the data from the Binary table row with the given ID into a file. | ||
| 53 | // | ||
| 54 | HRESULT WIXAPI WcaExtractBinaryToFile( | ||
| 55 | __in LPCWSTR wzBinaryId, | ||
| 56 | __in LPCWSTR wzPath | ||
| 57 | ) | ||
| 58 | { | ||
| 59 | HRESULT hr = S_OK; | ||
| 60 | BYTE* pbData = NULL; | ||
| 61 | DWORD cbData = 0; | ||
| 62 | HANDLE hFile = INVALID_HANDLE_VALUE; | ||
| 63 | |||
| 64 | // grab the bits | ||
| 65 | hr = WcaExtractBinaryToBuffer(wzBinaryId, &pbData, &cbData); | ||
| 66 | ExitOnFailure(hr, "Failed to extract binary data: %ls", wzBinaryId); | ||
| 67 | |||
| 68 | // write 'em to the file | ||
| 69 | hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | ||
| 70 | if (INVALID_HANDLE_VALUE == hFile) | ||
| 71 | { | ||
| 72 | ExitWithLastError(hr, "Failed to create file: %ls", wzPath); | ||
| 73 | } | ||
| 74 | |||
| 75 | DWORD cbWritten = 0; | ||
| 76 | if (!::WriteFile(hFile, pbData, cbData, &cbWritten, NULL)) | ||
| 77 | { | ||
| 78 | ExitWithLastError(hr, "Failed to write data to file: %ls", wzPath); | ||
| 79 | } | ||
| 80 | |||
| 81 | LExit: | ||
| 82 | ReleaseFile(hFile); | ||
| 83 | ReleaseMem(pbData); | ||
| 84 | |||
| 85 | return hr; | ||
| 86 | } | ||
| 87 | |||
| 88 | // | ||
| 89 | // Extracts the data from the Binary table row with the given ID into a string. | ||
| 90 | // | ||
| 91 | HRESULT WIXAPI WcaExtractBinaryToString( | ||
| 92 | __in LPCWSTR wzBinaryId, | ||
| 93 | __deref_out_z LPWSTR* psczOutput, | ||
| 94 | __out WCA_ENCODING* encoding | ||
| 95 | ) | ||
| 96 | { | ||
| 97 | HRESULT hr = S_OK; | ||
| 98 | BYTE* pbData = NULL; | ||
| 99 | DWORD cbData = 0; | ||
| 100 | |||
| 101 | // grab the bits | ||
| 102 | hr = WcaExtractBinaryToBuffer(wzBinaryId, &pbData, &cbData); | ||
| 103 | ExitOnFailure(hr, "Failed to extract binary data: %ls", wzBinaryId); | ||
| 104 | |||
| 105 | // expand by a NULL character (or two) to make sure the buffer is null-terminated | ||
| 106 | cbData += 2; | ||
| 107 | pbData = reinterpret_cast<LPBYTE>(MemReAlloc(pbData, cbData, TRUE)); | ||
| 108 | ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to expand binary buffer"); | ||
| 109 | |||
| 110 | // Check for BOMs. | ||
| 111 | if (2 < cbData) | ||
| 112 | { | ||
| 113 | if ((0xFF == *pbData) && (0xFE == *(pbData + 1))) | ||
| 114 | { | ||
| 115 | *encoding = WCA_ENCODING_UTF_16; | ||
| 116 | hr = StrAllocString(psczOutput, reinterpret_cast<LPWSTR>(pbData), 0); | ||
| 117 | } | ||
| 118 | else if ((0xEF == *pbData) && (0xBB == *(pbData + 1)) && (0xBF == *(pbData + 2))) | ||
| 119 | { | ||
| 120 | *encoding = WCA_ENCODING_UTF_8; | ||
| 121 | hr = StrAllocStringAnsi(psczOutput, reinterpret_cast<LPCSTR>(pbData), 0, CP_UTF8); | ||
| 122 | } | ||
| 123 | else | ||
| 124 | { | ||
| 125 | *encoding = WCA_ENCODING_ANSI; | ||
| 126 | hr = StrAllocStringAnsi(psczOutput, reinterpret_cast<LPCSTR>(pbData), 0, CP_ACP); | ||
| 127 | } | ||
| 128 | ExitOnFailure(hr, "Failed to allocate string for binary buffer."); | ||
| 129 | } | ||
| 130 | |||
| 131 | // Free the byte buffer since it has been converted to a new UNICODE string, one way or another. | ||
| 132 | if (pbData) | ||
| 133 | { | ||
| 134 | WcaFreeStream(pbData); | ||
| 135 | pbData = NULL; | ||
| 136 | } | ||
| 137 | |||
| 138 | LExit: | ||
| 139 | ReleaseMem(pbData); | ||
| 140 | |||
| 141 | return hr; | ||
| 142 | } | ||
diff --git a/src/wcautil/inc/wcalog.h b/src/wcautil/inc/wcalog.h new file mode 100644 index 00000000..ffa3fa03 --- /dev/null +++ b/src/wcautil/inc/wcalog.h | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | #pragma once | ||
| 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. | ||
| 3 | |||
| 4 | |||
| 5 | #ifdef __cplusplus | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | BOOL WIXAPI IsVerboseLogging(); | ||
| 10 | HRESULT WIXAPI SetVerboseLoggingAtom(BOOL bValue); | ||
| 11 | |||
| 12 | #ifdef __cplusplus | ||
| 13 | } | ||
| 14 | #endif | ||
diff --git a/src/wcautil/inc/wcautil.h b/src/wcautil/inc/wcautil.h new file mode 100644 index 00000000..8139a7ca --- /dev/null +++ b/src/wcautil/inc/wcautil.h | |||
| @@ -0,0 +1,384 @@ | |||
| 1 | #pragma once | ||
| 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. | ||
| 3 | |||
| 4 | |||
| 5 | #ifdef __cplusplus | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | #define WIXAPI __stdcall | ||
| 10 | #define ExitTrace WcaLogError | ||
| 11 | |||
| 12 | #include "dutil.h" | ||
| 13 | |||
| 14 | #define MessageExitOnLastError(x, e, s, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { ExitTrace(x, "%s", s, __VA_ARGS__); WcaErrorMessage(e, x, MB_OK, -1, __VA_ARGS__); goto LExit; } } | ||
| 15 | #define MessageExitOnFailure(x, e, s, ...) if (FAILED(x)) { ExitTrace(x, "%s", s, __VA_ARGS__); WcaErrorMessage(e, x, INSTALLMESSAGE_ERROR | MB_OK, -1, __VA_ARGS__); goto LExit; } | ||
| 16 | #define MessageExitOnNullWithLastError(p, x, e, s, ...) if (NULL == p) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (!FAILED(x)) { x = E_FAIL; } ExitTrace(x, "%s", s, __VA_ARGS__); WcaErrorMessage(e, x, MB_OK, -1, __VA_ARGS__); goto LExit; } | ||
| 17 | |||
| 18 | // Generic action enum. | ||
| 19 | typedef enum WCA_ACTION | ||
| 20 | { | ||
| 21 | WCA_ACTION_NONE, | ||
| 22 | WCA_ACTION_INSTALL, | ||
| 23 | WCA_ACTION_UNINSTALL, | ||
| 24 | } WCA_ACTION; | ||
| 25 | |||
| 26 | typedef enum WCA_CASCRIPT | ||
| 27 | { | ||
| 28 | WCA_CASCRIPT_SCHEDULED, | ||
| 29 | WCA_CASCRIPT_ROLLBACK, | ||
| 30 | } WCA_CASCRIPT; | ||
| 31 | |||
| 32 | typedef enum WCA_CASCRIPT_CLOSE | ||
| 33 | { | ||
| 34 | WCA_CASCRIPT_CLOSE_PRESERVE, | ||
| 35 | WCA_CASCRIPT_CLOSE_DELETE, | ||
| 36 | } WCA_CASCRIPT_CLOSE; | ||
| 37 | |||
| 38 | typedef enum WCA_TODO | ||
| 39 | { | ||
| 40 | WCA_TODO_UNKNOWN, | ||
| 41 | WCA_TODO_INSTALL, | ||
| 42 | WCA_TODO_UNINSTALL, | ||
| 43 | WCA_TODO_REINSTALL, | ||
| 44 | } WCA_TODO; | ||
| 45 | |||
| 46 | typedef struct WCA_CASCRIPT_STRUCT | ||
| 47 | { | ||
| 48 | LPWSTR pwzScriptPath; | ||
| 49 | HANDLE hScriptFile; | ||
| 50 | } *WCA_CASCRIPT_HANDLE; | ||
| 51 | |||
| 52 | typedef enum WCA_ENCODING | ||
| 53 | { | ||
| 54 | WCA_ENCODING_UNKNOWN, | ||
| 55 | WCA_ENCODING_UTF_16, | ||
| 56 | WCA_ENCODING_UTF_8, | ||
| 57 | WCA_ENCODING_ANSI, | ||
| 58 | } WCA_ENCODING; | ||
| 59 | |||
| 60 | void WIXAPI WcaGlobalInitialize( | ||
| 61 | __in HINSTANCE hInst | ||
| 62 | ); | ||
| 63 | void WIXAPI WcaGlobalFinalize(); | ||
| 64 | |||
| 65 | HRESULT WIXAPI WcaInitialize( | ||
| 66 | __in MSIHANDLE hInstall, | ||
| 67 | __in_z PCSTR szCustomActionLogName | ||
| 68 | ); | ||
| 69 | UINT WIXAPI WcaFinalize( | ||
| 70 | __in UINT iReturnValue | ||
| 71 | ); | ||
| 72 | BOOL WIXAPI WcaIsInitialized(); | ||
| 73 | |||
| 74 | MSIHANDLE WIXAPI WcaGetInstallHandle(); | ||
| 75 | MSIHANDLE WIXAPI WcaGetDatabaseHandle(); | ||
| 76 | |||
| 77 | const char* WIXAPI WcaGetLogName(); | ||
| 78 | |||
| 79 | void WIXAPI WcaSetReturnValue( | ||
| 80 | __in UINT iReturnValue | ||
| 81 | ); | ||
| 82 | BOOL WIXAPI WcaCancelDetected(); | ||
| 83 | |||
| 84 | #define LOG_BUFFER 2048 | ||
| 85 | typedef enum LOGLEVEL | ||
| 86 | { | ||
| 87 | LOGMSG_TRACEONLY, // Never written to the log file (except in DEBUG builds) | ||
| 88 | LOGMSG_VERBOSE, // Written to log when LOGVERBOSE | ||
| 89 | LOGMSG_STANDARD // Written to log whenever informational logging is enabled | ||
| 90 | } LOGLEVEL; | ||
| 91 | |||
| 92 | void __cdecl WcaLog( | ||
| 93 | __in LOGLEVEL llv, | ||
| 94 | __in_z __format_string PCSTR fmt, ... | ||
| 95 | ); | ||
| 96 | BOOL WIXAPI WcaDisplayAssert( | ||
| 97 | __in LPCSTR sz | ||
| 98 | ); | ||
| 99 | void __cdecl WcaLogError( | ||
| 100 | __in HRESULT hr, | ||
| 101 | __in LPCSTR szMessage, | ||
| 102 | ... | ||
| 103 | ); | ||
| 104 | |||
| 105 | UINT WIXAPI WcaProcessMessage( | ||
| 106 | __in INSTALLMESSAGE eMessageType, | ||
| 107 | __in MSIHANDLE hRecord | ||
| 108 | ); | ||
| 109 | UINT __cdecl WcaErrorMessage( | ||
| 110 | __in int iError, | ||
| 111 | __in HRESULT hrError, | ||
| 112 | __in UINT uiType, | ||
| 113 | __in INT cArgs, | ||
| 114 | ... | ||
| 115 | ); | ||
| 116 | HRESULT WIXAPI WcaProgressMessage( | ||
| 117 | __in UINT uiCost, | ||
| 118 | __in BOOL fExtendProgressBar | ||
| 119 | ); | ||
| 120 | |||
| 121 | BOOL WIXAPI WcaIsInstalling( | ||
| 122 | __in INSTALLSTATE isInstalled, | ||
| 123 | __in INSTALLSTATE isAction | ||
| 124 | ); | ||
| 125 | BOOL WIXAPI WcaIsReInstalling( | ||
| 126 | __in INSTALLSTATE isInstalled, | ||
| 127 | __in INSTALLSTATE isAction | ||
| 128 | ); | ||
| 129 | BOOL WIXAPI WcaIsUninstalling( | ||
| 130 | __in INSTALLSTATE isInstalled, | ||
| 131 | __in INSTALLSTATE isAction | ||
| 132 | ); | ||
| 133 | |||
| 134 | HRESULT WIXAPI WcaSetComponentState( | ||
| 135 | __in_z LPCWSTR wzComponent, | ||
| 136 | __in INSTALLSTATE isState | ||
| 137 | ); | ||
| 138 | |||
| 139 | HRESULT WIXAPI WcaTableExists( | ||
| 140 | __in_z LPCWSTR wzTable | ||
| 141 | ); | ||
| 142 | |||
| 143 | HRESULT WIXAPI WcaOpenView( | ||
| 144 | __in_z LPCWSTR wzSql, | ||
| 145 | __out MSIHANDLE* phView | ||
| 146 | ); | ||
| 147 | HRESULT WIXAPI WcaExecuteView( | ||
| 148 | __in MSIHANDLE hView, | ||
| 149 | __in MSIHANDLE hRec | ||
| 150 | ); | ||
| 151 | HRESULT WIXAPI WcaOpenExecuteView( | ||
| 152 | __in_z LPCWSTR wzSql, | ||
| 153 | __out MSIHANDLE* phView | ||
| 154 | ); | ||
| 155 | HRESULT WIXAPI WcaFetchRecord( | ||
| 156 | __in MSIHANDLE hView, | ||
| 157 | __out MSIHANDLE* phRec | ||
| 158 | ); | ||
| 159 | HRESULT WIXAPI WcaFetchSingleRecord( | ||
| 160 | __in MSIHANDLE hView, | ||
| 161 | __out MSIHANDLE* phRec | ||
| 162 | ); | ||
| 163 | |||
| 164 | HRESULT WIXAPI WcaGetProperty( | ||
| 165 | __in_z LPCWSTR wzProperty, | ||
| 166 | __inout LPWSTR* ppwzData | ||
| 167 | ); | ||
| 168 | HRESULT WIXAPI WcaGetFormattedProperty( | ||
| 169 | __in_z LPCWSTR wzProperty, | ||
| 170 | __out LPWSTR* ppwzData | ||
| 171 | ); | ||
| 172 | HRESULT WIXAPI WcaGetFormattedString( | ||
| 173 | __in_z LPCWSTR wzString, | ||
| 174 | __out LPWSTR* ppwzData | ||
| 175 | ); | ||
| 176 | HRESULT WIXAPI WcaGetIntProperty( | ||
| 177 | __in_z LPCWSTR wzProperty, | ||
| 178 | __inout int* piData | ||
| 179 | ); | ||
| 180 | HRESULT WIXAPI WcaGetTargetPath( | ||
| 181 | __in_z LPCWSTR wzFolder, | ||
| 182 | __out LPWSTR* ppwzData | ||
| 183 | ); | ||
| 184 | HRESULT WIXAPI WcaSetProperty( | ||
| 185 | __in_z LPCWSTR wzPropertyName, | ||
| 186 | __in_z LPCWSTR wzPropertyValue | ||
| 187 | ); | ||
| 188 | HRESULT WIXAPI WcaSetIntProperty( | ||
| 189 | __in_z LPCWSTR wzPropertyName, | ||
| 190 | __in int nPropertyValue | ||
| 191 | ); | ||
| 192 | BOOL WIXAPI WcaIsPropertySet( | ||
| 193 | __in LPCSTR szProperty | ||
| 194 | ); | ||
| 195 | BOOL WIXAPI WcaIsUnicodePropertySet( | ||
| 196 | __in LPCWSTR wzProperty | ||
| 197 | ); | ||
| 198 | |||
| 199 | HRESULT WIXAPI WcaGetRecordInteger( | ||
| 200 | __in MSIHANDLE hRec, | ||
| 201 | __in UINT uiField, | ||
| 202 | __inout int* piData | ||
| 203 | ); | ||
| 204 | HRESULT WIXAPI WcaGetRecordString( | ||
| 205 | __in MSIHANDLE hRec, | ||
| 206 | __in UINT uiField, | ||
| 207 | __inout LPWSTR* ppwzData | ||
| 208 | ); | ||
| 209 | HRESULT WIXAPI WcaGetRecordFormattedInteger( | ||
| 210 | __in MSIHANDLE hRec, | ||
| 211 | __in UINT uiField, | ||
| 212 | __out int* piData | ||
| 213 | ); | ||
| 214 | HRESULT WIXAPI WcaGetRecordFormattedString( | ||
| 215 | __in MSIHANDLE hRec, | ||
| 216 | __in UINT uiField, | ||
| 217 | __inout LPWSTR* ppwzData | ||
| 218 | ); | ||
| 219 | |||
| 220 | HRESULT WIXAPI WcaAllocStream( | ||
| 221 | __deref_out_bcount_part(cbData, 0) BYTE** ppbData, | ||
| 222 | __in DWORD cbData | ||
| 223 | ); | ||
| 224 | HRESULT WIXAPI WcaFreeStream( | ||
| 225 | __in BYTE* pbData | ||
| 226 | ); | ||
| 227 | |||
| 228 | HRESULT WIXAPI WcaGetRecordStream( | ||
| 229 | __in MSIHANDLE hRecBinary, | ||
| 230 | __in UINT uiField, | ||
| 231 | __deref_out_bcount_full(*pcbData) BYTE** ppbData, | ||
| 232 | __out DWORD* pcbData | ||
| 233 | ); | ||
| 234 | HRESULT WIXAPI WcaSetRecordString( | ||
| 235 | __in MSIHANDLE hRec, | ||
| 236 | __in UINT uiField, | ||
| 237 | __in_z LPCWSTR wzData | ||
| 238 | ); | ||
| 239 | HRESULT WIXAPI WcaSetRecordInteger( | ||
| 240 | __in MSIHANDLE hRec, | ||
| 241 | __in UINT uiField, | ||
| 242 | __in int iValue | ||
| 243 | ); | ||
| 244 | |||
| 245 | HRESULT WIXAPI WcaDoDeferredAction( | ||
| 246 | __in_z LPCWSTR wzAction, | ||
| 247 | __in_z LPCWSTR wzCustomActionData, | ||
| 248 | __in UINT uiCost | ||
| 249 | ); | ||
| 250 | DWORD WIXAPI WcaCountOfCustomActionDataRecords( | ||
| 251 | __in_z LPCWSTR wzData | ||
| 252 | ); | ||
| 253 | |||
| 254 | HRESULT WIXAPI WcaReadStringFromCaData( | ||
| 255 | __deref_in LPWSTR* ppwzCustomActionData, | ||
| 256 | __deref_out_z LPWSTR* ppwzString | ||
| 257 | ); | ||
| 258 | HRESULT WIXAPI WcaReadIntegerFromCaData( | ||
| 259 | __deref_in LPWSTR* ppwzCustomActionData, | ||
| 260 | __out int* piResult | ||
| 261 | ); | ||
| 262 | HRESULT WIXAPI WcaReadStreamFromCaData( | ||
| 263 | __deref_in LPWSTR* ppwzCustomActionData, | ||
| 264 | __deref_out_bcount(*pcbData) BYTE** ppbData, | ||
| 265 | __out DWORD_PTR* pcbData | ||
| 266 | ); | ||
| 267 | HRESULT WIXAPI WcaWriteStringToCaData( | ||
| 268 | __in_z LPCWSTR wzString, | ||
| 269 | __deref_inout_z LPWSTR* ppwzCustomActionData | ||
| 270 | ); | ||
| 271 | HRESULT WIXAPI WcaWriteIntegerToCaData( | ||
| 272 | __in int i, | ||
| 273 | __deref_out_z_opt LPWSTR* ppwzCustomActionData | ||
| 274 | ); | ||
| 275 | HRESULT WIXAPI WcaWriteStreamToCaData( | ||
| 276 | __in_bcount(cbData) const BYTE* pbData, | ||
| 277 | __in DWORD cbData, | ||
| 278 | __deref_inout_z_opt LPWSTR* ppwzCustomActionData | ||
| 279 | ); | ||
| 280 | |||
| 281 | HRESULT __cdecl WcaAddTempRecord( | ||
| 282 | __inout MSIHANDLE* phTableView, | ||
| 283 | __inout MSIHANDLE* phColumns, | ||
| 284 | __in_z LPCWSTR wzTable, | ||
| 285 | __out_opt MSIDBERROR* pdbError, | ||
| 286 | __in UINT uiUniquifyColumn, | ||
| 287 | __in UINT cColumns, | ||
| 288 | ... | ||
| 289 | ); | ||
| 290 | |||
| 291 | HRESULT WIXAPI WcaDumpTable( | ||
| 292 | __in_z LPCWSTR wzTable | ||
| 293 | ); | ||
| 294 | |||
| 295 | HRESULT WIXAPI WcaDeferredActionRequiresReboot(); | ||
| 296 | BOOL WIXAPI WcaDidDeferredActionRequireReboot(); | ||
| 297 | |||
| 298 | HRESULT WIXAPI WcaCaScriptCreateKey( | ||
| 299 | __out LPWSTR* ppwzScriptKey | ||
| 300 | ); | ||
| 301 | |||
| 302 | HRESULT WIXAPI WcaCaScriptCreate( | ||
| 303 | __in WCA_ACTION action, | ||
| 304 | __in WCA_CASCRIPT script, | ||
| 305 | __in BOOL fImpersonated, | ||
| 306 | __in_z LPCWSTR wzScriptKey, | ||
| 307 | __in BOOL fAppend, | ||
| 308 | __out WCA_CASCRIPT_HANDLE* phScript | ||
| 309 | ); | ||
| 310 | |||
| 311 | HRESULT WIXAPI WcaCaScriptOpen( | ||
| 312 | __in WCA_ACTION action, | ||
| 313 | __in WCA_CASCRIPT script, | ||
| 314 | __in BOOL fImpersonated, | ||
| 315 | __in_z LPCWSTR wzScriptKey, | ||
| 316 | __out WCA_CASCRIPT_HANDLE* phScript | ||
| 317 | ); | ||
| 318 | |||
| 319 | void WIXAPI WcaCaScriptClose( | ||
| 320 | __in_opt WCA_CASCRIPT_HANDLE hScript, | ||
| 321 | __in WCA_CASCRIPT_CLOSE closeOperation | ||
| 322 | ); | ||
| 323 | |||
| 324 | HRESULT WIXAPI WcaCaScriptReadAsCustomActionData( | ||
| 325 | __in WCA_CASCRIPT_HANDLE hScript, | ||
| 326 | __out LPWSTR* ppwzCustomActionData | ||
| 327 | ); | ||
| 328 | |||
| 329 | HRESULT WIXAPI WcaCaScriptWriteString( | ||
| 330 | __in WCA_CASCRIPT_HANDLE hScript, | ||
| 331 | __in_z LPCWSTR wzValue | ||
| 332 | ); | ||
| 333 | |||
| 334 | HRESULT WIXAPI WcaCaScriptWriteNumber( | ||
| 335 | __in WCA_CASCRIPT_HANDLE hScript, | ||
| 336 | __in DWORD dwValue | ||
| 337 | ); | ||
| 338 | |||
| 339 | void WIXAPI WcaCaScriptFlush( | ||
| 340 | __in WCA_CASCRIPT_HANDLE hScript | ||
| 341 | ); | ||
| 342 | |||
| 343 | void WIXAPI WcaCaScriptCleanup( | ||
| 344 | __in_z LPCWSTR wzProductCode, | ||
| 345 | __in BOOL fImpersonated | ||
| 346 | ); | ||
| 347 | |||
| 348 | HRESULT WIXAPI QuietExec( | ||
| 349 | __inout_z LPWSTR wzCommand, | ||
| 350 | __in DWORD dwTimeout, | ||
| 351 | __in BOOL fLogCommand, | ||
| 352 | __in BOOL fLogOutput | ||
| 353 | ); | ||
| 354 | |||
| 355 | HRESULT WIXAPI QuietExecCapture( | ||
| 356 | __inout_z LPWSTR wzCommand, | ||
| 357 | __in DWORD dwTimeout, | ||
| 358 | __in BOOL fLogCommand, | ||
| 359 | __in BOOL fLogOutput, | ||
| 360 | __out_z_opt LPWSTR* psczOutput | ||
| 361 | ); | ||
| 362 | |||
| 363 | WCA_TODO WIXAPI WcaGetComponentToDo( | ||
| 364 | __in_z LPCWSTR wzComponentId | ||
| 365 | ); | ||
| 366 | |||
| 367 | HRESULT WIXAPI WcaExtractBinaryToBuffer( | ||
| 368 | __in LPCWSTR wzBinaryId, | ||
| 369 | __out BYTE** pbData, | ||
| 370 | __out DWORD* pcbData | ||
| 371 | ); | ||
| 372 | HRESULT WIXAPI WcaExtractBinaryToFile( | ||
| 373 | __in LPCWSTR wzBinaryId, | ||
| 374 | __in LPCWSTR wzPath | ||
| 375 | ); | ||
| 376 | HRESULT WIXAPI WcaExtractBinaryToString( | ||
| 377 | __in LPCWSTR wzBinaryId, | ||
| 378 | __deref_out_z LPWSTR* psczOutput, | ||
| 379 | __out WCA_ENCODING* encoding | ||
| 380 | ); | ||
| 381 | |||
| 382 | #ifdef __cplusplus | ||
| 383 | } | ||
| 384 | #endif | ||
diff --git a/src/wcautil/inc/wcawow64.h b/src/wcautil/inc/wcawow64.h new file mode 100644 index 00000000..dd55f3fd --- /dev/null +++ b/src/wcautil/inc/wcawow64.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | #pragma once | ||
| 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. | ||
| 3 | |||
| 4 | |||
| 5 | #include "wcautil.h" | ||
| 6 | |||
| 7 | #ifdef __cplusplus | ||
| 8 | extern "C" { | ||
| 9 | #endif | ||
| 10 | |||
| 11 | HRESULT WIXAPI WcaInitializeWow64(); | ||
| 12 | BOOL WIXAPI WcaIsWow64Process(); | ||
| 13 | BOOL WIXAPI WcaIsWow64Initialized(); | ||
| 14 | HRESULT WIXAPI WcaDisableWow64FSRedirection(); | ||
| 15 | HRESULT WIXAPI WcaRevertWow64FSRedirection(); | ||
| 16 | HRESULT WIXAPI WcaFinalizeWow64(); | ||
| 17 | |||
| 18 | #ifdef __cplusplus | ||
| 19 | } | ||
| 20 | #endif | ||
diff --git a/src/wcautil/inc/wcawrapquery.h b/src/wcautil/inc/wcawrapquery.h new file mode 100644 index 00000000..e08f1c3f --- /dev/null +++ b/src/wcautil/inc/wcawrapquery.h | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | #pragma once | ||
| 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. | ||
| 3 | |||
| 4 | |||
| 5 | #include "wcautil.h" | ||
| 6 | |||
| 7 | // Enumerations | ||
| 8 | typedef enum eWrapQueryAction | ||
| 9 | { | ||
| 10 | wqaTableBegin = 1, | ||
| 11 | wqaTableFinish, | ||
| 12 | wqaRowBegin, | ||
| 13 | wqaRowFinish | ||
| 14 | } eWrapQueryAction; | ||
| 15 | |||
| 16 | typedef enum eColumnDataType | ||
| 17 | { | ||
| 18 | cdtString = 1, | ||
| 19 | cdtInt, | ||
| 20 | cdtStream, | ||
| 21 | cdtUnknown | ||
| 22 | } eColumnDataType; | ||
| 23 | |||
| 24 | typedef enum eFormatMaskColumn | ||
| 25 | { | ||
| 26 | efmcColumn1 = 1, | ||
| 27 | efmcColumn2 = 1 << 1, | ||
| 28 | efmcColumn3 = 1 << 2, | ||
| 29 | efmcColumn4 = 1 << 3, | ||
| 30 | efmcColumn5 = 1 << 4, | ||
| 31 | efmcColumn6 = 1 << 5, | ||
| 32 | efmcColumn7 = 1 << 6, | ||
| 33 | efmcColumn8 = 1 << 7, | ||
| 34 | efmcColumn9 = 1 << 8, | ||
| 35 | efmcColumn10 = 1 << 9, | ||
| 36 | efmcColumn11 = 1 << 10, | ||
| 37 | efmcColumn12 = 1 << 11, | ||
| 38 | efmcColumn13 = 1 << 12, | ||
| 39 | efmcColumn14 = 1 << 13, | ||
| 40 | efmcColumn15 = 1 << 14, | ||
| 41 | efmcColumn16 = 1 << 15, | ||
| 42 | efmcColumn17 = 1 << 16, | ||
| 43 | efmcColumn18 = 1 << 17, | ||
| 44 | efmcColumn19 = 1 << 18, | ||
| 45 | efmcColumn20 = 1 << 19, | ||
| 46 | efmcColumn21 = 1 << 20, | ||
| 47 | efmcColumn22 = 1 << 21, | ||
| 48 | efmcColumn23 = 1 << 22, | ||
| 49 | efmcColumn24 = 1 << 23, | ||
| 50 | efmcColumn25 = 1 << 24, | ||
| 51 | efmcColumn26 = 1 << 25, | ||
| 52 | efmcColumn27 = 1 << 26, | ||
| 53 | efmcColumn28 = 1 << 27, | ||
| 54 | efmcColumn29 = 1 << 28, | ||
| 55 | efmcColumn30 = 1 << 29, | ||
| 56 | efmcColumn31 = 1 << 30, | ||
| 57 | efmcColumn32 = 1 << 31, | ||
| 58 | } eFormatMaskColumn; | ||
| 59 | |||
| 60 | // Keeps track of the query instance for the reading CA (deferred CA) | ||
| 61 | typedef struct WCA_WRAPQUERY_STRUCT | ||
| 62 | { | ||
| 63 | // These are used to size our dynamic arrays below | ||
| 64 | DWORD dwColumns, dwRows, dwNextIndex; | ||
| 65 | |||
| 66 | // Dynamic arrays of column schema information | ||
| 67 | eColumnDataType *pcdtColumnType; | ||
| 68 | LPWSTR *ppwzColumnNames; | ||
| 69 | |||
| 70 | // Dynamic array of raw record data | ||
| 71 | MSIHANDLE *phRecords; | ||
| 72 | } *WCA_WRAPQUERY_HANDLE; | ||
| 73 | |||
| 74 | // Wrap a query | ||
| 75 | // Setting the pfFormatMask enables control over which fields will be formatted, and which will be left unchanged | ||
| 76 | // Setting dwComponentColumn to something other than 0xFFFFFFFF tells WcaWrapQuery to add two additional columns to the right side of the table | ||
| 77 | // - ISInstalled and ISAction - which map to the ComponentState of the component (the component is found in the column specified) | ||
| 78 | // Note that if a component is NULL, the component state columns will also be left null, and it will be up to the deferred CA to fail or ignore the case appropriately | ||
| 79 | // Setting dwDirectoryColumn to something other than 0xFFFFFFFF tells WcaWrapQuery to add two more additional columns to the right side of the table | ||
| 80 | // - SourcePath and TargetPath - which map to the Directory's Source and Target Path (the directory is found in the column specified) | ||
| 81 | // Note that if a directory is NULL, the directory source/target path columns will also be left null, and it will be up to the deferred CA to fail or ignore the case appropriately | ||
| 82 | HRESULT WIXAPI WcaWrapQuery( | ||
| 83 | __in_z LPCWSTR pwzQuery, | ||
| 84 | __inout LPWSTR * ppwzCustomActionData, | ||
| 85 | __in_opt DWORD dwFormatMask, | ||
| 86 | __in_opt DWORD dwComponentColumn, | ||
| 87 | __in_opt DWORD dwDirectoryColumn | ||
| 88 | ); | ||
| 89 | // This wraps an empty table query into the custom action data - this is a way to indicate to the deferred custom action that a necessary table doesn't exist, or its query returned no results | ||
| 90 | HRESULT WIXAPI WcaWrapEmptyQuery( | ||
| 91 | __inout LPWSTR * ppwzCustomActionData | ||
| 92 | ); | ||
| 93 | |||
| 94 | // Open a new unwrap query operation, with data from the ppwzCustomActionData string | ||
| 95 | HRESULT WIXAPI WcaBeginUnwrapQuery( | ||
| 96 | __out WCA_WRAPQUERY_HANDLE * phWrapQuery, | ||
| 97 | __inout LPWSTR * ppwzCustomActionData | ||
| 98 | ); | ||
| 99 | |||
| 100 | // Get the number of records in a query being unwrapped | ||
| 101 | DWORD WIXAPI WcaGetQueryRecords( | ||
| 102 | __in const WCA_WRAPQUERY_HANDLE hWrapQuery | ||
| 103 | ); | ||
| 104 | |||
| 105 | // This function resets a query back to its first row, so that the next fetch returns the first record | ||
| 106 | void WIXAPI WcaFetchWrappedReset( | ||
| 107 | __in WCA_WRAPQUERY_HANDLE hWrapQuery | ||
| 108 | ); | ||
| 109 | // Fetch the next record in this query | ||
| 110 | // NOTE: the MSIHANDLE returned by this function should not be released, as it is the same handle used by the query object to maintain the item. | ||
| 111 | // so, don't use this function with PMSIHANDLE objects! | ||
| 112 | HRESULT WIXAPI WcaFetchWrappedRecord( | ||
| 113 | __in WCA_WRAPQUERY_HANDLE hWrapQuery, | ||
| 114 | __out MSIHANDLE* phRec | ||
| 115 | ); | ||
| 116 | |||
| 117 | // Fetch the next record in the query where the string value in column dwComparisonColumn equals the value pwzExpectedValue | ||
| 118 | // NOTE: the MSIHANDLE returned by this function should not be released, as it is the same handle used by the query object to maintain the item. | ||
| 119 | // so, don't use this function with PMSIHANDLE objects! | ||
| 120 | HRESULT WIXAPI WcaFetchWrappedRecordWhereString( | ||
| 121 | __in WCA_WRAPQUERY_HANDLE hWrapQuery, | ||
| 122 | __in DWORD dwComparisonColumn, | ||
| 123 | __in_z LPCWSTR pwzExpectedValue, | ||
| 124 | __out MSIHANDLE* phRec | ||
| 125 | ); | ||
| 126 | |||
| 127 | // Release a query ID (frees memory, and frees the ID for a new query) | ||
| 128 | void WIXAPI WcaFinishUnwrapQuery( | ||
| 129 | __in_opt WCA_WRAPQUERY_HANDLE hWrapQuery | ||
| 130 | ); | ||
diff --git a/src/wcautil/packages.config b/src/wcautil/packages.config new file mode 100644 index 00000000..b11fe210 --- /dev/null +++ b/src/wcautil/packages.config | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <packages> | ||
| 3 | <package id="Nerdbank.GitVersioning" version="2.0.37-beta" targetFramework="native" developmentDependency="true" /> | ||
| 4 | <package id="WixToolset.DUtil" version="4.0.3" targetFramework="native" /> | ||
| 5 | </packages> \ No newline at end of file | ||
diff --git a/src/wcautil/precomp.h b/src/wcautil/precomp.h new file mode 100644 index 00000000..1d41337a --- /dev/null +++ b/src/wcautil/precomp.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | #pragma once | ||
| 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. | ||
| 3 | |||
| 4 | |||
| 5 | #include <windows.h> | ||
| 6 | #include <msiquery.h> | ||
| 7 | #include <wchar.h> | ||
| 8 | #include <strsafe.h> | ||
| 9 | |||
| 10 | const WCHAR MAGIC_MULTISZ_DELIM = 128; | ||
| 11 | |||
| 12 | #include "wcautil.h" | ||
| 13 | #include "inc\wcalog.h" | ||
| 14 | #include "inc\wcawow64.h" | ||
| 15 | #include "inc\wcawrapquery.h" | ||
| 16 | #include "wiutil.h" | ||
| 17 | #include "fileutil.h" | ||
| 18 | #include "memutil.h" | ||
| 19 | #include "strutil.h" | ||
diff --git a/src/wcautil/qtexec.cpp b/src/wcautil/qtexec.cpp new file mode 100644 index 00000000..19abfaf8 --- /dev/null +++ b/src/wcautil/qtexec.cpp | |||
| @@ -0,0 +1,340 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | #include "precomp.h" | ||
| 4 | |||
| 5 | #define OUTPUT_BUFFER 1024 | ||
| 6 | #define ONEMINUTE 60000 | ||
| 7 | |||
| 8 | static HRESULT CreatePipes( | ||
| 9 | __out HANDLE *phOutRead, | ||
| 10 | __out HANDLE *phOutWrite, | ||
| 11 | __out HANDLE *phErrWrite, | ||
| 12 | __out HANDLE *phInRead, | ||
| 13 | __out HANDLE *phInWrite | ||
| 14 | ) | ||
| 15 | { | ||
| 16 | Assert(phOutRead); | ||
| 17 | Assert(phOutWrite); | ||
| 18 | Assert(phErrWrite); | ||
| 19 | Assert(phInRead); | ||
| 20 | Assert(phInWrite); | ||
| 21 | |||
| 22 | HRESULT hr = S_OK; | ||
| 23 | SECURITY_ATTRIBUTES sa; | ||
| 24 | HANDLE hOutTemp = INVALID_HANDLE_VALUE; | ||
| 25 | HANDLE hInTemp = INVALID_HANDLE_VALUE; | ||
| 26 | |||
| 27 | HANDLE hOutRead = INVALID_HANDLE_VALUE; | ||
| 28 | HANDLE hOutWrite = INVALID_HANDLE_VALUE; | ||
| 29 | HANDLE hErrWrite = INVALID_HANDLE_VALUE; | ||
| 30 | HANDLE hInRead = INVALID_HANDLE_VALUE; | ||
| 31 | HANDLE hInWrite = INVALID_HANDLE_VALUE; | ||
| 32 | |||
| 33 | // Fill out security structure so we can inherit handles | ||
| 34 | ::ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); | ||
| 35 | sa.nLength = sizeof(SECURITY_ATTRIBUTES); | ||
| 36 | sa.bInheritHandle = TRUE; | ||
| 37 | sa.lpSecurityDescriptor = NULL; | ||
| 38 | |||
| 39 | // Create pipes | ||
| 40 | if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0)) | ||
| 41 | { | ||
| 42 | ExitOnLastError(hr, "Failed to create output pipe"); | ||
| 43 | } | ||
| 44 | |||
| 45 | if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0)) | ||
| 46 | { | ||
| 47 | ExitOnLastError(hr, "Failed to create input pipe"); | ||
| 48 | } | ||
| 49 | |||
| 50 | |||
| 51 | // Duplicate output pipe so standard error and standard output write to | ||
| 52 | // the same pipe | ||
| 53 | if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) | ||
| 54 | { | ||
| 55 | ExitOnLastError(hr, "Failed to duplicate write handle"); | ||
| 56 | } | ||
| 57 | |||
| 58 | // We need to create new output read and input write handles that are | ||
| 59 | // non inheritable. Otherwise it creates handles that can't be closed. | ||
| 60 | if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS)) | ||
| 61 | { | ||
| 62 | ExitOnLastError(hr, "Failed to duplicate output pipe"); | ||
| 63 | } | ||
| 64 | |||
| 65 | if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS)) | ||
| 66 | { | ||
| 67 | ExitOnLastError(hr, "Failed to duplicate input pipe"); | ||
| 68 | } | ||
| 69 | |||
| 70 | // now that everything has succeeded, assign to the outputs | ||
| 71 | *phOutRead = hOutRead; | ||
| 72 | hOutRead = INVALID_HANDLE_VALUE; | ||
| 73 | |||
| 74 | *phOutWrite = hOutWrite; | ||
| 75 | hOutWrite = INVALID_HANDLE_VALUE; | ||
| 76 | |||
| 77 | *phErrWrite = hErrWrite; | ||
| 78 | hErrWrite = INVALID_HANDLE_VALUE; | ||
| 79 | |||
| 80 | *phInRead = hInRead; | ||
| 81 | hInRead = INVALID_HANDLE_VALUE; | ||
| 82 | |||
| 83 | *phInWrite = hInWrite; | ||
| 84 | hInWrite = INVALID_HANDLE_VALUE; | ||
| 85 | |||
| 86 | LExit: | ||
| 87 | ReleaseFile(hOutRead); | ||
| 88 | ReleaseFile(hOutWrite); | ||
| 89 | ReleaseFile(hErrWrite); | ||
| 90 | ReleaseFile(hInRead); | ||
| 91 | ReleaseFile(hInWrite); | ||
| 92 | ReleaseFile(hOutTemp); | ||
| 93 | ReleaseFile(hInTemp); | ||
| 94 | |||
| 95 | return hr; | ||
| 96 | } | ||
| 97 | |||
| 98 | static HRESULT HandleOutput( | ||
| 99 | __in BOOL fLogOutput, | ||
| 100 | __in HANDLE hRead, | ||
| 101 | __out_z_opt LPWSTR* psczOutput | ||
| 102 | ) | ||
| 103 | { | ||
| 104 | BYTE* pBuffer = NULL; | ||
| 105 | LPWSTR szLog = NULL; | ||
| 106 | LPWSTR szTemp = NULL; | ||
| 107 | LPWSTR pEnd = NULL; | ||
| 108 | LPWSTR pNext = NULL; | ||
| 109 | LPWSTR sczEscaped = NULL; | ||
| 110 | LPSTR szWrite = NULL; | ||
| 111 | DWORD dwBytes = OUTPUT_BUFFER; | ||
| 112 | BOOL bFirst = TRUE; | ||
| 113 | BOOL bUnicode = TRUE; | ||
| 114 | HRESULT hr = S_OK; | ||
| 115 | |||
| 116 | // Get buffer for output | ||
| 117 | pBuffer = static_cast<BYTE *>(MemAlloc(OUTPUT_BUFFER, FALSE)); | ||
| 118 | ExitOnNull(pBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for output."); | ||
| 119 | |||
| 120 | while (0 != dwBytes) | ||
| 121 | { | ||
| 122 | ::ZeroMemory(pBuffer, OUTPUT_BUFFER); | ||
| 123 | if (!::ReadFile(hRead, pBuffer, OUTPUT_BUFFER - 1, &dwBytes, NULL) && GetLastError() != ERROR_BROKEN_PIPE) | ||
| 124 | { | ||
| 125 | ExitOnLastError(hr, "Failed to read from handle."); | ||
| 126 | } | ||
| 127 | |||
| 128 | if (fLogOutput) | ||
| 129 | { | ||
| 130 | // Check for UNICODE or ANSI output | ||
| 131 | if (bFirst) | ||
| 132 | { | ||
| 133 | if ((isgraph(pBuffer[0]) && isgraph(pBuffer[1])) || | ||
| 134 | (isgraph(pBuffer[0]) && isspace(pBuffer[1])) || | ||
| 135 | (isspace(pBuffer[0]) && isgraph(pBuffer[1])) || | ||
| 136 | (isspace(pBuffer[0]) && isspace(pBuffer[1]))) | ||
| 137 | { | ||
| 138 | bUnicode = FALSE; | ||
| 139 | } | ||
| 140 | |||
| 141 | bFirst = FALSE; | ||
| 142 | } | ||
| 143 | |||
| 144 | // Keep track of output | ||
| 145 | if (bUnicode) | ||
| 146 | { | ||
| 147 | hr = StrAllocConcat(&szLog, (LPCWSTR)pBuffer, 0); | ||
| 148 | ExitOnFailure(hr, "Failed to concatenate output strings."); | ||
| 149 | |||
| 150 | if (psczOutput) | ||
| 151 | { | ||
| 152 | hr = StrAllocConcat(psczOutput, (LPCWSTR)pBuffer, 0); | ||
| 153 | ExitOnFailure(hr, "Failed to concatenate output to return string."); | ||
| 154 | } | ||
| 155 | } | ||
| 156 | else | ||
| 157 | { | ||
| 158 | hr = StrAllocStringAnsi(&szTemp, (LPCSTR)pBuffer, 0, CP_OEMCP); | ||
| 159 | ExitOnFailure(hr, "Failed to allocate output string."); | ||
| 160 | hr = StrAllocConcat(&szLog, szTemp, 0); | ||
| 161 | ExitOnFailure(hr, "Failed to concatenate output strings."); | ||
| 162 | |||
| 163 | if (psczOutput) | ||
| 164 | { | ||
| 165 | hr = StrAllocConcat(psczOutput, szTemp, 0); | ||
| 166 | ExitOnFailure(hr, "Failed to concatenate output to return string."); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | // Log each line of the output | ||
| 171 | pNext = szLog; | ||
| 172 | pEnd = wcschr(szLog, L'\r'); | ||
| 173 | if (NULL == pEnd) | ||
| 174 | { | ||
| 175 | pEnd = wcschr(szLog, L'\n'); | ||
| 176 | } | ||
| 177 | while (pEnd && *pEnd) | ||
| 178 | { | ||
| 179 | // Find beginning of next line | ||
| 180 | pEnd[0] = 0; | ||
| 181 | ++pEnd; | ||
| 182 | if ((pEnd[0] == L'\r') || (pEnd[0] == L'\n')) | ||
| 183 | { | ||
| 184 | ++pEnd; | ||
| 185 | } | ||
| 186 | |||
| 187 | // Log output | ||
| 188 | hr = StrAllocString(&sczEscaped, pNext, 0); | ||
| 189 | ExitOnFailure(hr, "Failed to allocate copy of string"); | ||
| 190 | |||
| 191 | hr = StrReplaceStringAll(&sczEscaped, L"%", L"%%"); | ||
| 192 | ExitOnFailure(hr, "Failed to escape percent signs in string"); | ||
| 193 | |||
| 194 | hr = StrAnsiAllocString(&szWrite, sczEscaped, 0, CP_OEMCP); | ||
| 195 | ExitOnFailure(hr, "Failed to convert output to ANSI"); | ||
| 196 | WcaLog(LOGMSG_STANDARD, szWrite); | ||
| 197 | |||
| 198 | // Next line | ||
| 199 | pNext = pEnd; | ||
| 200 | pEnd = wcschr(pNext, L'\r'); | ||
| 201 | if (NULL == pEnd) | ||
| 202 | { | ||
| 203 | pEnd = wcschr(pNext, L'\n'); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | hr = StrAllocString(&szTemp, pNext, 0); | ||
| 208 | ExitOnFailure(hr, "Failed to allocate string"); | ||
| 209 | |||
| 210 | hr = StrAllocString(&szLog, szTemp, 0); | ||
| 211 | ExitOnFailure(hr, "Failed to allocate string"); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | // Print any text that didn't end with a new line | ||
| 216 | if (szLog && *szLog) | ||
| 217 | { | ||
| 218 | hr = StrReplaceStringAll(&szLog, L"%", L"%%"); | ||
| 219 | ExitOnFailure(hr, "Failed to escape percent signs in string"); | ||
| 220 | |||
| 221 | hr = StrAnsiAllocString(&szWrite, szLog, 0, CP_OEMCP); | ||
| 222 | ExitOnFailure(hr, "Failed to convert output to ANSI"); | ||
| 223 | |||
| 224 | WcaLog(LOGMSG_VERBOSE, szWrite); | ||
| 225 | } | ||
| 226 | |||
| 227 | LExit: | ||
| 228 | ReleaseMem(pBuffer); | ||
| 229 | |||
| 230 | ReleaseStr(szLog); | ||
| 231 | ReleaseStr(szTemp); | ||
| 232 | ReleaseStr(szWrite); | ||
| 233 | ReleaseStr(sczEscaped); | ||
| 234 | |||
| 235 | return hr; | ||
| 236 | } | ||
| 237 | |||
| 238 | static HRESULT QuietExecImpl( | ||
| 239 | __inout_z LPWSTR wzCommand, | ||
| 240 | __in DWORD dwTimeout, | ||
| 241 | __in BOOL fLogCommand, | ||
| 242 | __in BOOL fLogOutput, | ||
| 243 | __out_z_opt LPWSTR* psczOutput | ||
| 244 | ) | ||
| 245 | { | ||
| 246 | HRESULT hr = S_OK; | ||
| 247 | PROCESS_INFORMATION oProcInfo; | ||
| 248 | STARTUPINFOW oStartInfo; | ||
| 249 | DWORD dwExitCode = ERROR_SUCCESS; | ||
| 250 | HANDLE hOutRead = INVALID_HANDLE_VALUE; | ||
| 251 | HANDLE hOutWrite = INVALID_HANDLE_VALUE; | ||
| 252 | HANDLE hErrWrite = INVALID_HANDLE_VALUE; | ||
| 253 | HANDLE hInRead = INVALID_HANDLE_VALUE; | ||
| 254 | HANDLE hInWrite = INVALID_HANDLE_VALUE; | ||
| 255 | |||
| 256 | memset(&oProcInfo, 0, sizeof(oProcInfo)); | ||
| 257 | memset(&oStartInfo, 0, sizeof(oStartInfo)); | ||
| 258 | |||
| 259 | // Create output redirect pipes | ||
| 260 | hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite); | ||
| 261 | ExitOnFailure(hr, "Failed to create output pipes"); | ||
| 262 | |||
| 263 | // Set up startup structure | ||
| 264 | oStartInfo.cb = sizeof(STARTUPINFOW); | ||
| 265 | oStartInfo.dwFlags = STARTF_USESTDHANDLES; | ||
| 266 | oStartInfo.hStdInput = hInRead; | ||
| 267 | oStartInfo.hStdOutput = hOutWrite; | ||
| 268 | oStartInfo.hStdError = hErrWrite; | ||
| 269 | |||
| 270 | // Log command if we were asked to do so | ||
| 271 | if (fLogCommand) | ||
| 272 | { | ||
| 273 | WcaLog(LOGMSG_VERBOSE, "%ls", wzCommand); | ||
| 274 | } | ||
| 275 | |||
| 276 | #pragma prefast(suppress:25028) | ||
| 277 | if (::CreateProcessW(NULL, | ||
| 278 | wzCommand, // command line | ||
| 279 | NULL, // security info | ||
| 280 | NULL, // thread info | ||
| 281 | TRUE, // inherit handles | ||
| 282 | ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags | ||
| 283 | NULL, // environment | ||
| 284 | NULL, // cur dir | ||
| 285 | &oStartInfo, | ||
| 286 | &oProcInfo)) | ||
| 287 | { | ||
| 288 | ReleaseFile(oProcInfo.hThread); | ||
| 289 | |||
| 290 | // Close child output/input handles so it doesn't hang | ||
| 291 | ReleaseFile(hOutWrite); | ||
| 292 | ReleaseFile(hErrWrite); | ||
| 293 | ReleaseFile(hInRead); | ||
| 294 | |||
| 295 | // Log output if we were asked to do so; otherwise just read the output handle | ||
| 296 | HandleOutput(fLogOutput, hOutRead, psczOutput); | ||
| 297 | |||
| 298 | // Wait for everything to finish | ||
| 299 | ::WaitForSingleObject(oProcInfo.hProcess, dwTimeout); | ||
| 300 | if (!::GetExitCodeProcess(oProcInfo.hProcess, &dwExitCode)) | ||
| 301 | { | ||
| 302 | dwExitCode = ERROR_SEM_IS_SET; | ||
| 303 | } | ||
| 304 | |||
| 305 | ReleaseFile(hOutRead); | ||
| 306 | ReleaseFile(hInWrite); | ||
| 307 | ReleaseFile(oProcInfo.hProcess); | ||
| 308 | } | ||
| 309 | else | ||
| 310 | { | ||
| 311 | ExitOnLastError(hr, "Command failed to execute."); | ||
| 312 | } | ||
| 313 | |||
| 314 | ExitOnWin32Error(dwExitCode, hr, "Command line returned an error."); | ||
| 315 | |||
| 316 | LExit: | ||
| 317 | return hr; | ||
| 318 | } | ||
| 319 | |||
| 320 | |||
| 321 | HRESULT WIXAPI QuietExec( | ||
| 322 | __inout_z LPWSTR wzCommand, | ||
| 323 | __in DWORD dwTimeout, | ||
| 324 | __in BOOL fLogCommand, | ||
| 325 | __in BOOL fLogOutput | ||
| 326 | ) | ||
| 327 | { | ||
| 328 | return QuietExecImpl(wzCommand, dwTimeout, fLogCommand, fLogOutput, NULL); | ||
| 329 | } | ||
| 330 | |||
| 331 | HRESULT WIXAPI QuietExecCapture( | ||
| 332 | __inout_z LPWSTR wzCommand, | ||
| 333 | __in DWORD dwTimeout, | ||
| 334 | __in BOOL fLogCommand, | ||
| 335 | __in BOOL fLogOutput, | ||
| 336 | __out_z_opt LPWSTR* psczOutput | ||
| 337 | ) | ||
| 338 | { | ||
| 339 | return QuietExecImpl(wzCommand, dwTimeout, fLogCommand, fLogOutput, psczOutput); | ||
| 340 | } | ||
diff --git a/src/wcautil/wcalog.cpp b/src/wcautil/wcalog.cpp new file mode 100644 index 00000000..fa969bff --- /dev/null +++ b/src/wcautil/wcalog.cpp | |||
| @@ -0,0 +1,251 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | #include "precomp.h" | ||
| 4 | |||
| 5 | /******************************************************************** | ||
| 6 | IsVerboseLoggingPolicy() - internal helper function to detect if | ||
| 7 | policy is set for verbose logging. Does | ||
| 8 | not require database access. | ||
| 9 | ********************************************************************/ | ||
| 10 | static BOOL IsVerboseLoggingPolicy() | ||
| 11 | { | ||
| 12 | BOOL fVerbose = FALSE; | ||
| 13 | HKEY hkey = NULL; | ||
| 14 | WCHAR rgwc[16] = { 0 }; | ||
| 15 | DWORD cb = sizeof(rgwc); | ||
| 16 | if (ERROR_SUCCESS == ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Policies\\Microsoft\\Windows\\Installer", 0, KEY_QUERY_VALUE, &hkey)) | ||
| 17 | { | ||
| 18 | if (ERROR_SUCCESS == ::RegQueryValueExW(hkey, L"Logging", 0, NULL, reinterpret_cast<BYTE*>(rgwc), &cb)) | ||
| 19 | { | ||
| 20 | for (LPCWSTR pwc = rgwc; (cb / sizeof(WCHAR)) > static_cast<DWORD>(pwc - rgwc) && *pwc; pwc++) | ||
| 21 | { | ||
| 22 | if (L'v' == *pwc || L'V' == *pwc) | ||
| 23 | { | ||
| 24 | fVerbose = TRUE; | ||
| 25 | break; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | ::RegCloseKey(hkey); | ||
| 31 | } | ||
| 32 | return fVerbose; | ||
| 33 | } | ||
| 34 | |||
| 35 | /******************************************************************** | ||
| 36 | IsVerboseLogging() - internal helper function to detect if doing | ||
| 37 | verbose logging. Checks: | ||
| 38 | 1. LOGVERBOSE property. | ||
| 39 | 2. MsiLogging property contains 'v' | ||
| 40 | 3. Policy from registry. | ||
| 41 | |||
| 42 | Requires database access. | ||
| 43 | ********************************************************************/ | ||
| 44 | BOOL WIXAPI IsVerboseLogging() | ||
| 45 | { | ||
| 46 | static int iVerbose = -1; | ||
| 47 | LPWSTR pwzMsiLogging = NULL; | ||
| 48 | |||
| 49 | if (0 > iVerbose) | ||
| 50 | { | ||
| 51 | iVerbose = WcaIsPropertySet("LOGVERBOSE"); | ||
| 52 | if (0 == iVerbose) | ||
| 53 | { | ||
| 54 | // if the property wasn't set, check the MsiLogging property (MSI 4.0+) | ||
| 55 | HRESULT hr = WcaGetProperty(L"MsiLogging", &pwzMsiLogging); | ||
| 56 | ExitOnFailure(hr, "failed to get MsiLogging property"); | ||
| 57 | int cchMsiLogging = lstrlenW(pwzMsiLogging); | ||
| 58 | if (0 < cchMsiLogging) | ||
| 59 | { | ||
| 60 | for (int i = 0; i < cchMsiLogging; i++) | ||
| 61 | { | ||
| 62 | if (L'v' == pwzMsiLogging[i] || L'V' == pwzMsiLogging[i]) | ||
| 63 | { | ||
| 64 | iVerbose = 1; | ||
| 65 | break; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | // last chance: Check the registry to see if the logging policy was turned on | ||
| 71 | if (0 == iVerbose && IsVerboseLoggingPolicy()) | ||
| 72 | { | ||
| 73 | iVerbose = 1; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | LExit: | ||
| 79 | ReleaseStr(pwzMsiLogging); | ||
| 80 | Assert(iVerbose >= 0); | ||
| 81 | return (BOOL)iVerbose; | ||
| 82 | } | ||
| 83 | |||
| 84 | /******************************************************************** | ||
| 85 | SetVerboseLoggingAtom() - Sets one of two global Atoms to specify | ||
| 86 | if the install should do verbose logging. | ||
| 87 | Communicates the verbose setting to | ||
| 88 | deferred CAs. | ||
| 89 | Set a negative case atom so that we can | ||
| 90 | distinguish between an unset atom and the | ||
| 91 | non-verbose case. This helps prevent the | ||
| 92 | expensive regkey lookup for non-verbose. | ||
| 93 | ********************************************************************/ | ||
| 94 | HRESULT WIXAPI SetVerboseLoggingAtom(BOOL bValue) | ||
| 95 | { | ||
| 96 | HRESULT hr = S_OK; | ||
| 97 | ATOM atomVerbose = 0; | ||
| 98 | |||
| 99 | atomVerbose = ::GlobalFindAtomW(L"WcaVerboseLogging"); | ||
| 100 | if (0 == atomVerbose && bValue) | ||
| 101 | { | ||
| 102 | atomVerbose = ::GlobalAddAtomW(L"WcaVerboseLogging"); | ||
| 103 | ExitOnNullWithLastError(atomVerbose, hr, "Failed to create WcaVerboseLogging global atom."); | ||
| 104 | } | ||
| 105 | else if (0 != atomVerbose && !bValue) | ||
| 106 | { | ||
| 107 | ::SetLastError(ERROR_SUCCESS); | ||
| 108 | ::GlobalDeleteAtom(atomVerbose); | ||
| 109 | ExitOnLastError(hr, "Failed to delete WcaVerboseLogging global atom."); | ||
| 110 | } | ||
| 111 | |||
| 112 | atomVerbose = ::GlobalFindAtomW(L"WcaNotVerboseLogging"); | ||
| 113 | if (0 == atomVerbose && !bValue) | ||
| 114 | { | ||
| 115 | atomVerbose = ::GlobalAddAtomW(L"WcaNotVerboseLogging"); | ||
| 116 | ExitOnNullWithLastError(atomVerbose, hr, "Failed to create WcaNotVerboseLogging global atom."); | ||
| 117 | } | ||
| 118 | else if (0 != atomVerbose && bValue) | ||
| 119 | { | ||
| 120 | ::SetLastError(ERROR_SUCCESS); | ||
| 121 | ::GlobalDeleteAtom(atomVerbose); | ||
| 122 | ExitOnLastError(hr, "Failed to delete WcaNotVerboseLogging global atom."); | ||
| 123 | } | ||
| 124 | |||
| 125 | LExit: | ||
| 126 | return hr; | ||
| 127 | } | ||
| 128 | |||
| 129 | /******************************************************************** | ||
| 130 | IsVerboseLoggingLite() - internal helper function to detect if atom was | ||
| 131 | previously set to specify verbose logging. | ||
| 132 | Falls back on policy for an installer that is | ||
| 133 | unable to set the atom (no immediate CAs). | ||
| 134 | |||
| 135 | Does not require database access. | ||
| 136 | ********************************************************************/ | ||
| 137 | static BOOL IsVerboseLoggingLite() | ||
| 138 | { | ||
| 139 | ATOM atomVerbose = ::GlobalFindAtomW(L"WcaVerboseLogging"); | ||
| 140 | if (0 != atomVerbose) | ||
| 141 | { | ||
| 142 | return TRUE; | ||
| 143 | } | ||
| 144 | |||
| 145 | atomVerbose = ::GlobalFindAtomW(L"WcaNotVerboseLogging"); | ||
| 146 | if (0 != atomVerbose) | ||
| 147 | { | ||
| 148 | return FALSE; | ||
| 149 | } | ||
| 150 | |||
| 151 | return IsVerboseLoggingPolicy(); | ||
| 152 | } | ||
| 153 | |||
| 154 | /******************************************************************** | ||
| 155 | WcaLog() - outputs trace and log info | ||
| 156 | |||
| 157 | *******************************************************************/ | ||
| 158 | extern "C" void __cdecl WcaLog( | ||
| 159 | __in LOGLEVEL llv, | ||
| 160 | __in_z __format_string PCSTR fmt, | ||
| 161 | ... | ||
| 162 | ) | ||
| 163 | { | ||
| 164 | static char szFmt[LOG_BUFFER]; | ||
| 165 | static char szBuf[LOG_BUFFER]; | ||
| 166 | static bool fInLogPrint = false; | ||
| 167 | |||
| 168 | // prevent re-entrant logprints. (recursion issues between assert/logging code) | ||
| 169 | if (fInLogPrint) | ||
| 170 | return; | ||
| 171 | fInLogPrint = true; | ||
| 172 | |||
| 173 | if (LOGMSG_STANDARD == llv || | ||
| 174 | (LOGMSG_VERBOSE == llv && IsVerboseLoggingLite()) | ||
| 175 | #ifdef DEBUG | ||
| 176 | || LOGMSG_TRACEONLY == llv | ||
| 177 | #endif | ||
| 178 | ) | ||
| 179 | { | ||
| 180 | va_list args; | ||
| 181 | va_start(args, fmt); | ||
| 182 | |||
| 183 | LPCSTR szLogName = WcaGetLogName(); | ||
| 184 | if (szLogName[0] != 0) | ||
| 185 | StringCchPrintfA(szFmt, countof(szFmt), "%s: %s", szLogName, fmt); | ||
| 186 | else | ||
| 187 | StringCchCopyA(szFmt, countof(szFmt), fmt); | ||
| 188 | |||
| 189 | StringCchVPrintfA(szBuf, countof(szBuf), szFmt, args); | ||
| 190 | va_end(args); | ||
| 191 | |||
| 192 | #ifdef DEBUG | ||
| 193 | // always write to the log in debug | ||
| 194 | #else | ||
| 195 | if (llv == LOGMSG_STANDARD || (llv == LOGMSG_VERBOSE && IsVerboseLoggingLite())) | ||
| 196 | #endif | ||
| 197 | { | ||
| 198 | PMSIHANDLE hrec = MsiCreateRecord(1); | ||
| 199 | |||
| 200 | ::MsiRecordSetStringA(hrec, 0, szBuf); | ||
| 201 | // TODO: Recursion on failure. May not be safe to assert from here. | ||
| 202 | WcaProcessMessage(INSTALLMESSAGE_INFO, hrec); | ||
| 203 | } | ||
| 204 | |||
| 205 | #if DEBUG | ||
| 206 | StringCchCatA(szBuf, countof(szBuf), "\n"); | ||
| 207 | OutputDebugStringA(szBuf); | ||
| 208 | #endif | ||
| 209 | } | ||
| 210 | |||
| 211 | fInLogPrint = false; | ||
| 212 | return; | ||
| 213 | } | ||
| 214 | |||
| 215 | |||
| 216 | /******************************************************************** | ||
| 217 | WcaDisplayAssert() - called before Assert() dialog shows | ||
| 218 | |||
| 219 | NOTE: writes the assert string to the MSI log | ||
| 220 | ********************************************************************/ | ||
| 221 | extern "C" BOOL WIXAPI WcaDisplayAssert( | ||
| 222 | __in LPCSTR sz | ||
| 223 | ) | ||
| 224 | { | ||
| 225 | WcaLog(LOGMSG_STANDARD, "Debug Assert Message: %s", sz); | ||
| 226 | return TRUE; | ||
| 227 | } | ||
| 228 | |||
| 229 | |||
| 230 | /******************************************************************** | ||
| 231 | WcaLogError() - called before ExitOnXXX() macro exists the function | ||
| 232 | |||
| 233 | NOTE: writes the hresult and error string to the MSI log | ||
| 234 | ********************************************************************/ | ||
| 235 | extern "C" void WcaLogError( | ||
| 236 | __in HRESULT hr, | ||
| 237 | __in LPCSTR szMessage, | ||
| 238 | ... | ||
| 239 | ) | ||
| 240 | { | ||
| 241 | char szBuffer[LOG_BUFFER]; | ||
| 242 | va_list dots; | ||
| 243 | |||
| 244 | va_start(dots, szMessage); | ||
| 245 | StringCchVPrintfA(szBuffer, countof(szBuffer), szMessage, dots); | ||
| 246 | va_end(dots); | ||
| 247 | |||
| 248 | // log the message if using Wca common layer | ||
| 249 | if (WcaIsInitialized()) | ||
| 250 | WcaLog(LOGMSG_STANDARD, "Error 0x%x: %s", hr, szBuffer); | ||
| 251 | } | ||
diff --git a/src/wcautil/wcascript.cpp b/src/wcautil/wcascript.cpp new file mode 100644 index 00000000..b6629850 --- /dev/null +++ b/src/wcautil/wcascript.cpp | |||
| @@ -0,0 +1,447 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | #include "precomp.h" | ||
| 4 | |||
| 5 | |||
| 6 | static HRESULT CaScriptFileName( | ||
| 7 | __in WCA_ACTION action, | ||
| 8 | __in WCA_CASCRIPT script, | ||
| 9 | __in BOOL fImpersonated, | ||
| 10 | __in_z LPCWSTR wzScriptKey, | ||
| 11 | __out LPWSTR* pwzScriptName | ||
| 12 | ); | ||
| 13 | |||
| 14 | |||
| 15 | /******************************************************************** | ||
| 16 | WcaCaScriptCreateKey() - creates a unique script key for this | ||
| 17 | CustomAction. | ||
| 18 | |||
| 19 | ********************************************************************/ | ||
| 20 | extern "C" HRESULT WIXAPI WcaCaScriptCreateKey( | ||
| 21 | __out LPWSTR* ppwzScriptKey | ||
| 22 | ) | ||
| 23 | { | ||
| 24 | AssertSz(WcaIsInitialized(), "WcaInitialize() should have been called before calling this function."); | ||
| 25 | HRESULT hr = S_OK; | ||
| 26 | |||
| 27 | hr = StrAllocStringAnsi(ppwzScriptKey, WcaGetLogName(), 0, CP_ACP); | ||
| 28 | ExitOnFailure(hr, "Failed to create script key."); | ||
| 29 | |||
| 30 | LExit: | ||
| 31 | return hr; | ||
| 32 | } | ||
| 33 | |||
| 34 | |||
| 35 | /******************************************************************** | ||
| 36 | WcaCaScriptCreate() - creates the appropriate script for this | ||
| 37 | CustomAction Script Key. | ||
| 38 | |||
| 39 | ********************************************************************/ | ||
| 40 | extern "C" HRESULT WIXAPI WcaCaScriptCreate( | ||
| 41 | __in WCA_ACTION action, | ||
| 42 | __in WCA_CASCRIPT script, | ||
| 43 | __in BOOL fImpersonated, | ||
| 44 | __in_z LPCWSTR wzScriptKey, | ||
| 45 | __in BOOL fAppend, | ||
| 46 | __out WCA_CASCRIPT_HANDLE* phScript | ||
| 47 | ) | ||
| 48 | { | ||
| 49 | HRESULT hr = S_OK; | ||
| 50 | LPWSTR pwzScriptPath = NULL; | ||
| 51 | HANDLE hScriptFile = INVALID_HANDLE_VALUE; | ||
| 52 | |||
| 53 | hr = CaScriptFileName(action, script, fImpersonated, wzScriptKey, &pwzScriptPath); | ||
| 54 | ExitOnFailure(hr, "Failed to calculate script file name."); | ||
| 55 | |||
| 56 | hScriptFile = ::CreateFileW(pwzScriptPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, fAppend ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
| 57 | if (INVALID_HANDLE_VALUE == hScriptFile) | ||
| 58 | { | ||
| 59 | ExitWithLastError(hr, "Failed to open CaScript: %ls", pwzScriptPath); | ||
| 60 | } | ||
| 61 | |||
| 62 | if (fAppend && INVALID_SET_FILE_POINTER == ::SetFilePointer(hScriptFile, 0, NULL, FILE_END)) | ||
| 63 | { | ||
| 64 | ExitWithLastError(hr, "Failed to seek to end of file."); | ||
| 65 | } | ||
| 66 | |||
| 67 | *phScript = static_cast<WCA_CASCRIPT_HANDLE>(MemAlloc(sizeof(WCA_CASCRIPT_STRUCT), TRUE)); | ||
| 68 | ExitOnNull(*phScript, hr, E_OUTOFMEMORY, "Failed to allocate space for cascript handle."); | ||
| 69 | |||
| 70 | (*phScript)->pwzScriptPath = pwzScriptPath; | ||
| 71 | pwzScriptPath = NULL; | ||
| 72 | (*phScript)->hScriptFile = hScriptFile; | ||
| 73 | hScriptFile = INVALID_HANDLE_VALUE; | ||
| 74 | |||
| 75 | LExit: | ||
| 76 | if (INVALID_HANDLE_VALUE != hScriptFile) | ||
| 77 | { | ||
| 78 | ::CloseHandle(hScriptFile); | ||
| 79 | } | ||
| 80 | |||
| 81 | ReleaseStr(pwzScriptPath); | ||
| 82 | return hr; | ||
| 83 | } | ||
| 84 | |||
| 85 | |||
| 86 | /******************************************************************** | ||
| 87 | WcaCaScriptOpen() - opens the appropriate script for this CustomAction | ||
| 88 | Script Key. | ||
| 89 | |||
| 90 | ********************************************************************/ | ||
| 91 | extern "C" HRESULT WIXAPI WcaCaScriptOpen( | ||
| 92 | __in WCA_ACTION action, | ||
| 93 | __in WCA_CASCRIPT script, | ||
| 94 | __in BOOL fImpersonated, | ||
| 95 | __in_z LPCWSTR wzScriptKey, | ||
| 96 | __out WCA_CASCRIPT_HANDLE* phScript | ||
| 97 | ) | ||
| 98 | { | ||
| 99 | HRESULT hr = S_OK; | ||
| 100 | LPWSTR pwzScriptPath = NULL; | ||
| 101 | HANDLE hScriptFile = INVALID_HANDLE_VALUE; | ||
| 102 | |||
| 103 | hr = CaScriptFileName(action, script, fImpersonated, wzScriptKey, &pwzScriptPath); | ||
| 104 | ExitOnFailure(hr, "Failed to calculate script file name."); | ||
| 105 | |||
| 106 | hScriptFile = ::CreateFileW(pwzScriptPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
| 107 | if (INVALID_HANDLE_VALUE == hScriptFile) | ||
| 108 | { | ||
| 109 | ExitWithLastError(hr, "Failed to open CaScript: %ls", pwzScriptPath); | ||
| 110 | } | ||
| 111 | |||
| 112 | *phScript = static_cast<WCA_CASCRIPT_HANDLE>(MemAlloc(sizeof(WCA_CASCRIPT_STRUCT), TRUE)); | ||
| 113 | ExitOnNull(*phScript, hr, E_OUTOFMEMORY, "Failed to allocate space for cascript handle."); | ||
| 114 | |||
| 115 | (*phScript)->pwzScriptPath = pwzScriptPath; | ||
| 116 | pwzScriptPath = NULL; | ||
| 117 | (*phScript)->hScriptFile = hScriptFile; | ||
| 118 | hScriptFile = INVALID_HANDLE_VALUE; | ||
| 119 | |||
| 120 | LExit: | ||
| 121 | if (INVALID_HANDLE_VALUE != hScriptFile) | ||
| 122 | { | ||
| 123 | ::CloseHandle(hScriptFile); | ||
| 124 | } | ||
| 125 | |||
| 126 | ReleaseStr(pwzScriptPath); | ||
| 127 | return hr; | ||
| 128 | } | ||
| 129 | |||
| 130 | |||
| 131 | /******************************************************************** | ||
| 132 | WcaCaScriptClose() - closes an open script handle. | ||
| 133 | |||
| 134 | ********************************************************************/ | ||
| 135 | extern "C" void WIXAPI WcaCaScriptClose( | ||
| 136 | __in_opt WCA_CASCRIPT_HANDLE hScript, | ||
| 137 | __in WCA_CASCRIPT_CLOSE closeOperation | ||
| 138 | ) | ||
| 139 | { | ||
| 140 | if (hScript) | ||
| 141 | { | ||
| 142 | if (INVALID_HANDLE_VALUE != hScript->hScriptFile) | ||
| 143 | { | ||
| 144 | ::CloseHandle(hScript->hScriptFile); | ||
| 145 | } | ||
| 146 | |||
| 147 | if (hScript->pwzScriptPath) | ||
| 148 | { | ||
| 149 | if (WCA_CASCRIPT_CLOSE_DELETE == closeOperation) | ||
| 150 | { | ||
| 151 | ::DeleteFileW(hScript->pwzScriptPath); | ||
| 152 | } | ||
| 153 | |||
| 154 | StrFree(hScript->pwzScriptPath); | ||
| 155 | } | ||
| 156 | |||
| 157 | MemFree(hScript); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | |||
| 162 | /******************************************************************** | ||
| 163 | WcaCaScriptReadAsCustomActionData() - read the ca script into a format | ||
| 164 | that is useable by other CA data | ||
| 165 | functions. | ||
| 166 | |||
| 167 | ********************************************************************/ | ||
| 168 | extern "C" HRESULT WIXAPI WcaCaScriptReadAsCustomActionData( | ||
| 169 | __in WCA_CASCRIPT_HANDLE hScript, | ||
| 170 | __out LPWSTR* ppwzCustomActionData | ||
| 171 | ) | ||
| 172 | { | ||
| 173 | HRESULT hr = S_OK; | ||
| 174 | LARGE_INTEGER liScriptSize = { 0 }; | ||
| 175 | BYTE* pbData = NULL; | ||
| 176 | DWORD cbData = 0; | ||
| 177 | |||
| 178 | if (!::GetFileSizeEx(hScript->hScriptFile, &liScriptSize)) | ||
| 179 | { | ||
| 180 | ExitWithLastError(hr, "Failed to get size of ca script file."); | ||
| 181 | } | ||
| 182 | |||
| 183 | if (0 != liScriptSize.HighPart || 0 != (liScriptSize.LowPart % sizeof(WCHAR))) | ||
| 184 | { | ||
| 185 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 186 | ExitOnRootFailure(hr, "Invalid data read from ca script."); | ||
| 187 | } | ||
| 188 | |||
| 189 | cbData = liScriptSize.LowPart; | ||
| 190 | if (cbData) | ||
| 191 | { | ||
| 192 | pbData = static_cast<BYTE*>(MemAlloc(cbData, TRUE)); | ||
| 193 | ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in ca script."); | ||
| 194 | |||
| 195 | if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hScript->hScriptFile, 0, NULL, FILE_BEGIN)) | ||
| 196 | { | ||
| 197 | ExitWithLastError(hr, "Failed to reset to beginning of ca script."); | ||
| 198 | } | ||
| 199 | |||
| 200 | DWORD cbTotalRead = 0; | ||
| 201 | DWORD cbRead = 0; | ||
| 202 | do | ||
| 203 | { | ||
| 204 | if (!::ReadFile(hScript->hScriptFile, pbData + cbTotalRead, cbData - cbTotalRead, &cbRead, NULL)) | ||
| 205 | { | ||
| 206 | ExitWithLastError(hr, "Failed to read from ca script."); | ||
| 207 | } | ||
| 208 | |||
| 209 | cbTotalRead += cbRead; | ||
| 210 | } while (cbRead && cbTotalRead < cbData); | ||
| 211 | |||
| 212 | if (cbTotalRead != cbData) | ||
| 213 | { | ||
| 214 | hr = E_UNEXPECTED; | ||
| 215 | ExitOnFailure(hr, "Failed to completely read ca script."); | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | // Add one to the allocated space because the data stored in the script is not | ||
| 220 | // null terminated. After copying the memory over, we'll ensure the string is | ||
| 221 | // null terminated. | ||
| 222 | DWORD cchData = cbData / sizeof(WCHAR) + 1; | ||
| 223 | hr = StrAlloc(ppwzCustomActionData, cchData); | ||
| 224 | ExitOnFailure(hr, "Failed to copy ca script."); | ||
| 225 | |||
| 226 | if (cbData) | ||
| 227 | { | ||
| 228 | CopyMemory(*ppwzCustomActionData, pbData, cbData); | ||
| 229 | } | ||
| 230 | |||
| 231 | (*ppwzCustomActionData)[cchData - 1] = L'\0'; | ||
| 232 | |||
| 233 | LExit: | ||
| 234 | ReleaseMem(pbData); | ||
| 235 | return hr; | ||
| 236 | } | ||
| 237 | |||
| 238 | |||
| 239 | /******************************************************************** | ||
| 240 | WcaCaScriptWriteString() - writes a string to the ca script. | ||
| 241 | |||
| 242 | ********************************************************************/ | ||
| 243 | extern "C" HRESULT WIXAPI WcaCaScriptWriteString( | ||
| 244 | __in WCA_CASCRIPT_HANDLE hScript, | ||
| 245 | __in_z LPCWSTR wzValue | ||
| 246 | ) | ||
| 247 | { | ||
| 248 | HRESULT hr = S_OK; | ||
| 249 | DWORD cbFile = 0; | ||
| 250 | DWORD cbWrite = 0; | ||
| 251 | DWORD cbTotalWritten = 0; | ||
| 252 | WCHAR delim[] = { MAGIC_MULTISZ_DELIM }; // magic char followed by NULL terminator | ||
| 253 | |||
| 254 | cbFile = ::SetFilePointer(hScript->hScriptFile, 0, NULL, FILE_END); | ||
| 255 | if (INVALID_SET_FILE_POINTER == cbFile) | ||
| 256 | { | ||
| 257 | ExitWithLastError(hr, "Failed to move file pointer to end of file."); | ||
| 258 | } | ||
| 259 | |||
| 260 | // If there is existing data in the file, append on the magic delimeter | ||
| 261 | // before adding our new data on the end of the file. | ||
| 262 | if (0 < cbFile) | ||
| 263 | { | ||
| 264 | cbWrite = sizeof(delim); | ||
| 265 | cbTotalWritten = 0; | ||
| 266 | while (cbTotalWritten < cbWrite) | ||
| 267 | { | ||
| 268 | DWORD cbWritten = 0; | ||
| 269 | if (!::WriteFile(hScript->hScriptFile, reinterpret_cast<BYTE*>(delim) + cbTotalWritten, cbWrite - cbTotalWritten, &cbWritten, NULL)) | ||
| 270 | { | ||
| 271 | ExitWithLastError(hr, "Failed to write data to ca script."); | ||
| 272 | } | ||
| 273 | |||
| 274 | cbTotalWritten += cbWritten; | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | cbWrite = lstrlenW(wzValue) * sizeof(WCHAR); | ||
| 279 | cbTotalWritten = 0; | ||
| 280 | while (cbTotalWritten < cbWrite) | ||
| 281 | { | ||
| 282 | DWORD cbWritten = 0; | ||
| 283 | if (!::WriteFile(hScript->hScriptFile, reinterpret_cast<const BYTE*>(wzValue) + cbTotalWritten, cbWrite - cbTotalWritten, &cbWritten, NULL)) | ||
| 284 | { | ||
| 285 | ExitWithLastError(hr, "Failed to write data to ca script."); | ||
| 286 | } | ||
| 287 | |||
| 288 | cbTotalWritten += cbWritten; | ||
| 289 | } | ||
| 290 | |||
| 291 | LExit: | ||
| 292 | return hr; | ||
| 293 | } | ||
| 294 | |||
| 295 | |||
| 296 | /******************************************************************** | ||
| 297 | WcaCaScriptWriteNumber() - writes a number to the ca script. | ||
| 298 | |||
| 299 | ********************************************************************/ | ||
| 300 | extern "C" HRESULT WIXAPI WcaCaScriptWriteNumber( | ||
| 301 | __in WCA_CASCRIPT_HANDLE hScript, | ||
| 302 | __in DWORD dwValue | ||
| 303 | ) | ||
| 304 | { | ||
| 305 | HRESULT hr = S_OK; | ||
| 306 | WCHAR wzBuffer[13] = { 0 }; | ||
| 307 | |||
| 308 | hr = ::StringCchPrintfW(wzBuffer, countof(wzBuffer), L"%u", dwValue); | ||
| 309 | ExitOnFailure(hr, "Failed to convert number into string."); | ||
| 310 | |||
| 311 | hr = WcaCaScriptWriteString(hScript, wzBuffer); | ||
| 312 | ExitOnFailure(hr, "Failed to write number to script."); | ||
| 313 | |||
| 314 | LExit: | ||
| 315 | return hr; | ||
| 316 | } | ||
| 317 | |||
| 318 | |||
| 319 | /******************************************************************** | ||
| 320 | WcaCaScriptFlush() - best effort function to get script written to | ||
| 321 | disk. | ||
| 322 | |||
| 323 | ********************************************************************/ | ||
| 324 | extern "C" void WIXAPI WcaCaScriptFlush( | ||
| 325 | __in WCA_CASCRIPT_HANDLE hScript | ||
| 326 | ) | ||
| 327 | { | ||
| 328 | ::FlushFileBuffers(hScript->hScriptFile); | ||
| 329 | } | ||
| 330 | |||
| 331 | |||
| 332 | /******************************************************************** | ||
| 333 | WcaCaScriptCleanup() - best effort clean-up of any cascripts left | ||
| 334 | over from this install/uninstall. | ||
| 335 | |||
| 336 | ********************************************************************/ | ||
| 337 | extern "C" void WIXAPI WcaCaScriptCleanup( | ||
| 338 | __in_z LPCWSTR wzProductCode, | ||
| 339 | __in BOOL fImpersonated | ||
| 340 | ) | ||
| 341 | { | ||
| 342 | HRESULT hr = S_OK; | ||
| 343 | WCHAR wzTempPath[MAX_PATH]; | ||
| 344 | LPWSTR pwzWildCardPath = NULL; | ||
| 345 | WIN32_FIND_DATAW fd = { 0 }; | ||
| 346 | HANDLE hff = INVALID_HANDLE_VALUE; | ||
| 347 | LPWSTR pwzDeletePath = NULL; | ||
| 348 | |||
| 349 | if (fImpersonated) | ||
| 350 | { | ||
| 351 | if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) | ||
| 352 | { | ||
| 353 | ExitWithLastError(hr, "Failed to get temp path."); | ||
| 354 | } | ||
| 355 | } | ||
| 356 | else | ||
| 357 | { | ||
| 358 | if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath))) | ||
| 359 | { | ||
| 360 | ExitWithLastError(hr, "Failed to get windows path."); | ||
| 361 | } | ||
| 362 | |||
| 363 | hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"\\Installer\\"); | ||
| 364 | ExitOnFailure(hr, "Failed to concat Installer directory on windows path string."); | ||
| 365 | } | ||
| 366 | |||
| 367 | hr = StrAllocFormatted(&pwzWildCardPath, L"%swix%s.*.???", wzTempPath, wzProductCode); | ||
| 368 | ExitOnFailure(hr, "Failed to allocate wildcard path to ca scripts."); | ||
| 369 | |||
| 370 | hff = ::FindFirstFileW(pwzWildCardPath, &fd); | ||
| 371 | if (INVALID_HANDLE_VALUE == hff) | ||
| 372 | { | ||
| 373 | ExitWithLastError(hr, "Failed to find files with pattern: %ls", pwzWildCardPath); | ||
| 374 | } | ||
| 375 | |||
| 376 | do | ||
| 377 | { | ||
| 378 | hr = StrAllocFormatted(&pwzDeletePath, L"%s%s", wzTempPath, fd.cFileName); | ||
| 379 | if (SUCCEEDED(hr)) | ||
| 380 | { | ||
| 381 | if (!::DeleteFileW(pwzDeletePath)) | ||
| 382 | { | ||
| 383 | DWORD er = ::GetLastError(); | ||
| 384 | WcaLog(LOGMSG_VERBOSE, "Failed to clean up CAScript file: %ls, er: %d", fd.cFileName, er); | ||
| 385 | } | ||
| 386 | } | ||
| 387 | else | ||
| 388 | { | ||
| 389 | WcaLog(LOGMSG_VERBOSE, "Failed to allocate path to clean up CAScript file: %ls, hr: 0x%x", fd.cFileName, hr); | ||
| 390 | } | ||
| 391 | } while(::FindNextFileW(hff, &fd)); | ||
| 392 | |||
| 393 | LExit: | ||
| 394 | if (INVALID_HANDLE_VALUE == hff) | ||
| 395 | { | ||
| 396 | ::FindClose(hff); | ||
| 397 | } | ||
| 398 | |||
| 399 | ReleaseStr(pwzDeletePath); | ||
| 400 | ReleaseStr(pwzWildCardPath); | ||
| 401 | return; | ||
| 402 | } | ||
| 403 | |||
| 404 | |||
| 405 | static HRESULT CaScriptFileName( | ||
| 406 | __in WCA_ACTION action, | ||
| 407 | __in WCA_CASCRIPT script, | ||
| 408 | __in BOOL fImpersonated, | ||
| 409 | __in_z LPCWSTR wzScriptKey, | ||
| 410 | __out LPWSTR* ppwzScriptName | ||
| 411 | ) | ||
| 412 | { | ||
| 413 | HRESULT hr = S_OK; | ||
| 414 | WCHAR wzTempPath[MAX_PATH]; | ||
| 415 | LPWSTR pwzProductCode = NULL; | ||
| 416 | WCHAR chInstallOrUninstall = action == WCA_ACTION_INSTALL ? L'i' : L'u'; | ||
| 417 | WCHAR chScheduledOrRollback = script == WCA_CASCRIPT_SCHEDULED ? L's' : L'r'; | ||
| 418 | WCHAR chUserOrMachine = fImpersonated ? L'u' : L'm'; | ||
| 419 | |||
| 420 | if (fImpersonated) | ||
| 421 | { | ||
| 422 | if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) | ||
| 423 | { | ||
| 424 | ExitWithLastError(hr, "Failed to get temp path."); | ||
| 425 | } | ||
| 426 | } | ||
| 427 | else | ||
| 428 | { | ||
| 429 | if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath))) | ||
| 430 | { | ||
| 431 | ExitWithLastError(hr, "Failed to get windows path."); | ||
| 432 | } | ||
| 433 | |||
| 434 | hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"\\Installer\\"); | ||
| 435 | ExitOnFailure(hr, "Failed to concat Installer directory on windows path string."); | ||
| 436 | } | ||
| 437 | |||
| 438 | hr = WcaGetProperty(L"ProductCode", &pwzProductCode); | ||
| 439 | ExitOnFailure(hr, "Failed to get ProductCode."); | ||
| 440 | |||
| 441 | hr = StrAllocFormatted(ppwzScriptName, L"%swix%s.%s.%c%c%c", wzTempPath, pwzProductCode, wzScriptKey, chScheduledOrRollback, chUserOrMachine, chInstallOrUninstall); | ||
| 442 | ExitOnFailure(hr, "Failed to allocate path to ca script."); | ||
| 443 | |||
| 444 | LExit: | ||
| 445 | ReleaseStr(pwzProductCode); | ||
| 446 | return hr; | ||
| 447 | } | ||
diff --git a/src/wcautil/wcautil.cpp b/src/wcautil/wcautil.cpp new file mode 100644 index 00000000..ce5ef151 --- /dev/null +++ b/src/wcautil/wcautil.cpp | |||
| @@ -0,0 +1,216 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | #include "precomp.h" | ||
| 4 | |||
| 5 | // globals | ||
| 6 | HMODULE g_hInstCADLL; | ||
| 7 | |||
| 8 | // statics | ||
| 9 | static BOOL s_fInitialized; | ||
| 10 | static MSIHANDLE s_hInstall; | ||
| 11 | static MSIHANDLE s_hDatabase; | ||
| 12 | static char s_szCustomActionLogName[32]; | ||
| 13 | static UINT s_iRetVal; | ||
| 14 | |||
| 15 | |||
| 16 | /******************************************************************** | ||
| 17 | WcaGlobalInitialize() - initializes the Wca library, should be | ||
| 18 | called once per custom action Dll during | ||
| 19 | DllMain on DLL_PROCESS_ATTACH | ||
| 20 | |||
| 21 | ********************************************************************/ | ||
| 22 | extern "C" void WIXAPI WcaGlobalInitialize( | ||
| 23 | __in HINSTANCE hInst | ||
| 24 | ) | ||
| 25 | { | ||
| 26 | g_hInstCADLL = hInst; | ||
| 27 | MemInitialize(); | ||
| 28 | |||
| 29 | AssertSetModule(g_hInstCADLL); | ||
| 30 | AssertSetDisplayFunction(WcaDisplayAssert); | ||
| 31 | } | ||
| 32 | |||
| 33 | |||
| 34 | /******************************************************************** | ||
| 35 | WcaGlobalFinalize() - finalizes the Wca library, should be the | ||
| 36 | called once per custom action Dll during | ||
| 37 | DllMain on DLL_PROCESS_DETACH | ||
| 38 | |||
| 39 | ********************************************************************/ | ||
| 40 | extern "C" void WIXAPI WcaGlobalFinalize() | ||
| 41 | { | ||
| 42 | #ifdef DEBUG | ||
| 43 | if (WcaIsInitialized()) | ||
| 44 | { | ||
| 45 | CHAR szBuf[2048]; | ||
| 46 | StringCchPrintfA(szBuf, countof(szBuf), "CustomAction %s called WcaInitialize() but not WcaFinalize()", WcaGetLogName()); | ||
| 47 | |||
| 48 | AssertSz(FALSE, szBuf); | ||
| 49 | } | ||
| 50 | #endif | ||
| 51 | MemUninitialize(); | ||
| 52 | g_hInstCADLL = NULL; | ||
| 53 | } | ||
| 54 | |||
| 55 | |||
| 56 | /******************************************************************** | ||
| 57 | WcaInitialize() - initializes the Wca framework, should be the first | ||
| 58 | thing called by all CustomActions | ||
| 59 | |||
| 60 | ********************************************************************/ | ||
| 61 | extern "C" HRESULT WIXAPI WcaInitialize( | ||
| 62 | __in MSIHANDLE hInstall, | ||
| 63 | __in_z PCSTR szCustomActionLogName | ||
| 64 | ) | ||
| 65 | { | ||
| 66 | WCHAR wzCAFileName[MAX_PATH] = {0}; | ||
| 67 | DWORD dwMajorVersion = 0; | ||
| 68 | DWORD dwMinorVersion = 0; | ||
| 69 | |||
| 70 | // these statics should be called once per CustomAction invocation. | ||
| 71 | // Darwin does doesn't preserve DLL state across CustomAction calls so | ||
| 72 | // these should always be initialized to NULL. If that behavior changes | ||
| 73 | // we would need to do a careful review of all of our module/global data. | ||
| 74 | AssertSz(!s_fInitialized, "WcaInitialize() should only be called once per CustomAction"); | ||
| 75 | Assert(NULL == s_hInstall); | ||
| 76 | Assert(NULL == s_hDatabase); | ||
| 77 | Assert(0 == *s_szCustomActionLogName); | ||
| 78 | |||
| 79 | HRESULT hr = S_OK; | ||
| 80 | |||
| 81 | s_fInitialized = TRUE; | ||
| 82 | s_iRetVal = ERROR_SUCCESS; // assume all will go well | ||
| 83 | |||
| 84 | s_hInstall = hInstall; | ||
| 85 | s_hDatabase = ::MsiGetActiveDatabase(s_hInstall); // may return null if deferred CustomAction | ||
| 86 | |||
| 87 | hr = ::StringCchCopy(s_szCustomActionLogName, countof(s_szCustomActionLogName), szCustomActionLogName); | ||
| 88 | ExitOnFailure(hr, "Failed to copy CustomAction log name: %s", szCustomActionLogName); | ||
| 89 | |||
| 90 | // If we got the database handle IE: immediate CA | ||
| 91 | if (s_hDatabase) | ||
| 92 | { | ||
| 93 | hr = SetVerboseLoggingAtom(IsVerboseLogging()); | ||
| 94 | ExitOnFailure(hr, "Failed to set verbose logging global atom"); | ||
| 95 | } | ||
| 96 | |||
| 97 | if (!::GetModuleFileNameW(g_hInstCADLL, wzCAFileName, countof(wzCAFileName))) | ||
| 98 | { | ||
| 99 | ExitWithLastError(hr, "Failed to get module filename"); | ||
| 100 | } | ||
| 101 | |||
| 102 | FileVersion(wzCAFileName, &dwMajorVersion, &dwMinorVersion); // Ignore failure, just log 0.0.0.0 | ||
| 103 | |||
| 104 | WcaLog(LOGMSG_VERBOSE, "Entering %s in %ls, version %u.%u.%u.%u", szCustomActionLogName, wzCAFileName, (DWORD)HIWORD(dwMajorVersion), (DWORD)LOWORD(dwMajorVersion), (DWORD)HIWORD(dwMinorVersion), (DWORD)LOWORD(dwMinorVersion)); | ||
| 105 | |||
| 106 | Assert(s_hInstall); | ||
| 107 | LExit: | ||
| 108 | if (FAILED(hr)) | ||
| 109 | { | ||
| 110 | if (s_hDatabase) | ||
| 111 | { | ||
| 112 | ::MsiCloseHandle(s_hDatabase); | ||
| 113 | s_hDatabase = NULL; | ||
| 114 | } | ||
| 115 | |||
| 116 | s_hInstall = NULL; | ||
| 117 | s_fInitialized = FALSE; | ||
| 118 | } | ||
| 119 | |||
| 120 | return hr; | ||
| 121 | } | ||
| 122 | |||
| 123 | |||
| 124 | /******************************************************************** | ||
| 125 | WcaFinalize() - cleans up after the Wca framework, should be the last | ||
| 126 | thing called by all CustomActions | ||
| 127 | |||
| 128 | ********************************************************************/ | ||
| 129 | extern "C" UINT WIXAPI WcaFinalize( | ||
| 130 | __in UINT iReturnValue | ||
| 131 | ) | ||
| 132 | { | ||
| 133 | AssertSz(!WcaIsWow64Initialized(), "WcaFinalizeWow64() should be called before calling WcaFinalize()"); | ||
| 134 | |||
| 135 | // clean up after our initialization | ||
| 136 | if (s_hDatabase) | ||
| 137 | { | ||
| 138 | ::MsiCloseHandle(s_hDatabase); | ||
| 139 | s_hDatabase = NULL; | ||
| 140 | } | ||
| 141 | |||
| 142 | s_hInstall = NULL; | ||
| 143 | s_fInitialized = FALSE; | ||
| 144 | |||
| 145 | // if no error occurred during the processing of the CusotmAction return the passed in return value | ||
| 146 | // otherwise return the previous failure | ||
| 147 | return (ERROR_SUCCESS == s_iRetVal) ? iReturnValue : s_iRetVal; | ||
| 148 | } | ||
| 149 | |||
| 150 | |||
| 151 | /******************************************************************** | ||
| 152 | WcaIsInitialized() - determines if WcaInitialize() has been called | ||
| 153 | |||
| 154 | ********************************************************************/ | ||
| 155 | extern "C" BOOL WIXAPI WcaIsInitialized() | ||
| 156 | { | ||
| 157 | return s_fInitialized; | ||
| 158 | } | ||
| 159 | |||
| 160 | |||
| 161 | /******************************************************************** | ||
| 162 | WcaGetInstallHandle() - gets the handle to the active install session | ||
| 163 | |||
| 164 | ********************************************************************/ | ||
| 165 | extern "C" MSIHANDLE WIXAPI WcaGetInstallHandle() | ||
| 166 | { | ||
| 167 | AssertSz(s_hInstall, "WcaInitialize() should be called before attempting to access the install handle."); | ||
| 168 | return s_hInstall; | ||
| 169 | } | ||
| 170 | |||
| 171 | |||
| 172 | /******************************************************************** | ||
| 173 | WcaGetDatabaseHandle() - gets the handle to the active database | ||
| 174 | |||
| 175 | NOTE: this function can only be used in immediate CustomActions. | ||
| 176 | Deferred CustomActions do not have access to the active | ||
| 177 | database. | ||
| 178 | ********************************************************************/ | ||
| 179 | extern "C" MSIHANDLE WIXAPI WcaGetDatabaseHandle() | ||
| 180 | { | ||
| 181 | AssertSz(s_hDatabase, "WcaInitialize() should be called before attempting to access the install handle. Also note that deferred CustomActions do not have access to the active database."); | ||
| 182 | return s_hDatabase; | ||
| 183 | } | ||
| 184 | |||
| 185 | |||
| 186 | /******************************************************************** | ||
| 187 | WcaGetLogName() - gets the name of the CustomAction used in logging | ||
| 188 | |||
| 189 | ********************************************************************/ | ||
| 190 | extern "C" const char* WIXAPI WcaGetLogName() | ||
| 191 | { | ||
| 192 | return s_szCustomActionLogName; | ||
| 193 | } | ||
| 194 | |||
| 195 | |||
| 196 | /******************************************************************** | ||
| 197 | WcaSetReturnValue() - sets the value to return from the CustomAction | ||
| 198 | |||
| 199 | ********************************************************************/ | ||
| 200 | extern "C" void WIXAPI WcaSetReturnValue( | ||
| 201 | __in UINT iReturnValue | ||
| 202 | ) | ||
| 203 | { | ||
| 204 | s_iRetVal = iReturnValue; | ||
| 205 | } | ||
| 206 | |||
| 207 | |||
| 208 | /******************************************************************** | ||
| 209 | WcaCancelDetected() - determines if the user has canceled yet | ||
| 210 | |||
| 211 | NOTE: returns true when WcaSetReturnValue() is set to ERROR_INSTALL_USEREXIT | ||
| 212 | ********************************************************************/ | ||
| 213 | extern "C" BOOL WIXAPI WcaCancelDetected() | ||
| 214 | { | ||
| 215 | return ERROR_INSTALL_USEREXIT == s_iRetVal; | ||
| 216 | } | ||
diff --git a/src/wcautil/wcautil.nuspec b/src/wcautil/wcautil.nuspec new file mode 100644 index 00000000..3d1d2722 --- /dev/null +++ b/src/wcautil/wcautil.nuspec | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | <?xml version="1.0"?> | ||
| 2 | <package > | ||
| 3 | <metadata> | ||
| 4 | <id>$id$</id> | ||
| 5 | <version>$version$</version> | ||
| 6 | <authors>$authors$</authors> | ||
| 7 | <owners>$authors$</owners> | ||
| 8 | <licenseUrl>https://github.com/wixtoolset/wcautil/blob/master/LICENSE.TXT</licenseUrl> | ||
| 9 | <projectUrl>https://github.com/wixtoolset/wcautil</projectUrl> | ||
| 10 | <requireLicenseAcceptance>false</requireLicenseAcceptance> | ||
| 11 | <description>$description$</description> | ||
| 12 | <copyright>$copyright$</copyright> | ||
| 13 | </metadata> | ||
| 14 | |||
| 15 | <files> | ||
| 16 | <file src="build\$id$.props" target="build\" /> | ||
| 17 | <file src="inc\*" target="build\native\include" /> | ||
| 18 | <file src="..\..\build\$configuration$\v140_xp\x64\wcautil.lib" target="build\native\lib\v140\x64" /> | ||
| 19 | <file src="..\..\build\$configuration$\v140_xp\x86\wcautil.lib" target="build\native\lib\v140\x86" /> | ||
| 20 | <file src="..\..\build\$configuration$\v141_xp\x64\wcautil.lib" target="build\native\lib\v141\x64" /> | ||
| 21 | <file src="..\..\build\$configuration$\v141_xp\x86\wcautil.lib" target="build\native\lib\v141\x86" /> | ||
| 22 | </files> | ||
| 23 | </package> | ||
diff --git a/src/wcautil/wcautil.vcxproj b/src/wcautil/wcautil.vcxproj new file mode 100644 index 00000000..61bd9437 --- /dev/null +++ b/src/wcautil/wcautil.vcxproj | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 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. --> | ||
| 3 | <Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
| 4 | <Import Project="..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props')" /> | ||
| 5 | <ItemGroup Label="ProjectConfigurations"> | ||
| 6 | <ProjectConfiguration Include="Debug|Win32"> | ||
| 7 | <Configuration>Debug</Configuration> | ||
| 8 | <Platform>Win32</Platform> | ||
| 9 | </ProjectConfiguration> | ||
| 10 | <ProjectConfiguration Include="Release|Win32"> | ||
| 11 | <Configuration>Release</Configuration> | ||
| 12 | <Platform>Win32</Platform> | ||
| 13 | </ProjectConfiguration> | ||
| 14 | <ProjectConfiguration Include="Debug|x64"> | ||
| 15 | <Configuration>Debug</Configuration> | ||
| 16 | <Platform>x64</Platform> | ||
| 17 | </ProjectConfiguration> | ||
| 18 | <ProjectConfiguration Include="Release|x64"> | ||
| 19 | <Configuration>Release</Configuration> | ||
| 20 | <Platform>x64</Platform> | ||
| 21 | </ProjectConfiguration> | ||
| 22 | </ItemGroup> | ||
| 23 | <PropertyGroup Label="Globals"> | ||
| 24 | <ProjectGuid>{5B3714B6-3A76-463E-8595-D48DA276C512}</ProjectGuid> | ||
| 25 | <ConfigurationType>StaticLibrary</ConfigurationType> | ||
| 26 | <TargetName>wcautil</TargetName> | ||
| 27 | <MultiTargetLibrary>true</MultiTargetLibrary> | ||
| 28 | <PlatformToolset>v141_xp</PlatformToolset> | ||
| 29 | <CharacterSet>MultiByte</CharacterSet> | ||
| 30 | <Description>WiX Toolset Custom Action native utility library</Description> | ||
| 31 | </PropertyGroup> | ||
| 32 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | ||
| 33 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> | ||
| 34 | <ImportGroup Label="Shared"> | ||
| 35 | <Import Project="..\..\packages\Nerdbank.GitVersioning.2.0.37-beta\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.2.0.37-beta\build\Nerdbank.GitVersioning.targets')" /> | ||
| 36 | </ImportGroup> | ||
| 37 | <Import Project="..\NativeMultiTargeting.Build.props" /> | ||
| 38 | <ItemGroup> | ||
| 39 | <ClCompile Include="exbinary.cpp" /> | ||
| 40 | <ClCompile Include="wcalog.cpp" /> | ||
| 41 | <ClCompile Include="wcascript.cpp" /> | ||
| 42 | <ClCompile Include="wcautil.cpp"> | ||
| 43 | <PrecompiledHeader>Create</PrecompiledHeader> | ||
| 44 | </ClCompile> | ||
| 45 | <ClCompile Include="wcawrapquery.cpp" /> | ||
| 46 | <ClCompile Include="wcawow64.cpp" /> | ||
| 47 | <ClCompile Include="wcawrap.cpp" /> | ||
| 48 | <ClCompile Include="qtexec.cpp" /> | ||
| 49 | </ItemGroup> | ||
| 50 | <ItemGroup Condition="'$(Platform)' == 'Win32'"> | ||
| 51 | <ClInclude Include="custommsierrors.h"> | ||
| 52 | <GenerateWixInclude>caerr.wxi</GenerateWixInclude> | ||
| 53 | </ClInclude> | ||
| 54 | </ItemGroup> | ||
| 55 | <ItemGroup Condition="'$(Platform)' != 'Win32'"> | ||
| 56 | <ClInclude Include="custommsierrors.h" /> | ||
| 57 | </ItemGroup> | ||
| 58 | <ItemGroup> | ||
| 59 | <ClInclude Include="precomp.h" /> | ||
| 60 | <ClInclude Include="inc\wcalog.h" /> | ||
| 61 | <ClInclude Include="inc\wcautil.h" /> | ||
| 62 | <ClInclude Include="inc\wcawow64.h" /> | ||
| 63 | <ClInclude Include="inc\wcawrapquery.h" /> | ||
| 64 | </ItemGroup> | ||
| 65 | <ItemGroup> | ||
| 66 | <None Include="packages.config" /> | ||
| 67 | </ItemGroup> | ||
| 68 | <Target Name="PackNativeNuget" DependsOnTargets="GetBuildVersion"> | ||
| 69 | <Exec Command="nuget pack wcautil.nuspec -OutputDirectory $(BaseOutputPath) -Properties Configuration=$(Configuration);Id=WixToolset.WcaUtil;Version="$(BuildVersionSimple)";Authors="$(Authors)";Copyright="$(Copyright)";Description="$(Description)";Title="$(Title)"" /> | ||
| 70 | </Target> | ||
| 71 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | ||
| 72 | <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> | ||
| 73 | <PropertyGroup> | ||
| 74 | <ErrorText>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}.</ErrorText> | ||
| 75 | </PropertyGroup> | ||
| 76 | <Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.2.0.37-beta\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.2.0.37-beta\build\Nerdbank.GitVersioning.targets'))" /> | ||
| 77 | <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props'))" /> | ||
| 78 | </Target> | ||
| 79 | </Project> \ No newline at end of file | ||
diff --git a/src/wcautil/wcawow64.cpp b/src/wcautil/wcawow64.cpp new file mode 100644 index 00000000..8174c43e --- /dev/null +++ b/src/wcautil/wcawow64.cpp | |||
| @@ -0,0 +1,169 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | #include "precomp.h" | ||
| 4 | |||
| 5 | static HMODULE s_hKernel32; | ||
| 6 | static BOOL s_fWow64Initialized; | ||
| 7 | static BOOL (*s_pfnDisableWow64)(__out PVOID* ); | ||
| 8 | static BOOL (*s_pfnRevertWow64)(__in PVOID ); | ||
| 9 | static BOOL (*s_pfnIsWow64Process) (HANDLE, PBOOL); | ||
| 10 | static PVOID s_Wow64FSRevertState; | ||
| 11 | static BOOL s_fWow64FSDisabled; | ||
| 12 | |||
| 13 | /******************************************************************** | ||
| 14 | WcaInitializeWow64() - Initializes the Wow64 API | ||
| 15 | |||
| 16 | ********************************************************************/ | ||
| 17 | extern "C" HRESULT WIXAPI WcaInitializeWow64() | ||
| 18 | { | ||
| 19 | AssertSz(WcaIsInitialized(), "WcaInitialize() should be called before calling WcaInitializeWow64()"); | ||
| 20 | AssertSz(!WcaIsWow64Initialized(), "WcaInitializeWow64() should not be called twice without calling WcaFinalizeWow64()"); | ||
| 21 | |||
| 22 | s_fWow64Initialized = FALSE; | ||
| 23 | HRESULT hr = S_OK; | ||
| 24 | s_Wow64FSRevertState = NULL; | ||
| 25 | s_fWow64FSDisabled = false; | ||
| 26 | |||
| 27 | // Test if we have access to the Wow64 API, and store the result in bWow64APIPresent | ||
| 28 | s_hKernel32 = ::GetModuleHandleW(L"kernel32.dll"); | ||
| 29 | if (!s_hKernel32) | ||
| 30 | { | ||
| 31 | ExitWithLastError(hr, "failed to get handle to kernel32.dll"); | ||
| 32 | } | ||
| 33 | |||
| 34 | // This will test if we have access to the Wow64 API | ||
| 35 | s_pfnIsWow64Process = (BOOL (*)(HANDLE, PBOOL))::GetProcAddress(s_hKernel32, "IsWow64Process"); | ||
| 36 | if (NULL != s_pfnIsWow64Process) | ||
| 37 | { | ||
| 38 | s_pfnDisableWow64 = (BOOL (*)(PVOID *))::GetProcAddress(s_hKernel32, "Wow64DisableWow64FsRedirection"); | ||
| 39 | // If we fail, log the error but proceed, because we may not need a particular function, or the Wow64 API at all | ||
| 40 | if (!s_pfnDisableWow64) | ||
| 41 | { | ||
| 42 | return S_FALSE; | ||
| 43 | } | ||
| 44 | |||
| 45 | s_pfnRevertWow64 = (BOOL (*)(PVOID))::GetProcAddress(s_hKernel32, "Wow64RevertWow64FsRedirection"); | ||
| 46 | if (!s_pfnRevertWow64) | ||
| 47 | { | ||
| 48 | return S_FALSE; | ||
| 49 | } | ||
| 50 | |||
| 51 | if (s_pfnDisableWow64 && s_pfnRevertWow64) | ||
| 52 | { | ||
| 53 | s_fWow64Initialized = TRUE; | ||
| 54 | } | ||
| 55 | } | ||
| 56 | else | ||
| 57 | { | ||
| 58 | return S_FALSE; | ||
| 59 | } | ||
| 60 | |||
| 61 | LExit: | ||
| 62 | |||
| 63 | return hr; | ||
| 64 | } | ||
| 65 | |||
| 66 | /******************************************************************** | ||
| 67 | WcaIsWow64Process() - determines if the current process is running | ||
| 68 | in WOW | ||
| 69 | |||
| 70 | ********************************************************************/ | ||
| 71 | extern "C" BOOL WIXAPI WcaIsWow64Process() | ||
| 72 | { | ||
| 73 | BOOL fIsWow64Process = FALSE; | ||
| 74 | if (s_fWow64Initialized) | ||
| 75 | { | ||
| 76 | if (!s_pfnIsWow64Process(GetCurrentProcess(), &fIsWow64Process)) | ||
| 77 | { | ||
| 78 | // clear out the value since call failed | ||
| 79 | fIsWow64Process = FALSE; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | return fIsWow64Process; | ||
| 83 | } | ||
| 84 | |||
| 85 | /******************************************************************** | ||
| 86 | WcaIsWow64Initialized() - determines if WcaInitializeWow64() has | ||
| 87 | been successfully called | ||
| 88 | |||
| 89 | ********************************************************************/ | ||
| 90 | extern "C" BOOL WIXAPI WcaIsWow64Initialized() | ||
| 91 | { | ||
| 92 | return s_fWow64Initialized; | ||
| 93 | } | ||
| 94 | |||
| 95 | /******************************************************************** | ||
| 96 | WcaDisableWow64FSRedirection() - Disables Wow64 FS Redirection | ||
| 97 | |||
| 98 | ********************************************************************/ | ||
| 99 | extern "C" HRESULT WIXAPI WcaDisableWow64FSRedirection() | ||
| 100 | { | ||
| 101 | AssertSz(s_fWow64Initialized && s_pfnDisableWow64 != NULL, "WcaDisableWow64FSRedirection() called, but Wow64 API was not initialized"); | ||
| 102 | |||
| 103 | #ifdef DEBUG | ||
| 104 | AssertSz(!s_fWow64FSDisabled, "You must call WcaRevertWow64FSRedirection() before calling WcaDisableWow64FSRedirection() again"); | ||
| 105 | #endif | ||
| 106 | |||
| 107 | HRESULT hr = S_OK; | ||
| 108 | if (s_pfnDisableWow64(&s_Wow64FSRevertState)) | ||
| 109 | { | ||
| 110 | s_fWow64FSDisabled = TRUE; | ||
| 111 | } | ||
| 112 | else | ||
| 113 | { | ||
| 114 | ExitWithLastError(hr, "Failed to disable WOW64."); | ||
| 115 | } | ||
| 116 | |||
| 117 | LExit: | ||
| 118 | return hr; | ||
| 119 | } | ||
| 120 | |||
| 121 | /******************************************************************** | ||
| 122 | WcaRevertWow64FSRedirection() - Reverts Wow64 FS Redirection to its | ||
| 123 | pre-disabled state | ||
| 124 | |||
| 125 | ********************************************************************/ | ||
| 126 | extern "C" HRESULT WIXAPI WcaRevertWow64FSRedirection() | ||
| 127 | { | ||
| 128 | AssertSz(s_fWow64Initialized && s_pfnDisableWow64 != NULL, "WcaRevertWow64FSRedirection() called, but Wow64 API was not initialized"); | ||
| 129 | |||
| 130 | #ifdef DEBUG | ||
| 131 | AssertSz(s_fWow64FSDisabled, "You must call WcaDisableWow64FSRedirection() before calling WcaRevertWow64FSRedirection()"); | ||
| 132 | #endif | ||
| 133 | |||
| 134 | HRESULT hr = S_OK; | ||
| 135 | if (s_pfnRevertWow64(s_Wow64FSRevertState)) | ||
| 136 | { | ||
| 137 | s_fWow64FSDisabled = FALSE; | ||
| 138 | } | ||
| 139 | else | ||
| 140 | { | ||
| 141 | ExitWithLastError(hr, "Failed to revert WOW64."); | ||
| 142 | } | ||
| 143 | |||
| 144 | LExit: | ||
| 145 | return hr; | ||
| 146 | } | ||
| 147 | |||
| 148 | /******************************************************************** | ||
| 149 | WcaFinalizeWow64() - Cleans up after Wow64 API Initialization | ||
| 150 | |||
| 151 | ********************************************************************/ | ||
| 152 | extern "C" HRESULT WIXAPI WcaFinalizeWow64() | ||
| 153 | { | ||
| 154 | if (s_fWow64FSDisabled) | ||
| 155 | { | ||
| 156 | #ifdef DEBUG | ||
| 157 | AssertSz(FALSE, "WcaFinalizeWow64() called while Filesystem redirection was disabled."); | ||
| 158 | #else | ||
| 159 | // If we aren't in debug mode, let's do our best to recover gracefully | ||
| 160 | WcaRevertWow64FSRedirection(); | ||
| 161 | #endif | ||
| 162 | } | ||
| 163 | |||
| 164 | s_fWow64Initialized = FALSE; | ||
| 165 | s_pfnDisableWow64 = NULL; | ||
| 166 | s_pfnRevertWow64 = NULL; | ||
| 167 | |||
| 168 | return S_OK; | ||
| 169 | } | ||
diff --git a/src/wcautil/wcawrap.cpp b/src/wcautil/wcawrap.cpp new file mode 100644 index 00000000..625489c1 --- /dev/null +++ b/src/wcautil/wcawrap.cpp | |||
| @@ -0,0 +1,1643 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | #include "precomp.h" | ||
| 4 | |||
| 5 | |||
| 6 | /******************************************************************** | ||
| 7 | WcaProcessMessage() - sends a message from the CustomAction | ||
| 8 | |||
| 9 | ********************************************************************/ | ||
| 10 | extern "C" UINT WIXAPI WcaProcessMessage( | ||
| 11 | __in INSTALLMESSAGE eMessageType, | ||
| 12 | __in MSIHANDLE hRecord | ||
| 13 | ) | ||
| 14 | { | ||
| 15 | UINT er = ::MsiProcessMessage(WcaGetInstallHandle(), eMessageType, hRecord); | ||
| 16 | if (ERROR_INSTALL_USEREXIT == er || IDCANCEL == er) | ||
| 17 | { | ||
| 18 | WcaSetReturnValue(ERROR_INSTALL_USEREXIT); | ||
| 19 | } | ||
| 20 | |||
| 21 | return er; | ||
| 22 | } | ||
| 23 | |||
| 24 | |||
| 25 | /******************************************************************** | ||
| 26 | WcaErrorMessage() - sends an error message from the CustomAction using | ||
| 27 | the Error table | ||
| 28 | |||
| 29 | NOTE: Any and all var_args (...) must be WCHAR* | ||
| 30 | If you pass -1 to cArgs the count will be determined | ||
| 31 | ********************************************************************/ | ||
| 32 | extern "C" UINT __cdecl WcaErrorMessage( | ||
| 33 | __in int iError, | ||
| 34 | __in HRESULT hrError, | ||
| 35 | __in UINT uiType, | ||
| 36 | __in INT cArgs, | ||
| 37 | ... | ||
| 38 | ) | ||
| 39 | { | ||
| 40 | UINT er; | ||
| 41 | MSIHANDLE hRec = NULL; | ||
| 42 | va_list args = NULL; | ||
| 43 | |||
| 44 | uiType |= INSTALLMESSAGE_ERROR; // ensure error type is set | ||
| 45 | hRec = ::MsiCreateRecord(cArgs + 2); | ||
| 46 | if (!hRec) | ||
| 47 | { | ||
| 48 | er = ERROR_OUTOFMEMORY; | ||
| 49 | ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to create record when sending error message"); | ||
| 50 | } | ||
| 51 | |||
| 52 | er = ::MsiRecordSetInteger(hRec, 1, iError); | ||
| 53 | ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to set error code into error message"); | ||
| 54 | |||
| 55 | er = ::MsiRecordSetInteger(hRec, 2, hrError); | ||
| 56 | ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to set hresult code into error message"); | ||
| 57 | |||
| 58 | va_start(args, cArgs); | ||
| 59 | if (-1 == cArgs) | ||
| 60 | { | ||
| 61 | LPCWSTR wzArg = NULL; | ||
| 62 | va_list iter = args; | ||
| 63 | cArgs = 0; | ||
| 64 | |||
| 65 | while (NULL != (wzArg = va_arg(iter, WCHAR*)) && L'\0' != *wzArg) | ||
| 66 | { | ||
| 67 | ++cArgs; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | for (INT i = 0; i < cArgs; i++) | ||
| 72 | { | ||
| 73 | er = ::MsiRecordSetStringW(hRec, i + 3, va_arg(args, WCHAR*)); | ||
| 74 | ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to set string string into error message"); | ||
| 75 | } | ||
| 76 | va_end(args); | ||
| 77 | |||
| 78 | er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(uiType), hRec); | ||
| 79 | LExit: | ||
| 80 | if (args) | ||
| 81 | { | ||
| 82 | va_end(args); | ||
| 83 | } | ||
| 84 | |||
| 85 | if (hRec) | ||
| 86 | { | ||
| 87 | ::MsiCloseHandle(hRec); | ||
| 88 | } | ||
| 89 | |||
| 90 | return er; | ||
| 91 | } | ||
| 92 | |||
| 93 | |||
| 94 | /******************************************************************** | ||
| 95 | WcaProgressMessage() - extends the progress bar or sends a progress | ||
| 96 | update from the CustomAction | ||
| 97 | |||
| 98 | ********************************************************************/ | ||
| 99 | extern "C" HRESULT WIXAPI WcaProgressMessage( | ||
| 100 | __in UINT uiCost, | ||
| 101 | __in BOOL fExtendProgressBar | ||
| 102 | ) | ||
| 103 | { | ||
| 104 | static BOOL fExplicitProgressMessages = FALSE; | ||
| 105 | |||
| 106 | HRESULT hr = S_OK; | ||
| 107 | UINT er = ERROR_SUCCESS; | ||
| 108 | MSIHANDLE hRec = ::MsiCreateRecord(3); | ||
| 109 | |||
| 110 | // if aren't extending the progress bar and we haven't switched into explicit message mode | ||
| 111 | if (!fExtendProgressBar && !fExplicitProgressMessages) | ||
| 112 | { | ||
| 113 | AssertSz(::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_SCHEDULED) || | ||
| 114 | ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_COMMIT) || | ||
| 115 | ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK), "can only send progress bar messages in a deferred CustomAction"); | ||
| 116 | |||
| 117 | // tell Darwin to use explicit progress messages | ||
| 118 | ::MsiRecordSetInteger(hRec, 1, 1); | ||
| 119 | ::MsiRecordSetInteger(hRec, 2, 1); | ||
| 120 | ::MsiRecordSetInteger(hRec, 3, 0); | ||
| 121 | |||
| 122 | er = WcaProcessMessage(INSTALLMESSAGE_PROGRESS, hRec); | ||
| 123 | if (0 == er || IDOK == er || IDYES == er) | ||
| 124 | { | ||
| 125 | hr = S_OK; | ||
| 126 | } | ||
| 127 | else if (IDABORT == er || IDCANCEL == er) | ||
| 128 | { | ||
| 129 | WcaSetReturnValue(ERROR_INSTALL_USEREXIT); // note that the user said exit | ||
| 130 | ExitFunction1(hr = S_FALSE); | ||
| 131 | } | ||
| 132 | else | ||
| 133 | { | ||
| 134 | hr = E_UNEXPECTED; | ||
| 135 | } | ||
| 136 | ExitOnFailure(hr, "failed to tell Darwin to use explicit progress messages"); | ||
| 137 | |||
| 138 | fExplicitProgressMessages = TRUE; | ||
| 139 | } | ||
| 140 | #if DEBUG | ||
| 141 | else if (fExtendProgressBar) // if we are extending the progress bar, make sure we're not deferred | ||
| 142 | { | ||
| 143 | AssertSz(!::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_SCHEDULED), "cannot add ticks to progress bar length from deferred CustomAction"); | ||
| 144 | } | ||
| 145 | #endif | ||
| 146 | |||
| 147 | // send the progress message | ||
| 148 | ::MsiRecordSetInteger(hRec, 1, (fExtendProgressBar) ? 3 : 2); | ||
| 149 | ::MsiRecordSetInteger(hRec, 2, uiCost); | ||
| 150 | ::MsiRecordSetInteger(hRec, 3, 0); | ||
| 151 | |||
| 152 | er = WcaProcessMessage(INSTALLMESSAGE_PROGRESS, hRec); | ||
| 153 | if (0 == er || IDOK == er || IDYES == er) | ||
| 154 | { | ||
| 155 | hr = S_OK; | ||
| 156 | } | ||
| 157 | else if (IDABORT == er || IDCANCEL == er) | ||
| 158 | { | ||
| 159 | WcaSetReturnValue(ERROR_INSTALL_USEREXIT); // note that the user said exit | ||
| 160 | hr = S_FALSE; | ||
| 161 | } | ||
| 162 | else | ||
| 163 | { | ||
| 164 | hr = E_UNEXPECTED; | ||
| 165 | } | ||
| 166 | |||
| 167 | LExit: | ||
| 168 | if (hRec) | ||
| 169 | { | ||
| 170 | ::MsiCloseHandle(hRec); | ||
| 171 | } | ||
| 172 | |||
| 173 | return hr; | ||
| 174 | } | ||
| 175 | |||
| 176 | |||
| 177 | /******************************************************************** | ||
| 178 | WcaIsInstalling() - determines if a pair of installstates means install | ||
| 179 | |||
| 180 | ********************************************************************/ | ||
| 181 | extern "C" BOOL WIXAPI WcaIsInstalling( | ||
| 182 | __in INSTALLSTATE isInstalled, | ||
| 183 | __in INSTALLSTATE isAction | ||
| 184 | ) | ||
| 185 | { | ||
| 186 | return (INSTALLSTATE_LOCAL == isAction || | ||
| 187 | INSTALLSTATE_SOURCE == isAction || | ||
| 188 | (INSTALLSTATE_DEFAULT == isAction && | ||
| 189 | (INSTALLSTATE_LOCAL == isInstalled || | ||
| 190 | INSTALLSTATE_SOURCE == isInstalled))); | ||
| 191 | } | ||
| 192 | |||
| 193 | /******************************************************************** | ||
| 194 | WcaIsReInstalling() - determines if a pair of installstates means reinstall | ||
| 195 | |||
| 196 | ********************************************************************/ | ||
| 197 | extern "C" BOOL WIXAPI WcaIsReInstalling( | ||
| 198 | __in INSTALLSTATE isInstalled, | ||
| 199 | __in INSTALLSTATE isAction | ||
| 200 | ) | ||
| 201 | { | ||
| 202 | return ((INSTALLSTATE_LOCAL == isAction || | ||
| 203 | INSTALLSTATE_SOURCE == isAction || | ||
| 204 | INSTALLSTATE_DEFAULT == isAction) && | ||
| 205 | (INSTALLSTATE_LOCAL == isInstalled || | ||
| 206 | INSTALLSTATE_SOURCE == isInstalled)); | ||
| 207 | } | ||
| 208 | |||
| 209 | |||
| 210 | /******************************************************************** | ||
| 211 | WcaIsUninstalling() - determines if a pair of installstates means uninstall | ||
| 212 | |||
| 213 | ********************************************************************/ | ||
| 214 | extern "C" BOOL WIXAPI WcaIsUninstalling( | ||
| 215 | __in INSTALLSTATE isInstalled, | ||
| 216 | __in INSTALLSTATE isAction | ||
| 217 | ) | ||
| 218 | { | ||
| 219 | return ((INSTALLSTATE_ABSENT == isAction || | ||
| 220 | INSTALLSTATE_REMOVED == isAction) && | ||
| 221 | (INSTALLSTATE_LOCAL == isInstalled || | ||
| 222 | INSTALLSTATE_SOURCE == isInstalled)); | ||
| 223 | } | ||
| 224 | |||
| 225 | |||
| 226 | /******************************************************************** | ||
| 227 | WcaGetComponentToDo() - gets a component's install states and | ||
| 228 | determines if they mean install, uninstall, or reinstall. | ||
| 229 | ********************************************************************/ | ||
| 230 | extern "C" WCA_TODO WIXAPI WcaGetComponentToDo( | ||
| 231 | __in_z LPCWSTR wzComponentId | ||
| 232 | ) | ||
| 233 | { | ||
| 234 | INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; | ||
| 235 | INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; | ||
| 236 | if (ERROR_SUCCESS != ::MsiGetComponentStateW(WcaGetInstallHandle(), wzComponentId, &isInstalled, &isAction)) | ||
| 237 | { | ||
| 238 | return WCA_TODO_UNKNOWN; | ||
| 239 | } | ||
| 240 | |||
| 241 | if (WcaIsReInstalling(isInstalled, isAction)) | ||
| 242 | { | ||
| 243 | return WCA_TODO_REINSTALL; | ||
| 244 | } | ||
| 245 | else if (WcaIsUninstalling(isInstalled, isAction)) | ||
| 246 | { | ||
| 247 | return WCA_TODO_UNINSTALL; | ||
| 248 | } | ||
| 249 | else if (WcaIsInstalling(isInstalled, isAction)) | ||
| 250 | { | ||
| 251 | return WCA_TODO_INSTALL; | ||
| 252 | } | ||
| 253 | else | ||
| 254 | { | ||
| 255 | return WCA_TODO_UNKNOWN; | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | |||
| 260 | /******************************************************************** | ||
| 261 | WcaSetComponentState() - sets the install state of a Component | ||
| 262 | |||
| 263 | ********************************************************************/ | ||
| 264 | extern "C" HRESULT WIXAPI WcaSetComponentState( | ||
| 265 | __in_z LPCWSTR wzComponent, | ||
| 266 | __in INSTALLSTATE isState | ||
| 267 | ) | ||
| 268 | { | ||
| 269 | UINT er = ::MsiSetComponentStateW(WcaGetInstallHandle(), wzComponent, isState); | ||
| 270 | if (ERROR_INSTALL_USEREXIT == er) | ||
| 271 | { | ||
| 272 | WcaSetReturnValue(er); | ||
| 273 | } | ||
| 274 | |||
| 275 | return HRESULT_FROM_WIN32(er); | ||
| 276 | } | ||
| 277 | |||
| 278 | |||
| 279 | /******************************************************************** | ||
| 280 | WcaTableExists() - determines if installing database contains a table | ||
| 281 | |||
| 282 | ********************************************************************/ | ||
| 283 | extern "C" HRESULT WIXAPI WcaTableExists( | ||
| 284 | __in_z LPCWSTR wzTable | ||
| 285 | ) | ||
| 286 | { | ||
| 287 | HRESULT hr = S_OK; | ||
| 288 | UINT er = ERROR_SUCCESS; | ||
| 289 | |||
| 290 | // NOTE: The following line of commented out code should work in a | ||
| 291 | // CustomAction but does not in Windows Installer v1.1 | ||
| 292 | // er = ::MsiDatabaseIsTablePersistentW(hDatabase, wzTable); | ||
| 293 | |||
| 294 | // a "most elegant" workaround a Darwin v1.1 bug | ||
| 295 | PMSIHANDLE hRec; | ||
| 296 | er = ::MsiDatabaseGetPrimaryKeysW(WcaGetDatabaseHandle(), wzTable, &hRec); | ||
| 297 | |||
| 298 | if (ERROR_SUCCESS == er) | ||
| 299 | { | ||
| 300 | hr = S_OK; | ||
| 301 | } | ||
| 302 | else if (ERROR_INVALID_TABLE == er) | ||
| 303 | { | ||
| 304 | hr = S_FALSE; | ||
| 305 | } | ||
| 306 | else | ||
| 307 | { | ||
| 308 | hr = E_FAIL; | ||
| 309 | } | ||
| 310 | Assert(SUCCEEDED(hr)); | ||
| 311 | |||
| 312 | return hr; | ||
| 313 | } | ||
| 314 | |||
| 315 | |||
| 316 | /******************************************************************** | ||
| 317 | WcaOpenView() - opens a view on the installing database | ||
| 318 | |||
| 319 | ********************************************************************/ | ||
| 320 | extern "C" HRESULT WIXAPI WcaOpenView( | ||
| 321 | __in_z LPCWSTR wzSql, | ||
| 322 | __out MSIHANDLE* phView | ||
| 323 | ) | ||
| 324 | { | ||
| 325 | if (!wzSql || !*wzSql|| !phView) | ||
| 326 | { | ||
| 327 | return E_INVALIDARG; | ||
| 328 | } | ||
| 329 | |||
| 330 | HRESULT hr = S_OK; | ||
| 331 | UINT er = ::MsiDatabaseOpenViewW(WcaGetDatabaseHandle(), wzSql, phView); | ||
| 332 | ExitOnWin32Error(er, hr, "failed to open view on database with SQL: %ls", wzSql); | ||
| 333 | |||
| 334 | LExit: | ||
| 335 | return hr; | ||
| 336 | } | ||
| 337 | |||
| 338 | |||
| 339 | /******************************************************************** | ||
| 340 | WcaExecuteView() - executes a parameterized open view on the installing database | ||
| 341 | |||
| 342 | ********************************************************************/ | ||
| 343 | extern "C" HRESULT WIXAPI WcaExecuteView( | ||
| 344 | __in MSIHANDLE hView, | ||
| 345 | __in MSIHANDLE hRec | ||
| 346 | ) | ||
| 347 | { | ||
| 348 | if (!hView) | ||
| 349 | { | ||
| 350 | return E_INVALIDARG; | ||
| 351 | } | ||
| 352 | AssertSz(hRec, "Use WcaOpenExecuteView() if you don't need to pass in a record"); | ||
| 353 | |||
| 354 | HRESULT hr = S_OK; | ||
| 355 | UINT er = ::MsiViewExecute(hView, hRec); | ||
| 356 | ExitOnWin32Error(er, hr, "failed to execute view"); | ||
| 357 | |||
| 358 | LExit: | ||
| 359 | return hr; | ||
| 360 | } | ||
| 361 | |||
| 362 | |||
| 363 | /******************************************************************** | ||
| 364 | WcaOpenExecuteView() - opens and executes a view on the installing database | ||
| 365 | |||
| 366 | ********************************************************************/ | ||
| 367 | extern "C" HRESULT WIXAPI WcaOpenExecuteView( | ||
| 368 | __in_z LPCWSTR wzSql, | ||
| 369 | __out MSIHANDLE* phView | ||
| 370 | ) | ||
| 371 | { | ||
| 372 | if (!wzSql || !*wzSql|| !phView) | ||
| 373 | { | ||
| 374 | return E_INVALIDARG; | ||
| 375 | } | ||
| 376 | |||
| 377 | HRESULT hr = S_OK; | ||
| 378 | UINT er = ::MsiDatabaseOpenViewW(WcaGetDatabaseHandle(), wzSql, phView); | ||
| 379 | ExitOnWin32Error(er, hr, "failed to open view on database"); | ||
| 380 | |||
| 381 | er = ::MsiViewExecute(*phView, NULL); | ||
| 382 | ExitOnWin32Error(er, hr, "failed to execute view"); | ||
| 383 | |||
| 384 | LExit: | ||
| 385 | return hr; | ||
| 386 | } | ||
| 387 | |||
| 388 | |||
| 389 | /******************************************************************** | ||
| 390 | WcaFetchRecord() - gets the next record from a view on the installing database | ||
| 391 | |||
| 392 | ********************************************************************/ | ||
| 393 | extern "C" HRESULT WIXAPI WcaFetchRecord( | ||
| 394 | __in MSIHANDLE hView, | ||
| 395 | __out MSIHANDLE* phRec | ||
| 396 | ) | ||
| 397 | { | ||
| 398 | if (!hView|| !phRec) | ||
| 399 | { | ||
| 400 | return E_INVALIDARG; | ||
| 401 | } | ||
| 402 | |||
| 403 | HRESULT hr = S_OK; | ||
| 404 | UINT er = ::MsiViewFetch(hView, phRec); | ||
| 405 | hr = HRESULT_FROM_WIN32(er); | ||
| 406 | if (FAILED(hr) && E_NOMOREITEMS != hr) | ||
| 407 | { | ||
| 408 | ExitOnFailure(hr, "failed to fetch record from view"); | ||
| 409 | } | ||
| 410 | |||
| 411 | LExit: | ||
| 412 | return hr; | ||
| 413 | } | ||
| 414 | |||
| 415 | |||
| 416 | /******************************************************************** | ||
| 417 | WcaFetchSingleRecord() - gets a single record from a view on the installing database | ||
| 418 | |||
| 419 | ********************************************************************/ | ||
| 420 | extern "C" HRESULT WIXAPI WcaFetchSingleRecord( | ||
| 421 | __in MSIHANDLE hView, | ||
| 422 | __out MSIHANDLE* phRec | ||
| 423 | ) | ||
| 424 | { | ||
| 425 | if (!hView|| !phRec) | ||
| 426 | { | ||
| 427 | return E_INVALIDARG; | ||
| 428 | } | ||
| 429 | |||
| 430 | HRESULT hr = S_OK; | ||
| 431 | UINT er = ::MsiViewFetch(hView, phRec); | ||
| 432 | if (ERROR_NO_MORE_ITEMS == er) | ||
| 433 | { | ||
| 434 | hr = S_FALSE; | ||
| 435 | } | ||
| 436 | else | ||
| 437 | { | ||
| 438 | hr = HRESULT_FROM_WIN32(er); | ||
| 439 | } | ||
| 440 | ExitOnFailure(hr, "failed to fetch single record from view"); | ||
| 441 | |||
| 442 | #ifdef DEBUG // only do this in debug to verify that a single record was returned | ||
| 443 | MSIHANDLE hRecTest; | ||
| 444 | er = ::MsiViewFetch(hView, &hRecTest); | ||
| 445 | AssertSz(ERROR_NO_MORE_ITEMS == er && NULL == hRecTest, "WcaSingleFetch() did not fetch a single record"); | ||
| 446 | ::MsiCloseHandle(hRecTest); | ||
| 447 | #endif | ||
| 448 | |||
| 449 | LExit: | ||
| 450 | return hr; | ||
| 451 | } | ||
| 452 | |||
| 453 | |||
| 454 | /******************************************************************** | ||
| 455 | WcaGetProperty - gets a string property value from the active install | ||
| 456 | |||
| 457 | ********************************************************************/ | ||
| 458 | extern "C" HRESULT WIXAPI WcaGetProperty( | ||
| 459 | __in_z LPCWSTR wzProperty, | ||
| 460 | __inout LPWSTR* ppwzData | ||
| 461 | ) | ||
| 462 | { | ||
| 463 | if (!wzProperty || !*wzProperty || !ppwzData) | ||
| 464 | { | ||
| 465 | return E_INVALIDARG; | ||
| 466 | } | ||
| 467 | |||
| 468 | HRESULT hr = S_OK; | ||
| 469 | UINT er = ERROR_SUCCESS; | ||
| 470 | DWORD_PTR cch = 0; | ||
| 471 | |||
| 472 | if (!*ppwzData) | ||
| 473 | { | ||
| 474 | WCHAR szEmpty[1] = L""; | ||
| 475 | er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, szEmpty, (DWORD *)&cch); | ||
| 476 | if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) | ||
| 477 | { | ||
| 478 | hr = StrAlloc(ppwzData, ++cch); | ||
| 479 | } | ||
| 480 | else | ||
| 481 | { | ||
| 482 | hr = HRESULT_FROM_WIN32(er); | ||
| 483 | } | ||
| 484 | ExitOnFailure(hr, "Failed to allocate string for Property '%ls'", wzProperty); | ||
| 485 | } | ||
| 486 | else | ||
| 487 | { | ||
| 488 | hr = StrMaxLength(*ppwzData, &cch); | ||
| 489 | ExitOnFailure(hr, "Failed to get previous size of property data string."); | ||
| 490 | } | ||
| 491 | |||
| 492 | er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, (DWORD *)&cch); | ||
| 493 | if (ERROR_MORE_DATA == er) | ||
| 494 | { | ||
| 495 | Assert(*ppwzData); | ||
| 496 | hr = StrAlloc(ppwzData, ++cch); | ||
| 497 | ExitOnFailure(hr, "Failed to allocate string for Property '%ls'", wzProperty); | ||
| 498 | |||
| 499 | er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, (DWORD *)&cch); | ||
| 500 | } | ||
| 501 | ExitOnWin32Error(er, hr, "Failed to get data for property '%ls'", wzProperty); | ||
| 502 | |||
| 503 | LExit: | ||
| 504 | return hr; | ||
| 505 | } | ||
| 506 | |||
| 507 | |||
| 508 | /******************************************************************** | ||
| 509 | WcaGetFormattedProperty - gets a formatted string property value from | ||
| 510 | the active install | ||
| 511 | |||
| 512 | ********************************************************************/ | ||
| 513 | extern "C" HRESULT WIXAPI WcaGetFormattedProperty( | ||
| 514 | __in_z LPCWSTR wzProperty, | ||
| 515 | __out LPWSTR* ppwzData | ||
| 516 | ) | ||
| 517 | { | ||
| 518 | if (!wzProperty || !*wzProperty || !ppwzData) | ||
| 519 | { | ||
| 520 | return E_INVALIDARG; | ||
| 521 | } | ||
| 522 | |||
| 523 | HRESULT hr = S_OK; | ||
| 524 | LPWSTR pwzPropertyValue = NULL; | ||
| 525 | |||
| 526 | hr = WcaGetProperty(wzProperty, &pwzPropertyValue); | ||
| 527 | ExitOnFailure(hr, "failed to get %ls", wzProperty); | ||
| 528 | |||
| 529 | hr = WcaGetFormattedString(pwzPropertyValue, ppwzData); | ||
| 530 | ExitOnFailure(hr, "failed to get formatted value for property: '%ls' with value: '%ls'", wzProperty, pwzPropertyValue); | ||
| 531 | |||
| 532 | LExit: | ||
| 533 | ReleaseStr(pwzPropertyValue); | ||
| 534 | |||
| 535 | return hr; | ||
| 536 | } | ||
| 537 | |||
| 538 | |||
| 539 | /******************************************************************** | ||
| 540 | WcaGetFormattedString - gets a formatted string value from | ||
| 541 | the active install | ||
| 542 | |||
| 543 | ********************************************************************/ | ||
| 544 | extern "C" HRESULT WIXAPI WcaGetFormattedString( | ||
| 545 | __in_z LPCWSTR wzString, | ||
| 546 | __out LPWSTR* ppwzData | ||
| 547 | ) | ||
| 548 | { | ||
| 549 | if (!wzString || !*wzString || !ppwzData) | ||
| 550 | { | ||
| 551 | return E_INVALIDARG; | ||
| 552 | } | ||
| 553 | |||
| 554 | HRESULT hr = S_OK; | ||
| 555 | UINT er = ERROR_SUCCESS; | ||
| 556 | PMSIHANDLE hRecord = ::MsiCreateRecord(1); | ||
| 557 | DWORD_PTR cch = 0; | ||
| 558 | |||
| 559 | er = ::MsiRecordSetStringW(hRecord, 0, wzString); | ||
| 560 | ExitOnWin32Error(er, hr, "Failed to set record field 0 with '%ls'", wzString); | ||
| 561 | |||
| 562 | if (!*ppwzData) | ||
| 563 | { | ||
| 564 | WCHAR szEmpty[1] = L""; | ||
| 565 | er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, szEmpty, (DWORD *)&cch); | ||
| 566 | if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) | ||
| 567 | { | ||
| 568 | hr = StrAlloc(ppwzData, ++cch); | ||
| 569 | } | ||
| 570 | else | ||
| 571 | { | ||
| 572 | hr = HRESULT_FROM_WIN32(er); | ||
| 573 | } | ||
| 574 | ExitOnFailure(hr, "Failed to allocate string for formatted string: '%ls'", wzString); | ||
| 575 | } | ||
| 576 | else | ||
| 577 | { | ||
| 578 | hr = StrMaxLength(*ppwzData, &cch); | ||
| 579 | ExitOnFailure(hr, "Failed to get previous size of property data string"); | ||
| 580 | } | ||
| 581 | |||
| 582 | er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, (DWORD *)&cch); | ||
| 583 | if (ERROR_MORE_DATA == er) | ||
| 584 | { | ||
| 585 | hr = StrAlloc(ppwzData, ++cch); | ||
| 586 | ExitOnFailure(hr, "Failed to allocate string for formatted string: '%ls'", wzString); | ||
| 587 | |||
| 588 | er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, (DWORD *)&cch); | ||
| 589 | } | ||
| 590 | ExitOnWin32Error(er, hr, "Failed to get formatted string: '%ls'", wzString); | ||
| 591 | |||
| 592 | LExit: | ||
| 593 | return hr; | ||
| 594 | } | ||
| 595 | |||
| 596 | |||
| 597 | /******************************************************************** | ||
| 598 | WcaGetIntProperty - gets an integer property value from the active install | ||
| 599 | |||
| 600 | ********************************************************************/ | ||
| 601 | extern "C" HRESULT WIXAPI WcaGetIntProperty( | ||
| 602 | __in_z LPCWSTR wzProperty, | ||
| 603 | __inout int* piData | ||
| 604 | ) | ||
| 605 | { | ||
| 606 | if (!piData) | ||
| 607 | return E_INVALIDARG; | ||
| 608 | |||
| 609 | HRESULT hr = S_OK; | ||
| 610 | UINT er; | ||
| 611 | |||
| 612 | WCHAR wzValue[32]; | ||
| 613 | DWORD cch = countof(wzValue) - 1; | ||
| 614 | |||
| 615 | er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, wzValue, &cch); | ||
| 616 | ExitOnWin32Error(er, hr, "Failed to get data for property '%ls'", wzProperty); | ||
| 617 | |||
| 618 | *piData = wcstol(wzValue, NULL, 10); | ||
| 619 | |||
| 620 | LExit: | ||
| 621 | return hr; | ||
| 622 | } | ||
| 623 | |||
| 624 | |||
| 625 | /******************************************************************** | ||
| 626 | WcaGetTargetPath - gets the target path for a specified folder | ||
| 627 | |||
| 628 | ********************************************************************/ | ||
| 629 | extern "C" HRESULT WIXAPI WcaGetTargetPath( | ||
| 630 | __in_z LPCWSTR wzFolder, | ||
| 631 | __out LPWSTR* ppwzData | ||
| 632 | ) | ||
| 633 | { | ||
| 634 | if (!wzFolder || !*wzFolder || !ppwzData) | ||
| 635 | return E_INVALIDARG; | ||
| 636 | |||
| 637 | HRESULT hr = S_OK; | ||
| 638 | |||
| 639 | UINT er = ERROR_SUCCESS; | ||
| 640 | DWORD_PTR cch = 0; | ||
| 641 | |||
| 642 | if (!*ppwzData) | ||
| 643 | { | ||
| 644 | WCHAR szEmpty[1] = L""; | ||
| 645 | er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, szEmpty, (DWORD*)&cch); | ||
| 646 | if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) | ||
| 647 | { | ||
| 648 | ++cch; //Add one for the null terminator | ||
| 649 | hr = StrAlloc(ppwzData, cch); | ||
| 650 | } | ||
| 651 | else | ||
| 652 | { | ||
| 653 | hr = HRESULT_FROM_WIN32(er); | ||
| 654 | } | ||
| 655 | ExitOnFailure(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder); | ||
| 656 | } | ||
| 657 | else | ||
| 658 | { | ||
| 659 | hr = StrMaxLength(*ppwzData, &cch); | ||
| 660 | ExitOnFailure(hr, "Failed to get previous size of string"); | ||
| 661 | } | ||
| 662 | |||
| 663 | er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, (DWORD*)&cch); | ||
| 664 | if (ERROR_MORE_DATA == er) | ||
| 665 | { | ||
| 666 | ++cch; | ||
| 667 | hr = StrAlloc(ppwzData, cch); | ||
| 668 | ExitOnFailure(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder); | ||
| 669 | |||
| 670 | er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, (DWORD*)&cch); | ||
| 671 | } | ||
| 672 | ExitOnWin32Error(er, hr, "Failed to get target path for folder '%ls'", wzFolder); | ||
| 673 | |||
| 674 | LExit: | ||
| 675 | return hr; | ||
| 676 | } | ||
| 677 | |||
| 678 | |||
| 679 | /******************************************************************** | ||
| 680 | WcaSetProperty - sets a string property value in the active install | ||
| 681 | |||
| 682 | ********************************************************************/ | ||
| 683 | extern "C" HRESULT WIXAPI WcaSetProperty( | ||
| 684 | __in_z LPCWSTR wzPropertyName, | ||
| 685 | __in_z LPCWSTR wzPropertyValue | ||
| 686 | ) | ||
| 687 | { | ||
| 688 | HRESULT hr = S_OK; | ||
| 689 | |||
| 690 | if (!wzPropertyName || !*wzPropertyName || !wzPropertyValue) | ||
| 691 | return E_INVALIDARG; | ||
| 692 | |||
| 693 | UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue); | ||
| 694 | ExitOnWin32Error(er, hr, "failed to set property: %ls", wzPropertyName); | ||
| 695 | |||
| 696 | LExit: | ||
| 697 | return hr; | ||
| 698 | } | ||
| 699 | |||
| 700 | |||
| 701 | /******************************************************************** | ||
| 702 | WcaSetIntProperty - sets a integer property value in the active install | ||
| 703 | |||
| 704 | ********************************************************************/ | ||
| 705 | extern "C" HRESULT WIXAPI WcaSetIntProperty( | ||
| 706 | __in_z LPCWSTR wzPropertyName, | ||
| 707 | __in int nPropertyValue | ||
| 708 | ) | ||
| 709 | { | ||
| 710 | if (!wzPropertyName || !*wzPropertyName) | ||
| 711 | return E_INVALIDARG; | ||
| 712 | |||
| 713 | // 12 characters should be enough for a 32-bit int: 10 digits, 1 sign, 1 null | ||
| 714 | WCHAR wzPropertyValue[13]; | ||
| 715 | HRESULT hr = StringCchPrintfW(wzPropertyValue, countof(wzPropertyValue), L"%d", nPropertyValue); | ||
| 716 | ExitOnFailure(hr, "failed to convert into string property value: %d", nPropertyValue); | ||
| 717 | |||
| 718 | UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue); | ||
| 719 | ExitOnWin32Error(er, hr, "failed to set property: %ls", wzPropertyName); | ||
| 720 | |||
| 721 | LExit: | ||
| 722 | return hr; | ||
| 723 | } | ||
| 724 | |||
| 725 | |||
| 726 | /******************************************************************** | ||
| 727 | WcaIsPropertySet() - returns TRUE if property is set | ||
| 728 | |||
| 729 | ********************************************************************/ | ||
| 730 | extern "C" BOOL WIXAPI WcaIsPropertySet( | ||
| 731 | __in LPCSTR szProperty | ||
| 732 | ) | ||
| 733 | { | ||
| 734 | DWORD cchProperty = 0; | ||
| 735 | char szEmpty[1] = ""; | ||
| 736 | #ifdef DEBUG | ||
| 737 | UINT er = | ||
| 738 | #endif | ||
| 739 | ::MsiGetPropertyA(WcaGetInstallHandle(), szProperty, szEmpty, &cchProperty); | ||
| 740 | AssertSz(ERROR_INVALID_PARAMETER != er && ERROR_INVALID_HANDLE != er, "Unexpected return value from ::MsiGetProperty()"); | ||
| 741 | |||
| 742 | return 0 < cchProperty; // property is set if the length is greater than zero | ||
| 743 | } | ||
| 744 | |||
| 745 | |||
| 746 | /******************************************************************** | ||
| 747 | WcaIsUnicodePropertySet() - returns TRUE if property is set | ||
| 748 | |||
| 749 | ********************************************************************/ | ||
| 750 | extern "C" BOOL WIXAPI WcaIsUnicodePropertySet( | ||
| 751 | __in LPCWSTR wzProperty | ||
| 752 | ) | ||
| 753 | { | ||
| 754 | DWORD cchProperty = 0; | ||
| 755 | wchar_t wzEmpty[1] = L""; | ||
| 756 | #ifdef DEBUG | ||
| 757 | UINT er = | ||
| 758 | #endif | ||
| 759 | ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, wzEmpty, &cchProperty); | ||
| 760 | AssertSz(ERROR_INVALID_PARAMETER != er && ERROR_INVALID_HANDLE != er, "Unexpected return value from ::MsiGetProperty()"); | ||
| 761 | |||
| 762 | return 0 < cchProperty; // property is set if the length is greater than zero | ||
| 763 | } | ||
| 764 | |||
| 765 | |||
| 766 | /******************************************************************** | ||
| 767 | WcaGetRecordInteger() - gets an integer field out of a record | ||
| 768 | |||
| 769 | NOTE: returns S_FALSE if the field was null | ||
| 770 | ********************************************************************/ | ||
| 771 | extern "C" HRESULT WIXAPI WcaGetRecordInteger( | ||
| 772 | __in MSIHANDLE hRec, | ||
| 773 | __in UINT uiField, | ||
| 774 | __inout int* piData | ||
| 775 | ) | ||
| 776 | { | ||
| 777 | if (!hRec || !piData) | ||
| 778 | return E_INVALIDARG; | ||
| 779 | |||
| 780 | HRESULT hr = S_OK; | ||
| 781 | *piData = ::MsiRecordGetInteger(hRec, uiField); | ||
| 782 | if (MSI_NULL_INTEGER == *piData) | ||
| 783 | hr = S_FALSE; | ||
| 784 | |||
| 785 | //LExit: | ||
| 786 | return hr; | ||
| 787 | } | ||
| 788 | |||
| 789 | |||
| 790 | /******************************************************************** | ||
| 791 | WcaGetRecordString() - gets a string field out of a record | ||
| 792 | |||
| 793 | ********************************************************************/ | ||
| 794 | extern "C" HRESULT WIXAPI WcaGetRecordString( | ||
| 795 | __in MSIHANDLE hRec, | ||
| 796 | __in UINT uiField, | ||
| 797 | __inout LPWSTR* ppwzData | ||
| 798 | ) | ||
| 799 | { | ||
| 800 | if (!hRec || !ppwzData) | ||
| 801 | return E_INVALIDARG; | ||
| 802 | |||
| 803 | HRESULT hr = S_OK; | ||
| 804 | UINT er; | ||
| 805 | DWORD_PTR cch = 0; | ||
| 806 | |||
| 807 | if (!*ppwzData) | ||
| 808 | { | ||
| 809 | WCHAR szEmpty[1] = L""; | ||
| 810 | er = ::MsiRecordGetStringW(hRec, uiField, szEmpty, (DWORD*)&cch); | ||
| 811 | if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) | ||
| 812 | { | ||
| 813 | hr = StrAlloc(ppwzData, ++cch); | ||
| 814 | } | ||
| 815 | else | ||
| 816 | { | ||
| 817 | hr = HRESULT_FROM_WIN32(er); | ||
| 818 | } | ||
| 819 | ExitOnFailure(hr, "Failed to allocate memory for record string"); | ||
| 820 | } | ||
| 821 | else | ||
| 822 | { | ||
| 823 | hr = StrMaxLength(*ppwzData, &cch); | ||
| 824 | ExitOnFailure(hr, "Failed to get previous size of string"); | ||
| 825 | } | ||
| 826 | |||
| 827 | er = ::MsiRecordGetStringW(hRec, uiField, *ppwzData, (DWORD*)&cch); | ||
| 828 | if (ERROR_MORE_DATA == er) | ||
| 829 | { | ||
| 830 | hr = StrAlloc(ppwzData, ++cch); | ||
| 831 | ExitOnFailure(hr, "Failed to allocate memory for record string"); | ||
| 832 | |||
| 833 | er = ::MsiRecordGetStringW(hRec, uiField, *ppwzData, (DWORD*)&cch); | ||
| 834 | } | ||
| 835 | ExitOnWin32Error(er, hr, "Failed to get string from record"); | ||
| 836 | |||
| 837 | LExit: | ||
| 838 | return hr; | ||
| 839 | } | ||
| 840 | |||
| 841 | |||
| 842 | /******************************************************************** | ||
| 843 | HideNulls() - internal helper function to escape [~] in formatted strings | ||
| 844 | |||
| 845 | ********************************************************************/ | ||
| 846 | static void HideNulls( | ||
| 847 | __inout_z LPWSTR wzData | ||
| 848 | ) | ||
| 849 | { | ||
| 850 | LPWSTR pwz = wzData; | ||
| 851 | |||
| 852 | while(*pwz) | ||
| 853 | { | ||
| 854 | if (pwz[0] == L'[' && pwz[1] == L'~' && pwz[2] == L']') // found a null [~] | ||
| 855 | { | ||
| 856 | pwz[0] = L'!'; // turn it into !$! | ||
| 857 | pwz[1] = L'$'; | ||
| 858 | pwz[2] = L'!'; | ||
| 859 | pwz += 3; | ||
| 860 | } | ||
| 861 | else | ||
| 862 | { | ||
| 863 | ++pwz; | ||
| 864 | } | ||
| 865 | } | ||
| 866 | } | ||
| 867 | |||
| 868 | |||
| 869 | /******************************************************************** | ||
| 870 | RevealNulls() - internal helper function to unescape !$! in formatted strings | ||
| 871 | |||
| 872 | ********************************************************************/ | ||
| 873 | static void RevealNulls( | ||
| 874 | __inout_z LPWSTR wzData | ||
| 875 | ) | ||
| 876 | { | ||
| 877 | LPWSTR pwz = wzData; | ||
| 878 | |||
| 879 | while(*pwz) | ||
| 880 | { | ||
| 881 | if (pwz[0] == L'!' && pwz[1] == L'$' && pwz[2] == L'!') // found the fake null !$! | ||
| 882 | { | ||
| 883 | pwz[0] = L'['; // turn it back into [~] | ||
| 884 | pwz[1] = L'~'; | ||
| 885 | pwz[2] = L']'; | ||
| 886 | pwz += 3; | ||
| 887 | } | ||
| 888 | else | ||
| 889 | { | ||
| 890 | ++pwz; | ||
| 891 | } | ||
| 892 | } | ||
| 893 | } | ||
| 894 | |||
| 895 | |||
| 896 | /******************************************************************** | ||
| 897 | WcaGetRecordFormattedString() - gets formatted string filed from record | ||
| 898 | |||
| 899 | ********************************************************************/ | ||
| 900 | extern "C" HRESULT WIXAPI WcaGetRecordFormattedString( | ||
| 901 | __in MSIHANDLE hRec, | ||
| 902 | __in UINT uiField, | ||
| 903 | __inout LPWSTR* ppwzData | ||
| 904 | ) | ||
| 905 | { | ||
| 906 | if (!hRec || !ppwzData) | ||
| 907 | { | ||
| 908 | return E_INVALIDARG; | ||
| 909 | } | ||
| 910 | |||
| 911 | HRESULT hr = S_OK; | ||
| 912 | UINT er; | ||
| 913 | DWORD_PTR cch = 0; | ||
| 914 | PMSIHANDLE hRecFormat; | ||
| 915 | |||
| 916 | // get the format string | ||
| 917 | hr = WcaGetRecordString(hRec, uiField, ppwzData); | ||
| 918 | ExitOnFailure(hr, "failed to get string from record"); | ||
| 919 | |||
| 920 | if (!**ppwzData) | ||
| 921 | { | ||
| 922 | ExitFunction(); | ||
| 923 | } | ||
| 924 | |||
| 925 | // hide the nulls '[~]' so we can get them back after formatting | ||
| 926 | HideNulls(*ppwzData); | ||
| 927 | |||
| 928 | // set up the format record | ||
| 929 | hRecFormat = ::MsiCreateRecord(1); | ||
| 930 | ExitOnNull(hRecFormat, hr, E_UNEXPECTED, "Failed to create record to format string"); | ||
| 931 | hr = WcaSetRecordString(hRecFormat, 0, *ppwzData); | ||
| 932 | ExitOnFailure(hr, "failed to set string to format record"); | ||
| 933 | |||
| 934 | // format the string | ||
| 935 | hr = StrMaxLength(*ppwzData, &cch); | ||
| 936 | ExitOnFailure(hr, "failed to get max length of string"); | ||
| 937 | |||
| 938 | er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecFormat, *ppwzData, (DWORD*)&cch); | ||
| 939 | if (ERROR_MORE_DATA == er) | ||
| 940 | { | ||
| 941 | hr = StrAlloc(ppwzData, ++cch); | ||
| 942 | ExitOnFailure(hr, "Failed to allocate memory for record string"); | ||
| 943 | |||
| 944 | er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecFormat, *ppwzData, (DWORD*)&cch); | ||
| 945 | } | ||
| 946 | ExitOnWin32Error(er, hr, "Failed to format string"); | ||
| 947 | |||
| 948 | // put the nulls back | ||
| 949 | RevealNulls(*ppwzData); | ||
| 950 | |||
| 951 | LExit: | ||
| 952 | return hr; | ||
| 953 | } | ||
| 954 | |||
| 955 | |||
| 956 | /******************************************************************** | ||
| 957 | WcaGetRecordFormattedInteger() - gets formatted integer from record | ||
| 958 | |||
| 959 | ********************************************************************/ | ||
| 960 | extern "C" HRESULT WIXAPI WcaGetRecordFormattedInteger( | ||
| 961 | __in MSIHANDLE hRec, | ||
| 962 | __in UINT uiField, | ||
| 963 | __out int* piData | ||
| 964 | ) | ||
| 965 | { | ||
| 966 | if (!hRec || !piData) | ||
| 967 | { | ||
| 968 | return E_INVALIDARG; | ||
| 969 | } | ||
| 970 | |||
| 971 | HRESULT hr = S_OK; | ||
| 972 | LPWSTR pwzData = NULL; | ||
| 973 | |||
| 974 | hr = WcaGetRecordFormattedString(hRec, uiField, &pwzData); | ||
| 975 | ExitOnFailure(hr, "failed to get record field: %u", uiField); | ||
| 976 | if (pwzData && *pwzData) | ||
| 977 | { | ||
| 978 | LPWSTR wz = NULL; | ||
| 979 | *piData = wcstol(pwzData, &wz, 10); | ||
| 980 | if (wz && *wz) | ||
| 981 | { | ||
| 982 | hr = E_INVALIDARG; | ||
| 983 | ExitOnFailure(hr, "failed to parse record field: %u as number: %ls", uiField, pwzData); | ||
| 984 | } | ||
| 985 | } | ||
| 986 | else | ||
| 987 | { | ||
| 988 | *piData = MSI_NULL_INTEGER; | ||
| 989 | } | ||
| 990 | |||
| 991 | LExit: | ||
| 992 | return hr; | ||
| 993 | } | ||
| 994 | |||
| 995 | |||
| 996 | /******************************************************************** | ||
| 997 | WcaAllocStream() - creates a byte stream of the specified size | ||
| 998 | |||
| 999 | NOTE: Use WcaFreeStream() to release the byte stream | ||
| 1000 | ********************************************************************/ | ||
| 1001 | extern "C" HRESULT WIXAPI WcaAllocStream( | ||
| 1002 | __deref_out_bcount_part(cbData, 0) BYTE** ppbData, | ||
| 1003 | __in DWORD cbData | ||
| 1004 | ) | ||
| 1005 | { | ||
| 1006 | Assert(ppbData); | ||
| 1007 | HRESULT hr; | ||
| 1008 | BYTE* pbNewData; | ||
| 1009 | |||
| 1010 | if (*ppbData) | ||
| 1011 | pbNewData = static_cast<BYTE*>(MemReAlloc(*ppbData, cbData, TRUE)); | ||
| 1012 | else | ||
| 1013 | pbNewData = static_cast<BYTE*>(MemAlloc(cbData, TRUE)); | ||
| 1014 | |||
| 1015 | if (!pbNewData) | ||
| 1016 | { | ||
| 1017 | ExitOnLastError(hr, "Failed to allocate string"); | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | *ppbData = pbNewData; | ||
| 1021 | pbNewData = NULL; | ||
| 1022 | |||
| 1023 | hr = S_OK; | ||
| 1024 | LExit: | ||
| 1025 | ReleaseMem(pbNewData); | ||
| 1026 | |||
| 1027 | return hr; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | |||
| 1031 | /******************************************************************** | ||
| 1032 | WcaFreeStream() - frees a byte stream | ||
| 1033 | |||
| 1034 | ********************************************************************/ | ||
| 1035 | extern "C" HRESULT WIXAPI WcaFreeStream( | ||
| 1036 | __in BYTE* pbData | ||
| 1037 | ) | ||
| 1038 | { | ||
| 1039 | if (!pbData) | ||
| 1040 | return E_INVALIDARG; | ||
| 1041 | |||
| 1042 | HRESULT hr = MemFree(pbData); | ||
| 1043 | return hr; | ||
| 1044 | } | ||
| 1045 | |||
| 1046 | |||
| 1047 | /******************************************************************** | ||
| 1048 | WcaGetRecordStream() - gets a byte stream field from record | ||
| 1049 | |||
| 1050 | ********************************************************************/ | ||
| 1051 | extern "C" HRESULT WIXAPI WcaGetRecordStream( | ||
| 1052 | __in MSIHANDLE hRecBinary, | ||
| 1053 | __in UINT uiField, | ||
| 1054 | __deref_out_bcount_full(*pcbData) BYTE** ppbData, | ||
| 1055 | __out DWORD* pcbData | ||
| 1056 | ) | ||
| 1057 | { | ||
| 1058 | HRESULT hr = S_OK; | ||
| 1059 | UINT er = ERROR_SUCCESS; | ||
| 1060 | |||
| 1061 | if (!hRecBinary || !ppbData || !pcbData) | ||
| 1062 | return E_INVALIDARG; | ||
| 1063 | |||
| 1064 | *pcbData = 0; | ||
| 1065 | er = ::MsiRecordReadStream(hRecBinary, uiField, NULL, pcbData); | ||
| 1066 | ExitOnWin32Error(er, hr, "failed to get size of stream"); | ||
| 1067 | |||
| 1068 | hr = WcaAllocStream(ppbData, *pcbData); | ||
| 1069 | ExitOnFailure(hr, "failed to allocate data for stream"); | ||
| 1070 | |||
| 1071 | er = ::MsiRecordReadStream(hRecBinary, uiField, (char*)*ppbData, pcbData); | ||
| 1072 | ExitOnWin32Error(er, hr, "failed to read from stream"); | ||
| 1073 | |||
| 1074 | LExit: | ||
| 1075 | return hr; | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | |||
| 1079 | /******************************************************************** | ||
| 1080 | WcaSetRecordString() - set a string field in record | ||
| 1081 | |||
| 1082 | ********************************************************************/ | ||
| 1083 | extern "C" HRESULT WIXAPI WcaSetRecordString( | ||
| 1084 | __in MSIHANDLE hRec, | ||
| 1085 | __in UINT uiField, | ||
| 1086 | __in_z LPCWSTR wzData | ||
| 1087 | ) | ||
| 1088 | { | ||
| 1089 | if (!hRec || !wzData) | ||
| 1090 | return E_INVALIDARG; | ||
| 1091 | |||
| 1092 | HRESULT hr = S_OK; | ||
| 1093 | UINT er = ::MsiRecordSetStringW(hRec, uiField, wzData); | ||
| 1094 | ExitOnWin32Error(er, hr, "failed to set string in record"); | ||
| 1095 | |||
| 1096 | LExit: | ||
| 1097 | return hr; | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | |||
| 1101 | /******************************************************************** | ||
| 1102 | WcaSetRecordInteger() - set a integer field in record | ||
| 1103 | |||
| 1104 | ********************************************************************/ | ||
| 1105 | extern "C" HRESULT WIXAPI WcaSetRecordInteger( | ||
| 1106 | __in MSIHANDLE hRec, | ||
| 1107 | __in UINT uiField, | ||
| 1108 | __in int iValue | ||
| 1109 | ) | ||
| 1110 | { | ||
| 1111 | if (!hRec) | ||
| 1112 | return E_INVALIDARG; | ||
| 1113 | |||
| 1114 | HRESULT hr = S_OK; | ||
| 1115 | UINT er = ::MsiRecordSetInteger(hRec, uiField, iValue); | ||
| 1116 | ExitOnWin32Error(er, hr, "failed to set integer in record"); | ||
| 1117 | |||
| 1118 | LExit: | ||
| 1119 | return hr; | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | |||
| 1123 | /******************************************************************** | ||
| 1124 | |||
| 1125 | WcaDoDeferredAction() - schedules an action at this point in the script | ||
| 1126 | |||
| 1127 | ********************************************************************/ | ||
| 1128 | extern "C" HRESULT WIXAPI WcaDoDeferredAction( | ||
| 1129 | __in_z LPCWSTR wzAction, | ||
| 1130 | __in_z LPCWSTR wzCustomActionData, | ||
| 1131 | __in UINT uiCost | ||
| 1132 | ) | ||
| 1133 | { | ||
| 1134 | HRESULT hr = S_OK; | ||
| 1135 | UINT er; | ||
| 1136 | |||
| 1137 | if (wzCustomActionData && *wzCustomActionData) | ||
| 1138 | { | ||
| 1139 | er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzAction, wzCustomActionData); | ||
| 1140 | ExitOnWin32Error(er, hr, "Failed to set CustomActionData for deferred action"); | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | if (0 < uiCost) | ||
| 1144 | { | ||
| 1145 | hr = WcaProgressMessage(uiCost, TRUE); // add ticks to the progress bar | ||
| 1146 | // TODO: handle the return codes correctly | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | er = ::MsiDoActionW(WcaGetInstallHandle(), wzAction); | ||
| 1150 | if (ERROR_INSTALL_USEREXIT == er) | ||
| 1151 | { | ||
| 1152 | WcaSetReturnValue(er); | ||
| 1153 | } | ||
| 1154 | ExitOnWin32Error(er, hr, "Failed MsiDoAction on deferred action"); | ||
| 1155 | |||
| 1156 | LExit: | ||
| 1157 | return hr; | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | |||
| 1161 | /******************************************************************** | ||
| 1162 | WcaCountOfCustomActionDataRecords() - counts the number of records | ||
| 1163 | passed to a deferred CustomAction | ||
| 1164 | |||
| 1165 | ********************************************************************/ | ||
| 1166 | extern "C" DWORD WIXAPI WcaCountOfCustomActionDataRecords( | ||
| 1167 | __in_z LPCWSTR wzData | ||
| 1168 | ) | ||
| 1169 | { | ||
| 1170 | WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by NULL terminator | ||
| 1171 | DWORD dwCount = 0; | ||
| 1172 | |||
| 1173 | // Loop through until there are no delimiters, we are at the end of the string, or the delimiter is the last character in the string | ||
| 1174 | for (LPCWSTR pwzCurrent = wzData; pwzCurrent && *pwzCurrent && *(pwzCurrent + 1); pwzCurrent = wcsstr(pwzCurrent, delim)) | ||
| 1175 | { | ||
| 1176 | ++dwCount; | ||
| 1177 | ++pwzCurrent; | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | return dwCount; | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | |||
| 1184 | /******************************************************************** | ||
| 1185 | BreakDownCustomActionData() - internal helper to chop up CustomActionData | ||
| 1186 | |||
| 1187 | NOTE: this modifies the passed in data | ||
| 1188 | ********************************************************************/ | ||
| 1189 | static LPWSTR BreakDownCustomActionData( | ||
| 1190 | __inout LPWSTR* ppwzData | ||
| 1191 | ) | ||
| 1192 | { | ||
| 1193 | if (!ppwzData) | ||
| 1194 | return NULL; | ||
| 1195 | if (0 == *ppwzData) | ||
| 1196 | return NULL; | ||
| 1197 | |||
| 1198 | WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by Null terminator | ||
| 1199 | |||
| 1200 | LPWSTR pwzReturn = *ppwzData; | ||
| 1201 | LPWSTR pwz = wcsstr(pwzReturn, delim); | ||
| 1202 | if (pwz) | ||
| 1203 | { | ||
| 1204 | *pwz = 0; | ||
| 1205 | *ppwzData = pwz + 1; | ||
| 1206 | } | ||
| 1207 | else | ||
| 1208 | *ppwzData = 0; | ||
| 1209 | |||
| 1210 | return pwzReturn; | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | |||
| 1214 | /******************************************************************** | ||
| 1215 | RevertCustomActionData() - Reverts custom action data changes made | ||
| 1216 | by BreakDownCustomActionData; | ||
| 1217 | |||
| 1218 | NOTE: this modifies the passed in data | ||
| 1219 | ********************************************************************/ | ||
| 1220 | extern "C" void WIXAPI RevertCustomActionData( | ||
| 1221 | __in LPWSTR wzRevertTo, | ||
| 1222 | __in LPCWSTR wzRevertFrom | ||
| 1223 | ) | ||
| 1224 | { | ||
| 1225 | if (!wzRevertTo) | ||
| 1226 | return; | ||
| 1227 | if (!wzRevertFrom) | ||
| 1228 | return; | ||
| 1229 | // start at the revert point and replace all \0 with MAGIC_MULTISZ_DELIM | ||
| 1230 | for(LPWSTR wzIndex = wzRevertTo; wzIndex < wzRevertFrom; wzIndex++) | ||
| 1231 | { | ||
| 1232 | if (0 == *wzIndex) | ||
| 1233 | { | ||
| 1234 | *wzIndex = MAGIC_MULTISZ_DELIM; | ||
| 1235 | } | ||
| 1236 | } | ||
| 1237 | return; | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | /******************************************************************** | ||
| 1241 | WcaReadStringFromCaData() - reads a string out of the CustomActionData | ||
| 1242 | |||
| 1243 | NOTE: this modifies the passed in ppwzCustomActionData variable | ||
| 1244 | ********************************************************************/ | ||
| 1245 | extern "C" HRESULT WIXAPI WcaReadStringFromCaData( | ||
| 1246 | __deref_in LPWSTR* ppwzCustomActionData, | ||
| 1247 | __deref_out_z LPWSTR* ppwzString | ||
| 1248 | ) | ||
| 1249 | { | ||
| 1250 | HRESULT hr = S_OK; | ||
| 1251 | |||
| 1252 | LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData); | ||
| 1253 | if (!pwz) | ||
| 1254 | return E_NOMOREITEMS; | ||
| 1255 | |||
| 1256 | hr = StrAllocString(ppwzString, pwz, 0); | ||
| 1257 | ExitOnFailure(hr, "failed to allocate memory for string"); | ||
| 1258 | |||
| 1259 | hr = S_OK; | ||
| 1260 | LExit: | ||
| 1261 | return hr; | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | |||
| 1265 | /******************************************************************** | ||
| 1266 | WcaReadIntegerFromCaData() - reads an integer out of the CustomActionData | ||
| 1267 | |||
| 1268 | NOTE: this modifies the passed in ppwzCustomActionData variable | ||
| 1269 | ********************************************************************/ | ||
| 1270 | extern "C" HRESULT WIXAPI WcaReadIntegerFromCaData( | ||
| 1271 | __deref_in LPWSTR* ppwzCustomActionData, | ||
| 1272 | __out int* piResult | ||
| 1273 | ) | ||
| 1274 | { | ||
| 1275 | LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData); | ||
| 1276 | if (!pwz || 0 == wcslen(pwz)) | ||
| 1277 | return E_NOMOREITEMS; | ||
| 1278 | |||
| 1279 | *piResult = wcstol(pwz, NULL, 10); | ||
| 1280 | return S_OK; | ||
| 1281 | } | ||
| 1282 | |||
| 1283 | |||
| 1284 | /******************************************************************** | ||
| 1285 | WcaReadStreamFromCaData() - reads a stream out of the CustomActionData | ||
| 1286 | |||
| 1287 | NOTE: this modifies the passed in ppwzCustomActionData variable | ||
| 1288 | NOTE: returned stream should be freed with WcaFreeStream() | ||
| 1289 | ********************************************************************/ | ||
| 1290 | extern "C" HRESULT WIXAPI WcaReadStreamFromCaData( | ||
| 1291 | __deref_in LPWSTR* ppwzCustomActionData, | ||
| 1292 | __deref_out_bcount(*pcbData) BYTE** ppbData, | ||
| 1293 | __out DWORD_PTR* pcbData | ||
| 1294 | ) | ||
| 1295 | { | ||
| 1296 | HRESULT hr; | ||
| 1297 | |||
| 1298 | LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData); | ||
| 1299 | if (!pwz) | ||
| 1300 | return E_NOMOREITEMS; | ||
| 1301 | |||
| 1302 | hr = StrAllocBase85Decode(pwz, ppbData, pcbData); | ||
| 1303 | ExitOnFailure(hr, "failed to decode string into stream"); | ||
| 1304 | |||
| 1305 | LExit: | ||
| 1306 | return hr; | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | |||
| 1310 | /******************************************************************** | ||
| 1311 | WcaWriteStringToCaData() - adds a string to the CustomActionData to | ||
| 1312 | feed a deferred CustomAction | ||
| 1313 | |||
| 1314 | ********************************************************************/ | ||
| 1315 | extern "C" HRESULT WIXAPI WcaWriteStringToCaData( | ||
| 1316 | __in_z LPCWSTR wzString, | ||
| 1317 | __deref_inout_z_opt LPWSTR* ppwzCustomActionData | ||
| 1318 | ) | ||
| 1319 | { | ||
| 1320 | HRESULT hr = S_OK; | ||
| 1321 | WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by NULL terminator | ||
| 1322 | |||
| 1323 | if (!ppwzCustomActionData) | ||
| 1324 | { | ||
| 1325 | ExitFunction1(hr = E_INVALIDARG); | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | DWORD cchString = lstrlenW(wzString) + 1; // assume we'll be adding the delim | ||
| 1329 | DWORD_PTR cchCustomActionData = 0; | ||
| 1330 | |||
| 1331 | if (*ppwzCustomActionData) | ||
| 1332 | { | ||
| 1333 | hr = StrMaxLength(*ppwzCustomActionData, &cchCustomActionData); | ||
| 1334 | ExitOnFailure(hr, "failed to get length of custom action data"); | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | if ((cchCustomActionData - lstrlenW(*ppwzCustomActionData)) < cchString + 1) | ||
| 1338 | { | ||
| 1339 | cchCustomActionData += cchString + 1 + 255; // add 255 for good measure | ||
| 1340 | hr = StrAlloc(ppwzCustomActionData, cchCustomActionData); | ||
| 1341 | ExitOnFailure(hr, "Failed to allocate memory for CustomActionData string"); | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | if (**ppwzCustomActionData) // if data exists toss the delimiter on before adding more to the end | ||
| 1345 | { | ||
| 1346 | hr = ::StringCchCatW(*ppwzCustomActionData, cchCustomActionData, delim); | ||
| 1347 | ExitOnFailure(hr, "Failed to concatenate CustomActionData string"); | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | hr = ::StringCchCatW(*ppwzCustomActionData, cchCustomActionData, wzString); | ||
| 1351 | ExitOnFailure(hr, "Failed to concatenate CustomActionData string"); | ||
| 1352 | |||
| 1353 | LExit: | ||
| 1354 | return hr; | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | |||
| 1358 | /******************************************************************** | ||
| 1359 | WcaWriteIntegerToCaData() - adds an integer to the CustomActionData to | ||
| 1360 | feed a deferred CustomAction | ||
| 1361 | |||
| 1362 | ********************************************************************/ | ||
| 1363 | extern "C" HRESULT WIXAPI WcaWriteIntegerToCaData( | ||
| 1364 | __in int i, | ||
| 1365 | __deref_out_z_opt LPWSTR* ppwzCustomActionData | ||
| 1366 | ) | ||
| 1367 | { | ||
| 1368 | WCHAR wzBuffer[13]; | ||
| 1369 | StringCchPrintfW(wzBuffer, countof(wzBuffer), L"%d", i); | ||
| 1370 | |||
| 1371 | return WcaWriteStringToCaData(wzBuffer, ppwzCustomActionData); | ||
| 1372 | } | ||
| 1373 | |||
| 1374 | |||
| 1375 | /******************************************************************** | ||
| 1376 | WcaWriteStreamToCaData() - adds a byte stream to the CustomActionData to | ||
| 1377 | feed a deferred CustomAction | ||
| 1378 | |||
| 1379 | ********************************************************************/ | ||
| 1380 | extern "C" HRESULT WIXAPI WcaWriteStreamToCaData( | ||
| 1381 | __in_bcount(cbData) const BYTE* pbData, | ||
| 1382 | __in DWORD cbData, | ||
| 1383 | __deref_inout_z_opt LPWSTR* ppwzCustomActionData | ||
| 1384 | ) | ||
| 1385 | { | ||
| 1386 | HRESULT hr; | ||
| 1387 | LPWSTR pwzData = NULL; | ||
| 1388 | |||
| 1389 | hr = StrAllocBase85Encode(pbData, cbData, &pwzData); | ||
| 1390 | ExitOnFailure(hr, "failed to encode data into string"); | ||
| 1391 | |||
| 1392 | hr = WcaWriteStringToCaData(pwzData, ppwzCustomActionData); | ||
| 1393 | |||
| 1394 | LExit: | ||
| 1395 | ReleaseStr(pwzData); | ||
| 1396 | return hr; | ||
| 1397 | } | ||
| 1398 | |||
| 1399 | |||
| 1400 | /******************************************************************** | ||
| 1401 | WcaAddTempRecord - adds a temporary record to the active database | ||
| 1402 | |||
| 1403 | NOTE: you cannot use PMSIHANDLEs for the __in/__out parameters | ||
| 1404 | NOTE: uiUniquifyColumn can be 0 if no column needs to be made unique | ||
| 1405 | ********************************************************************/ | ||
| 1406 | extern "C" HRESULT __cdecl WcaAddTempRecord( | ||
| 1407 | __inout MSIHANDLE* phTableView, | ||
| 1408 | __inout MSIHANDLE* phColumns, | ||
| 1409 | __in_z LPCWSTR wzTable, | ||
| 1410 | __out_opt MSIDBERROR* pdbError, | ||
| 1411 | __in UINT uiUniquifyColumn, | ||
| 1412 | __in UINT cColumns, | ||
| 1413 | ... | ||
| 1414 | ) | ||
| 1415 | { | ||
| 1416 | Assert(phTableView && phColumns); | ||
| 1417 | |||
| 1418 | static DWORD dwUniquifyValue = ::GetTickCount(); | ||
| 1419 | |||
| 1420 | HRESULT hr = S_OK; | ||
| 1421 | UINT er = ERROR_SUCCESS; | ||
| 1422 | |||
| 1423 | LPWSTR pwzQuery = NULL; | ||
| 1424 | PMSIHANDLE hTempRec; | ||
| 1425 | DWORD i; | ||
| 1426 | va_list args; | ||
| 1427 | |||
| 1428 | LPWSTR pwzData = NULL; | ||
| 1429 | LPWSTR pwzUniquify = NULL; | ||
| 1430 | |||
| 1431 | // | ||
| 1432 | // if we don't have a table and its columns already | ||
| 1433 | // | ||
| 1434 | if (NULL == *phTableView) | ||
| 1435 | { | ||
| 1436 | // set the query | ||
| 1437 | hr = StrAllocFormatted(&pwzQuery, L"SELECT * FROM `%s`",wzTable); | ||
| 1438 | ExitOnFailure(hr, "failed to allocate string for query"); | ||
| 1439 | |||
| 1440 | // Open and Execute the temp View | ||
| 1441 | hr = WcaOpenExecuteView(pwzQuery, phTableView); | ||
| 1442 | ExitOnFailure(hr, "failed to openexecute temp view with query %ls", pwzQuery); | ||
| 1443 | } | ||
| 1444 | |||
| 1445 | if (NULL == *phColumns) | ||
| 1446 | { | ||
| 1447 | // use GetColumnInfo to populate the datatype record | ||
| 1448 | er = ::MsiViewGetColumnInfo(*phTableView, MSICOLINFO_TYPES, phColumns); | ||
| 1449 | ExitOnWin32Error(er, hr, "failed to columns for table: %ls", wzTable); | ||
| 1450 | } | ||
| 1451 | AssertSz(::MsiRecordGetFieldCount(*phColumns) == cColumns, "passed in argument does not match number of columns in table"); | ||
| 1452 | |||
| 1453 | // | ||
| 1454 | // create the temp record | ||
| 1455 | // | ||
| 1456 | hTempRec = ::MsiCreateRecord(cColumns); | ||
| 1457 | ExitOnNull(hTempRec, hr, E_UNEXPECTED, "could not create temp record for table: %ls", wzTable); | ||
| 1458 | |||
| 1459 | // | ||
| 1460 | // loop through all the columns filling in the data | ||
| 1461 | // | ||
| 1462 | va_start(args, cColumns); | ||
| 1463 | for (i = 1; i <= cColumns; i++) | ||
| 1464 | { | ||
| 1465 | hr = WcaGetRecordString(*phColumns, i, &pwzData); | ||
| 1466 | ExitOnFailure(hr, "failed to get the data type for %d", i); | ||
| 1467 | |||
| 1468 | // if data type is string write string | ||
| 1469 | if (L's' == *pwzData || L'S' == *pwzData || L'g' == *pwzData || L'G' == *pwzData || L'l' == *pwzData || L'L' == *pwzData) | ||
| 1470 | { | ||
| 1471 | LPCWSTR wz = va_arg(args, WCHAR*); | ||
| 1472 | |||
| 1473 | // if this is the column that is supposed to be unique add the time stamp on the end | ||
| 1474 | if (uiUniquifyColumn == i) | ||
| 1475 | { | ||
| 1476 | hr = StrAllocFormatted(&pwzUniquify, L"%s%u", wz, ++dwUniquifyValue); // up the count so we have no collisions on the unique name | ||
| 1477 | ExitOnFailure(hr, "failed to allocate string for unique column: %d", uiUniquifyColumn); | ||
| 1478 | |||
| 1479 | wz = pwzUniquify; | ||
| 1480 | } | ||
| 1481 | |||
| 1482 | er = ::MsiRecordSetStringW(hTempRec, i, wz); | ||
| 1483 | ExitOnWin32Error(er, hr, "failed to set string value at position %d", i); | ||
| 1484 | } | ||
| 1485 | // if data type is integer write integer | ||
| 1486 | else if (L'i' == *pwzData || L'I' == *pwzData || L'j' == *pwzData || L'J' == *pwzData) | ||
| 1487 | { | ||
| 1488 | AssertSz(uiUniquifyColumn != i, "Cannot uniquify an integer column"); | ||
| 1489 | int iData = va_arg(args, int); | ||
| 1490 | |||
| 1491 | er = ::MsiRecordSetInteger(hTempRec, i, iData); | ||
| 1492 | ExitOnWin32Error(er, hr, "failed to set integer value at position %d", i); | ||
| 1493 | } | ||
| 1494 | else | ||
| 1495 | { | ||
| 1496 | // not supporting binary streams so error out | ||
| 1497 | hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH); | ||
| 1498 | ExitOnRootFailure(hr, "unsupported data type '%ls' in column: %d", pwzData, i); | ||
| 1499 | } | ||
| 1500 | } | ||
| 1501 | va_end(args); | ||
| 1502 | |||
| 1503 | // | ||
| 1504 | // add the temporary record to the MSI | ||
| 1505 | // | ||
| 1506 | er = ::MsiViewModify(*phTableView, MSIMODIFY_INSERT_TEMPORARY, hTempRec); | ||
| 1507 | hr = HRESULT_FROM_WIN32(er); | ||
| 1508 | if (FAILED(hr)) | ||
| 1509 | { | ||
| 1510 | if (pdbError) | ||
| 1511 | { | ||
| 1512 | // MSI provides only a generic ERROR_FUNCTION_FAILED if a temporary row | ||
| 1513 | // can't be inserted; if we're being asked to provide the detailed error, | ||
| 1514 | // get it using the MSIMODIFY_VALIDATE_NEW flag | ||
| 1515 | er = ::MsiViewModify(*phTableView, MSIMODIFY_VALIDATE_NEW, hTempRec); | ||
| 1516 | hr = HRESULT_FROM_WIN32(er); | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | WCHAR wzBuf[MAX_PATH]; | ||
| 1520 | DWORD cchBuf = countof(wzBuf); | ||
| 1521 | MSIDBERROR dbErr = ::MsiViewGetErrorW(*phTableView, wzBuf, &cchBuf); | ||
| 1522 | if (pdbError) | ||
| 1523 | { | ||
| 1524 | *pdbError = dbErr; | ||
| 1525 | } | ||
| 1526 | ExitOnFailure(hr, "failed to add temporary row, dberr: %d, err: %ls", dbErr, wzBuf); | ||
| 1527 | } | ||
| 1528 | |||
| 1529 | LExit: | ||
| 1530 | ReleaseStr(pwzUniquify); | ||
| 1531 | ReleaseStr(pwzData); | ||
| 1532 | ReleaseStr(pwzQuery); | ||
| 1533 | |||
| 1534 | return hr; | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | |||
| 1538 | /******************************************************************** | ||
| 1539 | WcaDumpTable - dumps a table to the log file | ||
| 1540 | |||
| 1541 | ********************************************************************/ | ||
| 1542 | extern "C" HRESULT WIXAPI WcaDumpTable( | ||
| 1543 | __in_z LPCWSTR wzTable | ||
| 1544 | ) | ||
| 1545 | { | ||
| 1546 | HRESULT hr = S_OK; | ||
| 1547 | UINT er = ERROR_SUCCESS; | ||
| 1548 | |||
| 1549 | LPWSTR pwzQuery = NULL; | ||
| 1550 | PMSIHANDLE hView; | ||
| 1551 | PMSIHANDLE hColumns; | ||
| 1552 | DWORD cColumns = 0; | ||
| 1553 | PMSIHANDLE hRec; | ||
| 1554 | |||
| 1555 | LPWSTR pwzData = NULL; | ||
| 1556 | LPWSTR pwzPrint = NULL; | ||
| 1557 | |||
| 1558 | hr = StrAllocFormatted(&pwzQuery, L"SELECT * FROM `%s`",wzTable); | ||
| 1559 | ExitOnFailure(hr, "failed to allocate string for query"); | ||
| 1560 | |||
| 1561 | // Open and Execute the temp View | ||
| 1562 | hr = WcaOpenExecuteView(pwzQuery, &hView); | ||
| 1563 | ExitOnFailure(hr, "failed to openexecute temp view with query %ls", pwzQuery); | ||
| 1564 | |||
| 1565 | // Use GetColumnInfo to populate the names of the columns. | ||
| 1566 | er = ::MsiViewGetColumnInfo(hView, MSICOLINFO_NAMES, &hColumns); | ||
| 1567 | hr = HRESULT_FROM_WIN32(er); | ||
| 1568 | ExitOnFailure(hr, "failed to get column info for table: %ls", wzTable); | ||
| 1569 | |||
| 1570 | cColumns = ::MsiRecordGetFieldCount(hColumns); | ||
| 1571 | |||
| 1572 | WcaLog(LOGMSG_STANDARD, "--- Begin Table Dump %ls ---", wzTable); | ||
| 1573 | |||
| 1574 | // Loop through all the columns filling in the data. | ||
| 1575 | for (DWORD i = 1; i <= cColumns; i++) | ||
| 1576 | { | ||
| 1577 | hr = WcaGetRecordString(hColumns, i, &pwzData); | ||
| 1578 | ExitOnFailure(hr, "failed to get the column name for %d", i); | ||
| 1579 | |||
| 1580 | hr = StrAllocConcat(&pwzPrint, pwzData, 0); | ||
| 1581 | ExitOnFailure(hr, "Failed to add column name."); | ||
| 1582 | |||
| 1583 | hr = StrAllocConcat(&pwzPrint, L"\t", 1); | ||
| 1584 | ExitOnFailure(hr, "Failed to add column name."); | ||
| 1585 | } | ||
| 1586 | |||
| 1587 | WcaLog(LOGMSG_STANDARD, "%ls", pwzPrint); | ||
| 1588 | |||
| 1589 | // Now dump the actual rows. | ||
| 1590 | while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) | ||
| 1591 | { | ||
| 1592 | if (pwzPrint && *pwzPrint) | ||
| 1593 | { | ||
| 1594 | *pwzPrint = L'\0'; | ||
| 1595 | } | ||
| 1596 | |||
| 1597 | for (DWORD i = 1; i <= cColumns; i++) | ||
| 1598 | { | ||
| 1599 | hr = WcaGetRecordString(hRec, i, &pwzData); | ||
| 1600 | ExitOnFailure(hr, "failed to get the column name for %d", i); | ||
| 1601 | |||
| 1602 | hr = StrAllocConcat(&pwzPrint, pwzData, 0); | ||
| 1603 | ExitOnFailure(hr, "Failed to add column name."); | ||
| 1604 | |||
| 1605 | hr = StrAllocConcat(&pwzPrint, L"\t", 1); | ||
| 1606 | ExitOnFailure(hr, "Failed to add column name."); | ||
| 1607 | } | ||
| 1608 | |||
| 1609 | WcaLog(LOGMSG_STANDARD, "%ls", pwzPrint); | ||
| 1610 | } | ||
| 1611 | |||
| 1612 | WcaLog(LOGMSG_STANDARD, "--- End Table Dump %ls ---", wzTable); | ||
| 1613 | |||
| 1614 | LExit: | ||
| 1615 | ReleaseStr(pwzPrint); | ||
| 1616 | ReleaseStr(pwzData); | ||
| 1617 | ReleaseStr(pwzQuery); | ||
| 1618 | |||
| 1619 | return hr; | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | |||
| 1623 | HRESULT WIXAPI WcaDeferredActionRequiresReboot() | ||
| 1624 | { | ||
| 1625 | HRESULT hr = S_OK; | ||
| 1626 | ATOM atomReboot = 0; | ||
| 1627 | |||
| 1628 | atomReboot = ::GlobalAddAtomW(L"WcaDeferredActionRequiresReboot"); | ||
| 1629 | ExitOnNullWithLastError(atomReboot, hr, "Failed to create WcaDeferredActionRequiresReboot global atom."); | ||
| 1630 | |||
| 1631 | LExit: | ||
| 1632 | return hr; | ||
| 1633 | } | ||
| 1634 | |||
| 1635 | |||
| 1636 | BOOL WIXAPI WcaDidDeferredActionRequireReboot() | ||
| 1637 | { | ||
| 1638 | // NOTE: This function does not delete the global atom. That is done | ||
| 1639 | // purposefully so that any other installs that occur after this point also | ||
| 1640 | // require a reboot. | ||
| 1641 | ATOM atomReboot = ::GlobalFindAtomW(L"WcaDeferredActionRequiresReboot"); | ||
| 1642 | return 0 != atomReboot; | ||
| 1643 | } | ||
diff --git a/src/wcautil/wcawrapquery.cpp b/src/wcautil/wcawrapquery.cpp new file mode 100644 index 00000000..f04da10d --- /dev/null +++ b/src/wcautil/wcawrapquery.cpp | |||
| @@ -0,0 +1,717 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | #include "precomp.h" | ||
| 4 | #include "wcawrapquery.h" | ||
| 5 | |||
| 6 | static const LPWSTR ISINSTALLEDCOLUMNNAME = L"ISInstalled"; | ||
| 7 | static const LPWSTR ISACTIONCOLUMNNAME = L"ISAction"; | ||
| 8 | static const LPWSTR SOURCEPATHCOLUMNNAME = L"SourcePath"; | ||
| 9 | static const LPWSTR TARGETPATHCOLUMNNAME = L"TargetPath"; | ||
| 10 | |||
| 11 | // This instantiates a new query object in the deferred CA, and returns the handle to the query | ||
| 12 | WCA_WRAPQUERY_HANDLE WIXAPI GetNewQueryInstance( | ||
| 13 | DWORD dwInColumns, | ||
| 14 | DWORD dwInRows | ||
| 15 | ) | ||
| 16 | { | ||
| 17 | HRESULT hr = S_OK; | ||
| 18 | |||
| 19 | WCA_WRAPQUERY_HANDLE hNewHandle = NULL; | ||
| 20 | |||
| 21 | hNewHandle = static_cast<WCA_WRAPQUERY_HANDLE>(MemAlloc(sizeof(WCA_WRAPQUERY_STRUCT), TRUE)); | ||
| 22 | if (NULL == hNewHandle) | ||
| 23 | { | ||
| 24 | hr = E_OUTOFMEMORY; | ||
| 25 | ExitOnFailure(hr, "Failed to allocate Query Instance"); | ||
| 26 | } | ||
| 27 | |||
| 28 | // Initialize non-array members | ||
| 29 | hNewHandle->dwColumns = dwInColumns; | ||
| 30 | hNewHandle->dwRows = dwInRows; | ||
| 31 | hNewHandle->dwNextIndex = 0; | ||
| 32 | |||
| 33 | // Initialize arrays | ||
| 34 | if (0 != hNewHandle->dwColumns) | ||
| 35 | { | ||
| 36 | hNewHandle->pcdtColumnType = static_cast<eColumnDataType *>(MemAlloc(hNewHandle->dwColumns * sizeof(eColumnDataType), TRUE)); | ||
| 37 | if (NULL == hNewHandle->pcdtColumnType) | ||
| 38 | { | ||
| 39 | hr = E_OUTOFMEMORY; | ||
| 40 | ExitOnFailure(hr, "Failed to allocate column type array"); | ||
| 41 | } | ||
| 42 | |||
| 43 | hNewHandle->ppwzColumnNames = static_cast<LPWSTR *>(MemAlloc(hNewHandle->dwColumns * sizeof(LPWSTR), TRUE)); | ||
| 44 | if (NULL == hNewHandle->ppwzColumnNames) | ||
| 45 | { | ||
| 46 | hr = E_OUTOFMEMORY; | ||
| 47 | ExitOnFailure(hr, "Failed to allocate column names array"); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | for (DWORD i=0;i<hNewHandle->dwColumns;i++) | ||
| 52 | { | ||
| 53 | hNewHandle->pcdtColumnType[i] = cdtUnknown; | ||
| 54 | hNewHandle->ppwzColumnNames[i] = NULL; | ||
| 55 | } | ||
| 56 | |||
| 57 | if (0 != hNewHandle->dwRows) | ||
| 58 | { | ||
| 59 | hNewHandle->phRecords = static_cast<MSIHANDLE *>(MemAlloc(hNewHandle->dwRows * sizeof(MSIHANDLE), TRUE)); | ||
| 60 | if (NULL == hNewHandle->phRecords) | ||
| 61 | { | ||
| 62 | hr = E_OUTOFMEMORY; | ||
| 63 | ExitOnFailure(hr, "Failed to allocate records array"); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | for (DWORD i=0;i<hNewHandle->dwRows;i++) | ||
| 68 | { | ||
| 69 | hNewHandle->phRecords[i] = NULL; | ||
| 70 | } | ||
| 71 | |||
| 72 | return hNewHandle; | ||
| 73 | |||
| 74 | LExit: | ||
| 75 | // The handle isn't complete, so destroy any memory it allocated before returning NULL | ||
| 76 | if (NULL != hNewHandle) | ||
| 77 | { | ||
| 78 | WcaFinishUnwrapQuery(hNewHandle); | ||
| 79 | } | ||
| 80 | |||
| 81 | return NULL; | ||
| 82 | } | ||
| 83 | |||
| 84 | // This function takes in the column type string from MsiViewGetColumnInfo, and returns | ||
| 85 | // whether the column stores strings, ints, binary streams, or | ||
| 86 | // cdtUnknown if this information couldn't be determined. | ||
| 87 | eColumnDataType WIXAPI GetDataTypeFromString( | ||
| 88 | LPCWSTR pwzTypeString | ||
| 89 | ) | ||
| 90 | { | ||
| 91 | if (NULL == pwzTypeString || 0 == wcslen(pwzTypeString)) | ||
| 92 | { | ||
| 93 | return cdtUnknown; | ||
| 94 | } | ||
| 95 | |||
| 96 | switch (pwzTypeString[0]) | ||
| 97 | { | ||
| 98 | case 'v': | ||
| 99 | case 'V': | ||
| 100 | case 'o': | ||
| 101 | case 'O': | ||
| 102 | return cdtStream; | ||
| 103 | |||
| 104 | case 'g': | ||
| 105 | case 'G': | ||
| 106 | case 's': | ||
| 107 | case 'S': | ||
| 108 | case 'l': | ||
| 109 | case 'L': | ||
| 110 | return cdtString; | ||
| 111 | |||
| 112 | case 'i': | ||
| 113 | case 'I': | ||
| 114 | case 'j': | ||
| 115 | case 'J': | ||
| 116 | return cdtInt; | ||
| 117 | |||
| 118 | default: | ||
| 119 | return cdtUnknown; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | HRESULT WIXAPI WcaWrapEmptyQuery( | ||
| 124 | __inout LPWSTR * ppwzCustomActionData | ||
| 125 | ) | ||
| 126 | { | ||
| 127 | HRESULT hr = S_OK; | ||
| 128 | |||
| 129 | WcaLog(LOGMSG_TRACEONLY, "Wrapping result of empty query"); | ||
| 130 | |||
| 131 | hr = WcaWriteIntegerToCaData(static_cast<int>(wqaTableBegin), ppwzCustomActionData); | ||
| 132 | ExitOnFailure(hr, "Failed to write table begin marker to custom action data"); | ||
| 133 | |||
| 134 | hr = WcaWriteIntegerToCaData(0, ppwzCustomActionData); | ||
| 135 | ExitOnFailure(hr, "Failed to write number of columns to custom action data"); | ||
| 136 | |||
| 137 | hr = WcaWriteIntegerToCaData(0, ppwzCustomActionData); | ||
| 138 | ExitOnFailure(hr, "Failed to write number of rows to custom action data"); | ||
| 139 | |||
| 140 | hr = WcaWriteIntegerToCaData(static_cast<int>(wqaTableFinish), ppwzCustomActionData); | ||
| 141 | ExitOnFailure(hr, "Failed to write table finish marker to custom action data"); | ||
| 142 | |||
| 143 | // WcaLog(LOGMSG_TRACEONLY, "Finished wrapping result of empty query"); | ||
| 144 | |||
| 145 | LExit: | ||
| 146 | return hr; | ||
| 147 | } | ||
| 148 | |||
| 149 | /******************************************************************** | ||
| 150 | WcaWrapQuery() - wraps a view and transmits it through the | ||
| 151 | CustomActionData property | ||
| 152 | |||
| 153 | ********************************************************************/ | ||
| 154 | HRESULT WIXAPI WcaWrapQuery( | ||
| 155 | __in_z LPCWSTR pwzQuery, | ||
| 156 | __inout LPWSTR * ppwzCustomActionData, | ||
| 157 | __in_opt DWORD dwFormatMask, | ||
| 158 | __in_opt DWORD dwComponentColumn, | ||
| 159 | __in_opt DWORD dwDirectoryColumn | ||
| 160 | ) | ||
| 161 | { | ||
| 162 | HRESULT hr = S_OK; | ||
| 163 | HRESULT hrTemp = S_OK; | ||
| 164 | UINT er = ERROR_SUCCESS; | ||
| 165 | UINT cViewColumns; | ||
| 166 | eColumnDataType *pcdtColumnTypeList = NULL; | ||
| 167 | LPWSTR pwzData = NULL; | ||
| 168 | LPWSTR pwzColumnData = NULL; | ||
| 169 | LPWSTR pwzRecordData = NULL; | ||
| 170 | BYTE* pbData = NULL; | ||
| 171 | DWORD dwNumRecords = 0; | ||
| 172 | BOOL fAddComponentState = FALSE; // Add two integer columns to the right side of the query - ISInstalled, and ISAction | ||
| 173 | BOOL fAddDirectoryPath = FALSE; // Add two string columns to the right side of the query - SourcePath, and TargetPath | ||
| 174 | int iTempInteger = 0; | ||
| 175 | |||
| 176 | WCHAR wzPath[MAX_PATH + 1]; | ||
| 177 | DWORD dwLen; | ||
| 178 | INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; | ||
| 179 | INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; | ||
| 180 | |||
| 181 | PMSIHANDLE hColumnTypes, hColumnNames; | ||
| 182 | PMSIHANDLE hView, hRec; | ||
| 183 | |||
| 184 | WcaLog(LOGMSG_TRACEONLY, "Wrapping result of query: \"%ls\"", pwzQuery); | ||
| 185 | |||
| 186 | // open the view | ||
| 187 | hr = WcaOpenExecuteView(pwzQuery, &hView); | ||
| 188 | ExitOnFailure(hr, "Failed to execute view"); | ||
| 189 | |||
| 190 | hr = WcaWriteIntegerToCaData(static_cast<int>(wqaTableBegin), ppwzCustomActionData); | ||
| 191 | ExitOnFailure(hr, "Failed to write table begin marker to custom action data"); | ||
| 192 | |||
| 193 | // WcaLog(LOGMSG_TRACEONLY, "Starting to wrap table's column information", pwzQuery); | ||
| 194 | |||
| 195 | // Use GetColumnInfo to populate the names of the columns. | ||
| 196 | er = ::MsiViewGetColumnInfo(hView, MSICOLINFO_TYPES, &hColumnTypes); | ||
| 197 | ExitOnWin32Error(er, hr, "Failed to get column types"); | ||
| 198 | |||
| 199 | er = ::MsiViewGetColumnInfo(hView, MSICOLINFO_NAMES, &hColumnNames); | ||
| 200 | ExitOnWin32Error(er, hr, "Failed to get column names"); | ||
| 201 | |||
| 202 | cViewColumns = ::MsiRecordGetFieldCount(hColumnTypes); | ||
| 203 | |||
| 204 | if (0xFFFFFFFF == cViewColumns) | ||
| 205 | { | ||
| 206 | // According to MSDN, this return value only happens when the handle is invalid | ||
| 207 | hr = E_HANDLE; | ||
| 208 | ExitOnFailure(hr, "Failed to get number of fields in record"); | ||
| 209 | } | ||
| 210 | |||
| 211 | if (cViewColumns >= dwComponentColumn) | ||
| 212 | { | ||
| 213 | fAddComponentState = TRUE; | ||
| 214 | } | ||
| 215 | else if (0xFFFFFFFF != dwComponentColumn) | ||
| 216 | { | ||
| 217 | hr = E_INVALIDARG; | ||
| 218 | ExitOnFailure(hr, "Component column %d out of range", dwComponentColumn); | ||
| 219 | } | ||
| 220 | |||
| 221 | if (cViewColumns >= dwDirectoryColumn) | ||
| 222 | { | ||
| 223 | fAddDirectoryPath = TRUE; | ||
| 224 | } | ||
| 225 | else if (0xFFFFFFFF != dwDirectoryColumn) | ||
| 226 | { | ||
| 227 | hr = E_INVALIDARG; | ||
| 228 | ExitOnFailure(hr, "Directory column %d out of range", dwDirectoryColumn); | ||
| 229 | } | ||
| 230 | |||
| 231 | hr = WcaWriteIntegerToCaData(static_cast<int>(cViewColumns) + 2 * static_cast<int>(fAddComponentState) + 2 * static_cast<int>(fAddDirectoryPath), ppwzCustomActionData); | ||
| 232 | ExitOnFailure(hr, "Failed to write number of columns to custom action data"); | ||
| 233 | |||
| 234 | pcdtColumnTypeList = new eColumnDataType[cViewColumns]; | ||
| 235 | ExitOnNull(pcdtColumnTypeList, hr, E_OUTOFMEMORY, "Failed to allocate memory to store column info types"); | ||
| 236 | |||
| 237 | // Loop through all the columns reporting information about each one | ||
| 238 | for (DWORD i = 0; i < cViewColumns; i++) | ||
| 239 | { | ||
| 240 | hr = WcaGetRecordString(hColumnNames, i+1, &pwzData); | ||
| 241 | ExitOnFailure(hr, "Failed to get the column %d name", i+1); | ||
| 242 | |||
| 243 | hr = WcaWriteStringToCaData(pwzData, &pwzColumnData); | ||
| 244 | ExitOnFailure(hr, "Failed to write column %d name %ls to custom action data", i+1, pwzData); | ||
| 245 | |||
| 246 | hr = WcaGetRecordString(hColumnTypes, i+1, &pwzData); | ||
| 247 | ExitOnFailure(hr, "Failed to get the column type string for column %d", i+1); | ||
| 248 | |||
| 249 | pcdtColumnTypeList[i] = GetDataTypeFromString(pwzData); | ||
| 250 | |||
| 251 | if (cdtUnknown == pcdtColumnTypeList[i]) | ||
| 252 | { | ||
| 253 | hr = E_INVALIDARG; | ||
| 254 | ExitOnFailure(hr, "Failed to recognize column %d type string: %ls", i+1, pwzData); | ||
| 255 | } | ||
| 256 | |||
| 257 | hr = WcaWriteIntegerToCaData(pcdtColumnTypeList[i], &pwzColumnData); | ||
| 258 | ExitOnFailure(hr, "Failed to write column %d type enumeration to custom action data", i+1); | ||
| 259 | } | ||
| 260 | |||
| 261 | // Add two integer columns to the right side of the query - ISInstalled, and ISAction | ||
| 262 | if (fAddComponentState) | ||
| 263 | { | ||
| 264 | hr = WcaWriteStringToCaData(ISINSTALLEDCOLUMNNAME, &pwzColumnData); | ||
| 265 | ExitOnFailure(hr, "Failed to write extra column %d name %ls to custom action data", cViewColumns + 1, ISINSTALLEDCOLUMNNAME); | ||
| 266 | |||
| 267 | hr = WcaWriteIntegerToCaData(cdtInt, &pwzColumnData); | ||
| 268 | ExitOnFailure(hr, "Failed to write extra column %d type to custom action data", cViewColumns + 1); | ||
| 269 | |||
| 270 | hr = WcaWriteStringToCaData(ISACTIONCOLUMNNAME, &pwzColumnData); | ||
| 271 | ExitOnFailure(hr, "Failed to write extra column %d name %ls to custom action data", cViewColumns + 1, ISACTIONCOLUMNNAME); | ||
| 272 | |||
| 273 | hr = WcaWriteIntegerToCaData(cdtInt, &pwzColumnData); | ||
| 274 | ExitOnFailure(hr, "Failed to write extra column %d type to custom action data", cViewColumns + 1); | ||
| 275 | } | ||
| 276 | |||
| 277 | if (fAddDirectoryPath) | ||
| 278 | { | ||
| 279 | hr = WcaWriteStringToCaData(SOURCEPATHCOLUMNNAME, &pwzColumnData); | ||
| 280 | ExitOnFailure(hr, "Failed to write extra column %d name %ls to custom action data", cViewColumns + 1, SOURCEPATHCOLUMNNAME); | ||
| 281 | |||
| 282 | hr = WcaWriteIntegerToCaData(cdtString, &pwzColumnData); | ||
| 283 | ExitOnFailure(hr, "Failed to write extra column %d type to custom action data", cViewColumns + 1); | ||
| 284 | |||
| 285 | hr = WcaWriteStringToCaData(TARGETPATHCOLUMNNAME, &pwzColumnData); | ||
| 286 | ExitOnFailure(hr, "Failed to write extra column %d name %ls to custom action data", cViewColumns + 1, TARGETPATHCOLUMNNAME); | ||
| 287 | |||
| 288 | hr = WcaWriteIntegerToCaData(cdtString, &pwzColumnData); | ||
| 289 | ExitOnFailure(hr, "Failed to write extra column %d type to custom action data", cViewColumns + 1); | ||
| 290 | } | ||
| 291 | |||
| 292 | // Begin wrapping actual table data | ||
| 293 | //WcaLog(LOGMSG_TRACEONLY, "Starting to wrap table data", pwzQuery); | ||
| 294 | while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) | ||
| 295 | { | ||
| 296 | hr = WcaWriteIntegerToCaData(static_cast<int>(wqaRowBegin), &pwzRecordData); | ||
| 297 | ExitOnFailure(hr, "Failed to write row begin marker to custom action data"); | ||
| 298 | |||
| 299 | for (DWORD i = 0; i < cViewColumns; i++) | ||
| 300 | { | ||
| 301 | switch (pcdtColumnTypeList[i]) | ||
| 302 | { | ||
| 303 | case cdtString: | ||
| 304 | // If we were given a format mask, we're not past the index, and it's set to true for this column, then format the string | ||
| 305 | if (i < (sizeof(dwFormatMask) * 8) && (dwFormatMask & (1 << i))) | ||
| 306 | { | ||
| 307 | hr = WcaGetRecordFormattedString(hRec, i + 1, &pwzData); | ||
| 308 | } | ||
| 309 | else | ||
| 310 | { | ||
| 311 | hr = WcaGetRecordString(hRec, i + 1, &pwzData); | ||
| 312 | } | ||
| 313 | ExitOnFailure(hr, "Failed to get string for column %d", i + 1); | ||
| 314 | |||
| 315 | hr = WcaWriteStringToCaData(pwzData, &pwzRecordData); | ||
| 316 | ExitOnFailure(hr, "Failed to write string to temporary record custom action data for column %d", i + 1); | ||
| 317 | break; | ||
| 318 | |||
| 319 | case cdtInt: | ||
| 320 | if (i < (sizeof(dwFormatMask) * 8) && (dwFormatMask & (1 << i))) | ||
| 321 | { | ||
| 322 | hr = WcaGetRecordFormattedInteger(hRec, i + 1, &iTempInteger); | ||
| 323 | } | ||
| 324 | else | ||
| 325 | { | ||
| 326 | hr = WcaGetRecordInteger(hRec, i + 1, &iTempInteger); | ||
| 327 | } | ||
| 328 | ExitOnFailure(hr, "Failed to get integer for column %d", i + 1); | ||
| 329 | |||
| 330 | hr = WcaWriteIntegerToCaData(iTempInteger, &pwzRecordData); | ||
| 331 | ExitOnFailure(hr, "Failed to write integer to temporary record custom action data for column %d", i + 1); | ||
| 332 | break; | ||
| 333 | |||
| 334 | case cdtStream: | ||
| 335 | hr = E_NOTIMPL; | ||
| 336 | ExitOnFailure(hr, "A query was wrapped which contained a binary stream data field in column %d - however, the ability to wrap stream data fields is not implemented at this time", i); | ||
| 337 | break; | ||
| 338 | |||
| 339 | case cdtUnknown: | ||
| 340 | default: | ||
| 341 | hr = E_INVALIDARG; | ||
| 342 | ExitOnFailure(hr, "Failed to recognize column type enumeration %d for column %d", pcdtColumnTypeList[i], i + 1); | ||
| 343 | } | ||
| 344 | } | ||
| 345 | |||
| 346 | // Add two integer columns to the right side of the query - ISInstalled, and ISAction | ||
| 347 | if (fAddComponentState) | ||
| 348 | { | ||
| 349 | // Get the component ID | ||
| 350 | hr = WcaGetRecordString(hRec, dwComponentColumn, &pwzData); | ||
| 351 | ExitOnFailure(hr, "Failed to get component from column %d while adding extra columns", dwComponentColumn); | ||
| 352 | |||
| 353 | if (0 == lstrlenW(pwzData)) | ||
| 354 | { | ||
| 355 | // If no component was provided, set these both to zero as though a structure to store them were allocated with memory zero'd out | ||
| 356 | isInstalled = (INSTALLSTATE)0; | ||
| 357 | isAction = (INSTALLSTATE)0; | ||
| 358 | } | ||
| 359 | else | ||
| 360 | { | ||
| 361 | er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &isInstalled, &isAction); | ||
| 362 | // If we don't get the component state, that may be because the component ID was invalid, but isn't necessarily an error, so write NULL's | ||
| 363 | if (FAILED(HRESULT_FROM_WIN32(er))) | ||
| 364 | { | ||
| 365 | ExitOnFailure(hr, "Failed to get component state for component %ls", pwzData); | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | hr = WcaWriteIntegerToCaData(isInstalled, &pwzRecordData); | ||
| 370 | ExitOnFailure(hr, "Failed to write extra ISInstalled column to custom action data"); | ||
| 371 | |||
| 372 | hr = WcaWriteIntegerToCaData(isAction, &pwzRecordData); | ||
| 373 | ExitOnFailure(hr, "Failed to write extra ISAction column to custom action data"); | ||
| 374 | } | ||
| 375 | |||
| 376 | // Add two string columns to the right side of the query - SourcePath, and TargetPath | ||
| 377 | if (fAddDirectoryPath) | ||
| 378 | { | ||
| 379 | hr = WcaGetRecordString(hRec, dwDirectoryColumn, &pwzData); | ||
| 380 | // If this fails, ignore it, and just leave those columns blank | ||
| 381 | if (SUCCEEDED(hr)) | ||
| 382 | { | ||
| 383 | // Only get source path if the component state is INSTALLSTATE_SOURCE, or if we have no component to check the installstate of | ||
| 384 | if (INSTALLSTATE_SOURCE == isAction || !fAddComponentState) | ||
| 385 | { | ||
| 386 | dwLen = countof(wzPath); | ||
| 387 | er = ::MsiGetSourcePathW(WcaGetInstallHandle(), pwzData, wzPath, &dwLen); | ||
| 388 | hrTemp = HRESULT_FROM_WIN32(er); | ||
| 389 | if (dwLen > countof(wzPath)) | ||
| 390 | { | ||
| 391 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | ||
| 392 | ExitOnRootFailure(hr, "Failed to record entire Source Path for Directory %ls because its length was greater than MAX_PATH.", pwzData); | ||
| 393 | } | ||
| 394 | |||
| 395 | if (SUCCEEDED(hrTemp)) | ||
| 396 | { | ||
| 397 | hr = WcaWriteStringToCaData(wzPath, &pwzRecordData); | ||
| 398 | ExitOnFailure(hr, "Failed to write source path string to record data string"); | ||
| 399 | } | ||
| 400 | else | ||
| 401 | { | ||
| 402 | hr = WcaWriteStringToCaData(L"", &pwzRecordData); | ||
| 403 | ExitOnFailure(hr, "Failed to write empty source path string to record data string"); | ||
| 404 | } | ||
| 405 | } | ||
| 406 | else | ||
| 407 | { | ||
| 408 | hr = WcaWriteStringToCaData(L"", &pwzRecordData); | ||
| 409 | ExitOnFailure(hr, "Failed to write empty source path string before writing target path string to record data string"); | ||
| 410 | } | ||
| 411 | |||
| 412 | dwLen = countof(wzPath); | ||
| 413 | er = ::MsiGetTargetPathW(WcaGetInstallHandle(), pwzData, wzPath, &dwLen); | ||
| 414 | hrTemp = HRESULT_FROM_WIN32(er); | ||
| 415 | if (dwLen > countof(wzPath)) | ||
| 416 | { | ||
| 417 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | ||
| 418 | ExitOnRootFailure(hr, "Failed to record entire Source Path for Directory %ls because its length was greater than MAX_PATH.", pwzData); | ||
| 419 | } | ||
| 420 | if (SUCCEEDED(hrTemp)) | ||
| 421 | { | ||
| 422 | hr = WcaWriteStringToCaData(wzPath, &pwzRecordData); | ||
| 423 | ExitOnFailure(hr, "Failed to write target path string to record data string"); | ||
| 424 | } | ||
| 425 | else | ||
| 426 | { | ||
| 427 | hr = WcaWriteStringToCaData(L"", &pwzRecordData); | ||
| 428 | ExitOnFailure(hr, "Failed to write empty target path string to record data string"); | ||
| 429 | } | ||
| 430 | } | ||
| 431 | else | ||
| 432 | { | ||
| 433 | // Write both fields as blank | ||
| 434 | hr = WcaWriteStringToCaData(L"", &pwzRecordData); | ||
| 435 | hr = WcaWriteStringToCaData(L"", &pwzRecordData); | ||
| 436 | } | ||
| 437 | } | ||
| 438 | |||
| 439 | hr = WcaWriteIntegerToCaData(static_cast<int>(wqaRowFinish), &pwzRecordData); | ||
| 440 | ExitOnFailure(hr, "Failed to write row finish marker to custom action data"); | ||
| 441 | |||
| 442 | ++dwNumRecords; | ||
| 443 | } | ||
| 444 | |||
| 445 | hr = WcaWriteIntegerToCaData(dwNumRecords, ppwzCustomActionData); | ||
| 446 | ExitOnFailure(hr, "Failed to write number of records to custom action data"); | ||
| 447 | |||
| 448 | if (NULL != pwzColumnData) | ||
| 449 | { | ||
| 450 | hr = WcaWriteStringToCaData(pwzColumnData, ppwzCustomActionData); | ||
| 451 | ExitOnFailure(hr, "Failed to write column data to custom action data"); | ||
| 452 | } | ||
| 453 | |||
| 454 | if (NULL != pwzRecordData) | ||
| 455 | { | ||
| 456 | hr = WcaWriteStringToCaData(pwzRecordData, ppwzCustomActionData); | ||
| 457 | ExitOnFailure(hr, "Failed to write record data to custom action data"); | ||
| 458 | } | ||
| 459 | |||
| 460 | hr = WcaWriteIntegerToCaData(static_cast<int>(wqaTableFinish), ppwzCustomActionData); | ||
| 461 | ExitOnFailure(hr, "Failed to write table finish marker to custom action data"); | ||
| 462 | |||
| 463 | // WcaLog(LOGMSG_TRACEONLY, "Finished wrapping result of query: \"%ls\"", pwzQuery); | ||
| 464 | |||
| 465 | LExit: | ||
| 466 | ReleaseStr(pwzData); | ||
| 467 | ReleaseStr(pwzColumnData); | ||
| 468 | ReleaseStr(pwzRecordData); | ||
| 469 | |||
| 470 | ReleaseMem(pbData); | ||
| 471 | |||
| 472 | return hr; | ||
| 473 | } | ||
| 474 | |||
| 475 | |||
| 476 | /******************************************************************** | ||
| 477 | WcaBeginUnwrapQuery() - unwraps a view for direct access from the | ||
| 478 | CustomActionData property | ||
| 479 | |||
| 480 | ********************************************************************/ | ||
| 481 | HRESULT WIXAPI WcaBeginUnwrapQuery( | ||
| 482 | __out WCA_WRAPQUERY_HANDLE * phWrapQuery, | ||
| 483 | __inout LPWSTR * ppwzCustomActionData | ||
| 484 | ) | ||
| 485 | { | ||
| 486 | HRESULT hr = S_OK; | ||
| 487 | int iTempInteger = 0; | ||
| 488 | int iColumns = 0; | ||
| 489 | int iRows = 0; | ||
| 490 | BYTE* pbData = NULL; | ||
| 491 | LPWSTR pwzData = NULL; | ||
| 492 | WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; | ||
| 493 | |||
| 494 | WcaLog(LOGMSG_TRACEONLY, "Unwrapping a query from custom action data"); | ||
| 495 | |||
| 496 | hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger); | ||
| 497 | if (wqaTableBegin != iTempInteger) | ||
| 498 | { | ||
| 499 | hr = E_INVALIDARG; | ||
| 500 | } | ||
| 501 | ExitOnFailure(hr, "Failed to read table begin marker from custom action data (read %d instead)", iTempInteger); | ||
| 502 | |||
| 503 | hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iColumns); | ||
| 504 | ExitOnFailure(hr, "Failed to read number of columns from custom action data"); | ||
| 505 | |||
| 506 | hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iRows); | ||
| 507 | ExitOnFailure(hr, "Failed to read number of rows from custom action data"); | ||
| 508 | |||
| 509 | hWrapQuery = GetNewQueryInstance(iColumns, iRows); | ||
| 510 | if (NULL == hWrapQuery) | ||
| 511 | { | ||
| 512 | hr = E_POINTER; | ||
| 513 | } | ||
| 514 | ExitOnFailure(hr, "Failed to get a query instance with %d columns and %d rows", iColumns, iRows); | ||
| 515 | |||
| 516 | for (int i = 0; i < iColumns; i++) | ||
| 517 | { | ||
| 518 | hr = WcaReadStringFromCaData(ppwzCustomActionData, &(hWrapQuery->ppwzColumnNames[i])); | ||
| 519 | ExitOnFailure(hr, "Failed to read column %d's name from custom action data", i+1); | ||
| 520 | |||
| 521 | hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger); | ||
| 522 | if (cdtString != iTempInteger && cdtInt != iTempInteger && cdtStream != iTempInteger) | ||
| 523 | { | ||
| 524 | hr = E_INVALIDARG; | ||
| 525 | } | ||
| 526 | ExitOnFailure(hr, "Failed to read column %d's type from custom action data", i+1); | ||
| 527 | |||
| 528 | // Set the column type into the actual data structure | ||
| 529 | hWrapQuery->pcdtColumnType[i] = (eColumnDataType)iTempInteger; | ||
| 530 | } | ||
| 531 | |||
| 532 | for (int i = 0; i < iRows; i++) | ||
| 533 | { | ||
| 534 | hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger); | ||
| 535 | if (wqaRowBegin != iTempInteger) | ||
| 536 | { | ||
| 537 | hr = E_INVALIDARG; | ||
| 538 | } | ||
| 539 | ExitOnFailure(hr, "Failed to read begin row marker from custom action data (read %d instead)", iTempInteger); | ||
| 540 | |||
| 541 | hWrapQuery->phRecords[i] = ::MsiCreateRecord((unsigned int)iColumns); | ||
| 542 | |||
| 543 | for (int j = 0; j < iColumns; j++) | ||
| 544 | { | ||
| 545 | switch (hWrapQuery->pcdtColumnType[j]) | ||
| 546 | { | ||
| 547 | case cdtString: | ||
| 548 | hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); | ||
| 549 | ExitOnFailure(hr, "Failed to read string from custom action data"); | ||
| 550 | |||
| 551 | hr = WcaSetRecordString(hWrapQuery->phRecords[i], j+1, pwzData); | ||
| 552 | ExitOnFailure(hr, "Failed to write string %ls to record in column %d", pwzData, j+1); | ||
| 553 | break; | ||
| 554 | |||
| 555 | case cdtInt: | ||
| 556 | WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger); | ||
| 557 | ExitOnFailure(hr, "Failed to read integer from custom action data"); | ||
| 558 | |||
| 559 | WcaSetRecordInteger(hWrapQuery->phRecords[i], j+1, iTempInteger); | ||
| 560 | ExitOnFailure(hr, "Failed to write integer %d to record in column %d", iTempInteger, j+1); | ||
| 561 | break; | ||
| 562 | |||
| 563 | case cdtStream: | ||
| 564 | hr = E_NOTIMPL; | ||
| 565 | ExitOnFailure(hr, "A query was wrapped which contained a stream data field - however, the ability to wrap stream data fields is not implemented at this time"); | ||
| 566 | break; | ||
| 567 | |||
| 568 | case cdtUnknown: | ||
| 569 | default: | ||
| 570 | hr = E_INVALIDARG; | ||
| 571 | ExitOnFailure(hr, "Failed to recognize column type enumeration %d for column %d", hWrapQuery->pcdtColumnType[j+1], i+1); | ||
| 572 | } | ||
| 573 | } | ||
| 574 | |||
| 575 | hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger); | ||
| 576 | if (wqaRowFinish != iTempInteger) | ||
| 577 | { | ||
| 578 | hr = E_INVALIDARG; | ||
| 579 | } | ||
| 580 | ExitOnFailure(hr, "Failed to read row finish marker from custom action data (read %d instead)", iTempInteger); | ||
| 581 | } | ||
| 582 | |||
| 583 | hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger); | ||
| 584 | if (wqaTableFinish != iTempInteger) | ||
| 585 | { | ||
| 586 | hr = E_INVALIDARG; | ||
| 587 | } | ||
| 588 | ExitOnFailure(hr, "Failed to read table finish marker from custom action data (read %d instead)", iTempInteger); | ||
| 589 | |||
| 590 | *phWrapQuery = hWrapQuery; | ||
| 591 | |||
| 592 | // WcaLog(LOGMSG_TRACEONLY, "Successfully finished unwrapping a query from custom action data"); | ||
| 593 | |||
| 594 | LExit: | ||
| 595 | ReleaseStr(pwzData); | ||
| 596 | ReleaseMem(pbData); | ||
| 597 | |||
| 598 | return hr; | ||
| 599 | } | ||
| 600 | |||
| 601 | // This function returns the total number of records in a query | ||
| 602 | DWORD WIXAPI WcaGetQueryRecords( | ||
| 603 | __in const WCA_WRAPQUERY_HANDLE hWrapQuery | ||
| 604 | ) | ||
| 605 | { | ||
| 606 | return hWrapQuery->dwRows; | ||
| 607 | } | ||
| 608 | |||
| 609 | // This function resets a query back to its first row, so that the next fetch returns the first record | ||
| 610 | void WIXAPI WcaFetchWrappedReset( | ||
| 611 | __in WCA_WRAPQUERY_HANDLE hWrapQuery | ||
| 612 | ) | ||
| 613 | { | ||
| 614 | hWrapQuery->dwNextIndex = 0; | ||
| 615 | } | ||
| 616 | |||
| 617 | // Fetches the next record in the query | ||
| 618 | // NOTE: the MSIHANDLE returned by this function should not be released, as it is the same handle used by the query object to maintain the item. | ||
| 619 | // so, don't use this function with PMSIHANDLE objects! | ||
| 620 | HRESULT WIXAPI WcaFetchWrappedRecord( | ||
| 621 | __in WCA_WRAPQUERY_HANDLE hWrapQuery, | ||
| 622 | __out MSIHANDLE* phRec | ||
| 623 | ) | ||
| 624 | { | ||
| 625 | DWORD dwNextIndex = hWrapQuery->dwNextIndex; | ||
| 626 | |||
| 627 | if (dwNextIndex >= hWrapQuery->dwRows) | ||
| 628 | { | ||
| 629 | return E_NOMOREITEMS; | ||
| 630 | } | ||
| 631 | |||
| 632 | if (NULL == hWrapQuery->phRecords[dwNextIndex]) | ||
| 633 | { | ||
| 634 | return E_HANDLE; | ||
| 635 | } | ||
| 636 | |||
| 637 | *phRec = hWrapQuery->phRecords[hWrapQuery->dwNextIndex]; | ||
| 638 | |||
| 639 | // Increment our next index variable | ||
| 640 | ++hWrapQuery->dwNextIndex; | ||
| 641 | |||
| 642 | return S_OK; | ||
| 643 | } | ||
| 644 | |||
| 645 | // Fetch the next record in the query where the string value in column dwComparisonColumn equals the value pwzExpectedValue | ||
| 646 | // NOTE: the MSIHANDLE returned by this function should not be released, as it is the same handle used by the query object to maintain the item. | ||
| 647 | // so, don't use this function with PMSIHANDLE objects! | ||
| 648 | HRESULT WIXAPI WcaFetchWrappedRecordWhereString( | ||
| 649 | __in WCA_WRAPQUERY_HANDLE hWrapQuery, | ||
| 650 | __in DWORD dwComparisonColumn, | ||
| 651 | __in_z LPCWSTR pwzExpectedValue, | ||
| 652 | __out MSIHANDLE* phRec | ||
| 653 | ) | ||
| 654 | { | ||
| 655 | HRESULT hr = S_OK; | ||
| 656 | MSIHANDLE hRec = NULL; | ||
| 657 | LPWSTR pwzData = NULL; | ||
| 658 | |||
| 659 | while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) | ||
| 660 | { | ||
| 661 | ExitOnFailure(hr, "Failed to fetch a wrapped record"); | ||
| 662 | |||
| 663 | hr = WcaGetRecordString(hRec, dwComparisonColumn, &pwzData); | ||
| 664 | ExitOnFailure(hr, "Failed to get record string in column %d", dwComparisonColumn); | ||
| 665 | |||
| 666 | if (0 == lstrcmpW(pwzData, pwzExpectedValue)) | ||
| 667 | { | ||
| 668 | *phRec = hRec; | ||
| 669 | ExitFunction1(hr = S_OK); | ||
| 670 | } | ||
| 671 | } | ||
| 672 | // If we errored here but not because there were no records left, write an error to the log | ||
| 673 | if (hr != E_NOMOREITEMS) | ||
| 674 | { | ||
| 675 | ExitOnFailure(hr, "Failed while searching for a wrapped record where column %d is set to %ls", dwComparisonColumn, pwzExpectedValue); | ||
| 676 | } | ||
| 677 | |||
| 678 | LExit: | ||
| 679 | ReleaseStr(pwzData); | ||
| 680 | |||
| 681 | return hr; | ||
| 682 | } | ||
| 683 | |||
| 684 | /******************************************************************** | ||
| 685 | WcaBeginUnwrapQuery() - Finishes unwrapping a view for direct access | ||
| 686 | from the CustomActionData property | ||
| 687 | |||
| 688 | ********************************************************************/ | ||
| 689 | void WIXAPI WcaFinishUnwrapQuery( | ||
| 690 | __in_opt WCA_WRAPQUERY_HANDLE hWrapQuery | ||
| 691 | ) | ||
| 692 | { | ||
| 693 | if (NULL == hWrapQuery) | ||
| 694 | { | ||
| 695 | WcaLog(LOGMSG_TRACEONLY, "Failed to finish an unwrap query - ignoring"); | ||
| 696 | return; | ||
| 697 | } | ||
| 698 | |||
| 699 | ReleaseMem(hWrapQuery->pcdtColumnType); | ||
| 700 | |||
| 701 | for (DWORD i=0;i<hWrapQuery->dwColumns;i++) | ||
| 702 | { | ||
| 703 | ReleaseStr(hWrapQuery->ppwzColumnNames[i]); | ||
| 704 | } | ||
| 705 | ReleaseMem(hWrapQuery->ppwzColumnNames); | ||
| 706 | |||
| 707 | for (DWORD i=0;i<hWrapQuery->dwRows;i++) | ||
| 708 | { | ||
| 709 | if (NULL != hWrapQuery->phRecords[i]) | ||
| 710 | { | ||
| 711 | ::MsiCloseHandle(hWrapQuery->phRecords[i]); | ||
| 712 | } | ||
| 713 | } | ||
| 714 | ReleaseMem(hWrapQuery->phRecords); | ||
| 715 | |||
| 716 | ReleaseMem(hWrapQuery); | ||
| 717 | } | ||
