Repository: Squirrel/Squirrel.Windows Branch: develop Commit: 51f5e2cb01ad Files: 216 Total size: 58.8 MB Directory structure: gitextract_rd62uedp/ ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ └── bug_report.md │ └── workflows/ │ └── build.yml ├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── COPYING ├── README.md ├── Squirrel.sln ├── devbuild.cmd ├── docs/ │ ├── contributing/ │ │ ├── branching-strategy.md │ │ ├── building-squirrel.md │ │ ├── contributing.md │ │ └── vs-solution-overview.md │ ├── faq.md │ ├── getting-started/ │ │ ├── 0-overview.md │ │ ├── 1-integrating.md │ │ ├── 2-packaging.md │ │ ├── 3-distributing.md │ │ ├── 4-installing.md │ │ └── 5-updating.md │ ├── goals.md │ ├── readme.md │ └── using/ │ ├── amazon-s3.md │ ├── application-signing.md │ ├── custom-squirrel-events-non-cs.md │ ├── custom-squirrel-events.md │ ├── debugging-installs.md │ ├── debugging-updates.md │ ├── delta-packages.md │ ├── github.md │ ├── install-process.md │ ├── loading-gif.md │ ├── machine-wide-installs.md │ ├── microsoft-iis.md │ ├── naming.md │ ├── nuget-package-metadata.md │ ├── octopack.md │ ├── packaging-tools.md │ ├── squirrel-command-line.md │ ├── staged-rollouts.md │ ├── teamcity.md │ ├── update-manager.md │ ├── update-process.md │ ├── visual-studio-packaging.md │ └── x-doc-template.md ├── src/ │ ├── Directory.Build.props │ ├── Setup/ │ │ ├── FxHelper.cpp │ │ ├── FxHelper.h │ │ ├── MachineInstaller.cpp │ │ ├── MachineInstaller.h │ │ ├── Setup.h │ │ ├── Setup.rc │ │ ├── Setup.vcxproj │ │ ├── Setup.vcxproj.filters │ │ ├── UpdateRunner.cpp │ │ ├── UpdateRunner.h │ │ ├── compat.manifest │ │ ├── resource.h │ │ ├── stdafx.cpp │ │ ├── stdafx.h │ │ ├── targetver.h │ │ ├── unzip.cpp │ │ ├── unzip.h │ │ ├── winmain.cpp │ │ └── wtl90/ │ │ ├── atlapp.h │ │ ├── atlcrack.h │ │ ├── atlctrls.h │ │ ├── atlctrlw.h │ │ ├── atlctrlx.h │ │ ├── atlddx.h │ │ ├── atldlgs.h │ │ ├── atldwm.h │ │ ├── atlfind.h │ │ ├── atlframe.h │ │ ├── atlgdi.h │ │ ├── atlmisc.h │ │ ├── atlprint.h │ │ ├── atlres.h │ │ ├── atlresce.h │ │ ├── atlribbon.h │ │ ├── atlscrl.h │ │ ├── atlsplit.h │ │ ├── atltheme.h │ │ ├── atluser.h │ │ ├── atlwince.h │ │ └── atlwinx.h │ ├── Squirrel/ │ │ ├── ApplyReleasesProgress.cs │ │ ├── BinaryPatchUtility.cs │ │ ├── ContentType.cs │ │ ├── DeltaPackage.cs │ │ ├── EnumerableExtensions.cs │ │ ├── FileDownloader.cs │ │ ├── IUpdateManager.cs │ │ ├── MarkdownSharp.cs │ │ ├── MsDeltaCompression.cs │ │ ├── NativeMethods.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── ReleaseEntry.cs │ │ ├── ReleaseExtensions.cs │ │ ├── ReleasePackage.cs │ │ ├── ShellFile.cs │ │ ├── SimpleJson/ │ │ │ └── SimpleJson.cs │ │ ├── SimpleSplat/ │ │ │ ├── AssemblyFinder.cs │ │ │ ├── Logging.cs │ │ │ ├── MemoizingMRUCache.cs │ │ │ ├── ModeDetector.cs │ │ │ ├── PlatformModeDetector.cs │ │ │ └── ServiceLocation.cs │ │ ├── Squirrel.csproj │ │ ├── SquirrelAwareApp.cs │ │ ├── SquirrelAwareExecutableDetector.cs │ │ ├── TaskbarHelper.cs │ │ ├── TrayHelper.cs │ │ ├── UpdateInfo.cs │ │ ├── UpdateManager.ApplyReleases.cs │ │ ├── UpdateManager.CheckForUpdates.cs │ │ ├── UpdateManager.DownloadReleases.cs │ │ ├── UpdateManager.Factory.cs │ │ ├── UpdateManager.InstallHelpers.cs │ │ ├── UpdateManager.cs │ │ └── Utility.cs │ ├── Squirrel.nuspec │ ├── StubExecutable/ │ │ ├── LICENSE.md │ │ ├── Resource.h │ │ ├── Semver200_comparator.cpp │ │ ├── Semver200_parser.cpp │ │ ├── StubExecutable.cpp │ │ ├── StubExecutable.h │ │ ├── StubExecutable.rc │ │ ├── StubExecutable.vcxproj │ │ ├── StubExecutable.vcxproj.filters │ │ ├── semver200.h │ │ ├── stdafx.cpp │ │ ├── stdafx.h │ │ ├── targetver.h │ │ ├── version.h │ │ └── version.inl │ ├── SyncReleases/ │ │ ├── App.config │ │ ├── Mono.Options/ │ │ │ └── Options.cs │ │ ├── Program.cs │ │ ├── SyncImplementations.cs │ │ └── SyncReleases.csproj │ ├── Update/ │ │ ├── AnimatedGifWindow.cs │ │ ├── App.config │ │ ├── AuthenticodeTools.cs │ │ ├── CopStache.cs │ │ ├── Mono.Options/ │ │ │ └── Options.cs │ │ ├── Program.cs │ │ ├── StartupOption.cs │ │ ├── Update-Mono.csproj │ │ ├── Update.com │ │ ├── Update.csproj │ │ ├── app.manifest │ │ └── packages.config │ ├── WriteZipToSetup/ │ │ ├── WriteZipToSetup.cpp │ │ ├── WriteZipToSetup.vcxproj │ │ ├── WriteZipToSetup.vcxproj.filters │ │ ├── stdafx.cpp │ │ └── stdafx.h │ ├── build_official.cmd │ └── squirrel.windows.props ├── test/ │ ├── Directory.Build.props │ ├── Squirrel.Tests/ │ │ ├── ApplyReleasesProgressTests.cs │ │ ├── ApplyReleasesTests.cs │ │ ├── CheckForUpdateTests.cs │ │ ├── ContentTypeTests.cs │ │ ├── DeltaPackageTests.cs │ │ ├── DownloadReleasesTests.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── ReleaseEntryTests.cs │ │ ├── ReleasePackageTests.cs │ │ ├── Squirrel.Tests.csproj │ │ ├── SquirrelAwareExecutableDetectorTests.cs │ │ ├── TestHelpers/ │ │ │ ├── AssertExtensions.cs │ │ │ ├── ExposedClass.cs │ │ │ ├── ExposedObject.cs │ │ │ ├── ExposedObjectHelper.cs │ │ │ ├── IntegrationTestHelper.cs │ │ │ └── StaticHttpServer.cs │ │ ├── UpdateManagerTests.cs │ │ ├── UtilityTests.cs │ │ └── fixtures/ │ │ ├── Caliburn.Micro.1.4.1.nupkg │ │ ├── Caliburn.Micro.1.5.2.nupkg │ │ ├── CaliburnMicroDemo.1.0.0.nupkg │ │ ├── ProjectDependsOnJsonDotNet.1.0.nupkg │ │ ├── ProjectWithContent.1.0.0.0-beta-full.nupkg │ │ ├── ProjectWithContent.1.0.0.0-beta.nupkg │ │ ├── RELEASES-OnePointOh │ │ ├── RELEASES-OnePointOne │ │ ├── SpecialCharacters-0.1.0-full.nupkg │ │ ├── Squirrel.Core.1.0.0.0-full.nupkg │ │ ├── Squirrel.Core.1.0.0.0.nupkg │ │ ├── Squirrel.Core.1.1.0.0-delta.nupkg │ │ ├── Squirrel.Core.1.1.0.0-full.nupkg │ │ ├── Squirrel.Core.1.1.0.0.nupkg │ │ ├── Squirrel.Core.1.1.0.0.nuspec │ │ ├── Squirrel.Core.1.2.0.0-full.nupkg │ │ ├── Squirrel.Core.1.3.0.0-full.nupkg │ │ ├── Squirrel.Core.NoDependencies.1.0.0.0.nupkg │ │ ├── Squirrel.Tests.0.1.0-pre.nupkg │ │ ├── Squirrel.Tests.0.2.0-pre.nupkg │ │ ├── SquirrelInstalledApp.nuspec │ │ ├── ThisShouldBeANet45Project.1.0.nupkg │ │ ├── content-types/ │ │ │ ├── basic-merged.xml │ │ │ ├── basic.xml │ │ │ ├── complex-merged.xml │ │ │ └── complex.xml │ │ ├── slack-1.1.8-full.nupkg │ │ └── slack-1.2.0-delta.nupkg │ └── test_official.cmd ├── vendor/ │ └── wix/ │ ├── candle.exe.config │ ├── darice.cub │ ├── light.exe.config │ └── template.wxs └── version.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain *.jpg binary *.png binary *.gif binary *.cs text diff=csharp *.vb text *.c text *.cpp text *.cxx text *.h text *.hxx text *.py text *.rb text *.java text *.html text *.htm text *.css text *.scss text *.sass text *.less text *.js text *.lisp text *.clj text *.sql text *.php text *.lua text *.m text *.asm text *.erl text *.fs text *.fsx text *.hs text *.csproj text merge=union *.vbproj text merge=union *.fsproj text merge=union *.dbproj text merge=union *.sln text eol=crlf merge=union src/Setup/resource.h binary src/Setup/Setup.rc binary ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Feedback to help us improve the project title: '' labels: '' assignees: '' --- **Squirrel version(s)** _Which version(s) of the project are you using?_ **Description** _Replace this text with a short description of the problem_ **Steps to recreate** 1. Replace this 2. text with 3. the steps 4. to recreate **Expected behavior** _Explain what it's doing and why it's wrong_ **Actual behavior** _Explain what it should be doing after it's fixed_ **Additional information** _Add any other context about the problem here_ ================================================ FILE: .github/workflows/build.yml ================================================ name: Build Squirrel on: push: branches: - master - develop pull_request: branches: - master - develop env: DOTNET_NOLOGO: true DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true DOTNET_CLI_TELEMETRY_OPTOUT: true NUGET_XMLDOC_MODE: skip jobs: build: name: Build runs-on: windows-2019 steps: - name: Checkout code uses: actions/checkout@v2 with: fetch-depth: 0 submodules: recursive - name: Build Squirrel shell: cmd run: ./src/build_official.cmd - name: Test Squirrel shell: cmd run: ./test/test_official.cmd - name: Save build uses: actions/upload-artifact@v2 with: name: artifacts path: build/artifacts/ ================================================ FILE: .gitignore ================================================ ################# ## Eclipse ################# *.pydevproject .project .metadata bin/** tmp/** tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath # External tool builders .externalToolBuilders/ # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ################# ## Visual Studio ################# ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates *.sln.ide # Build results **/[Dd]ebug/ **/[Rr]elease/ *_i.c *_p.c *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.vspscc .builds **/*.dotCover **/packages/ packages # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf # Visual Studio profiler *.psess *.vsp # ReSharper is a .NET coding add-in _ReSharper* # Installshield output folder [Ee]xpress # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish # Others [Bb]in [Oo]bj sql TestResults *.Cache ClientBin stylecop.* ~$* *.dbmdl Generated_Code #added for RIA/Silverlight projects # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML ############ ## Windows ############ # Windows image file caches Thumbs.db # Folder config file Desktop.ini ############# ## Python ############# *.py[co] # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox #Translations *.mo #Mr Developer .mr.developer.cfg # Mac crap .DS_Store # NCrunch crap *ncrunch* ## WiX PDBs *.wixpdb *.wixobj ## NUnit Test Output nunit-Squirrel.Tests.xml ## Pester Test Output tests/Test.xml ## CPP db crap *.db *.opendb .vs/ ================================================ FILE: .gitmodules ================================================ [submodule "vendor/nuget"] path = vendor/nuget url = https://github.com/Squirrel/NuGet branch = fix-prerelease-comparison ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at github@brendanforster.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq ================================================ FILE: COPYING ================================================ Copyright (c) 2012 GitHub, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ | README.md | |:---| # Contributors Needed We are looking for help with maintaining this important project - please read the discussion in [#1470](https://github.com/Squirrel/Squirrel.Windows/issues/1470) for more information. --- # Squirrel: It's like ClickOnce but Works™ ![](docs/artwork/Squirrel-Logo.png) [![Build Status](https://dev.azure.com/squirrel-installers/Squirrel.Windows/_apis/build/status/Squirrel.Squirrel.Windows?branchName=master)](https://dev.azure.com/squirrel-installers/Squirrel.Windows/_build/latest?definitionId=1&branchName=master) Squirrel is both a set of tools and a library, to completely manage both installation and updating your Desktop Windows application, written in either C# or any other language (i.e., Squirrel can manage native C++ applications). Squirrel uses NuGet packages to create installation and update packages, which means that you probably already know most of what you need to create an installer. ## What Do We Want? Windows apps should be as fast and as easy to install and update as apps like Google Chrome. From an app developer's side, it should be really straightforward to create an installer for my app, and publish updates to it, without having to jump through insane hoops. * **Integrating** an app to use Squirrel should be extremely easy, provide a client API, and be developer friendly. * **Packaging** is really easy, can be automated, and supports delta update packages. * **Distributing** should be straightforward, use simple HTTP updates, and provide multiple "channels" (a-la Chrome Dev/Beta/Release). * **Installing** is Wizard-Free™, with no UAC dialogs, does not require reboot, and is .NET Framework friendly. * **Updating** is in the background, doesn't interrupt the user, and does not require a reboot. Refer to our full list of goals for [integrating, packaging, distributing, installing, and updating](docs/goals.md). ## Documentation See the documentation [Table of Contents](docs/readme.md) for an overview of the available documentation for Squirrel.Windows. It includes a [Getting Started Guide](docs/getting-started/0-overview.md) as well as additional topics related to using Squirrel in your applications. ## Building Squirrel For the impatient: ```sh git clone --recursive https://github.com/squirrel/squirrel.windows cd squirrel.windows devbuild.cmd ``` See [Contributing](docs/contributing/contributing.md) for additional information on building and contributing to Squirrel. ## License and Usage See [COPYING](COPYING) for details on copyright and usage of the Squirrel.Windows software. ================================================ FILE: Squirrel.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28803.352 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squirrel", "src\Squirrel\Squirrel.csproj", "{1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squirrel.Tests", "test\Squirrel.Tests\Squirrel.Tests.csproj", "{98AEB048-E27D-42F4-9440-505B7F78BAFD}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Setup", "src\Setup\Setup.vcxproj", "{C1D40624-A484-438A-B846-052F321C89D1}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Update", "src\Update\Update.csproj", "{1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SyncReleases", "src\SyncReleases\SyncReleases.csproj", "{EB521191-1EBF-4D06-8541-ED192E2EE378}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionLevel", "SolutionLevel", "{ED657D2C-F8A0-4012-A64F-7367D41BE4D2}" ProjectSection(SolutionItems) = preProject src\Squirrel.nuspec = src\Squirrel.nuspec vendor\wix\template.wxs = vendor\wix\template.wxs EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "vendor\nuget\src\Core\Core.csproj", "{F879F274-EFA0-4157-8404-33A19B4E6AEC}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WriteZipToSetup", "src\WriteZipToSetup\WriteZipToSetup.vcxproj", "{4D3C8B70-075D-48A5-9FF3-EDB87347B136}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Update-Mono", "src\Update\Update-Mono.csproj", "{5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StubExecutable", "src\StubExecutable\StubExecutable.vcxproj", "{C028DB2A-E7C5-4232-8C22-D5FBA2176136}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CIBuild|Any CPU = CIBuild|Any CPU CIBuild|Mixed Platforms = CIBuild|Mixed Platforms CIBuild|x64 = CIBuild|x64 CIBuild|x86 = CIBuild|x86 Coverage|Any CPU = Coverage|Any CPU Coverage|Mixed Platforms = Coverage|Mixed Platforms Coverage|x64 = Coverage|x64 Coverage|x86 = Coverage|x86 Debug|Any CPU = Debug|Any CPU Debug|Mixed Platforms = Debug|Mixed Platforms Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Mono Debug|Any CPU = Mono Debug|Any CPU Mono Debug|Mixed Platforms = Mono Debug|Mixed Platforms Mono Debug|x64 = Mono Debug|x64 Mono Debug|x86 = Mono Debug|x86 Mono Release|Any CPU = Mono Release|Any CPU Mono Release|Mixed Platforms = Mono Release|Mixed Platforms Mono Release|x64 = Mono Release|x64 Mono Release|x86 = Mono Release|x86 Release|Any CPU = Release|Any CPU Release|Mixed Platforms = Release|Mixed Platforms Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.CIBuild|Any CPU.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.CIBuild|Any CPU.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.CIBuild|Mixed Platforms.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.CIBuild|Mixed Platforms.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.CIBuild|x64.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.CIBuild|x64.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.CIBuild|x86.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.CIBuild|x86.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Coverage|Any CPU.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Coverage|Any CPU.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Coverage|Mixed Platforms.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Coverage|Mixed Platforms.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Coverage|x64.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Coverage|x64.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Coverage|x86.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Coverage|x86.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Debug|Any CPU.Build.0 = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Debug|x64.ActiveCfg = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Debug|x64.Build.0 = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Debug|x86.ActiveCfg = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Debug|x86.Build.0 = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Debug|Any CPU.ActiveCfg = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Debug|Any CPU.Build.0 = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Debug|Mixed Platforms.Build.0 = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Debug|x64.ActiveCfg = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Debug|x64.Build.0 = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Debug|x86.ActiveCfg = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Debug|x86.Build.0 = Debug|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Release|Any CPU.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Release|Any CPU.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Release|Mixed Platforms.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Release|Mixed Platforms.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Release|x64.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Release|x64.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Release|x86.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Mono Release|x86.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Release|Any CPU.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Release|Any CPU.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Release|Mixed Platforms.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Release|x64.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Release|x64.Build.0 = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Release|x86.ActiveCfg = Release|Any CPU {1436E22A-FE3C-4D68-9A85-9E74DF2E6A92}.Release|x86.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.CIBuild|Any CPU.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.CIBuild|Any CPU.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.CIBuild|Mixed Platforms.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.CIBuild|Mixed Platforms.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.CIBuild|x64.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.CIBuild|x64.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.CIBuild|x86.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.CIBuild|x86.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Coverage|Any CPU.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Coverage|Any CPU.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Coverage|Mixed Platforms.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Coverage|Mixed Platforms.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Coverage|x64.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Coverage|x64.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Coverage|x86.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Coverage|x86.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Debug|Any CPU.Build.0 = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Debug|x64.ActiveCfg = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Debug|x64.Build.0 = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Debug|x86.ActiveCfg = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Debug|x86.Build.0 = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Debug|Any CPU.ActiveCfg = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Debug|Any CPU.Build.0 = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Debug|Mixed Platforms.Build.0 = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Debug|x64.ActiveCfg = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Debug|x64.Build.0 = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Debug|x86.ActiveCfg = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Debug|x86.Build.0 = Debug|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Release|Any CPU.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Release|Any CPU.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Release|Mixed Platforms.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Release|Mixed Platforms.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Release|x64.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Release|x64.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Release|x86.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Mono Release|x86.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Release|Any CPU.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Release|Any CPU.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Release|Mixed Platforms.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Release|x64.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Release|x64.Build.0 = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Release|x86.ActiveCfg = Release|Any CPU {98AEB048-E27D-42F4-9440-505B7F78BAFD}.Release|x86.Build.0 = Release|Any CPU {C1D40624-A484-438A-B846-052F321C89D1}.CIBuild|Any CPU.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.CIBuild|Any CPU.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.CIBuild|Mixed Platforms.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.CIBuild|Mixed Platforms.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.CIBuild|x64.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.CIBuild|x64.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.CIBuild|x86.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.CIBuild|x86.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Coverage|Any CPU.ActiveCfg = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Coverage|Any CPU.Build.0 = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Coverage|Mixed Platforms.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Coverage|Mixed Platforms.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Coverage|x64.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Coverage|x64.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Coverage|x86.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Coverage|x86.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Debug|Any CPU.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Debug|Any CPU.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Debug|Mixed Platforms.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Debug|x64.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Debug|x86.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Debug|x86.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Debug|Any CPU.ActiveCfg = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Debug|Any CPU.Build.0 = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Debug|Mixed Platforms.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Debug|Mixed Platforms.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Debug|x64.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Debug|x64.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Debug|x86.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Debug|x86.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Release|Any CPU.ActiveCfg = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Release|Any CPU.Build.0 = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Release|Mixed Platforms.ActiveCfg = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Release|Mixed Platforms.Build.0 = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Release|x64.ActiveCfg = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Release|x64.Build.0 = Debug|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Release|x86.ActiveCfg = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Mono Release|x86.Build.0 = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Release|Any CPU.ActiveCfg = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Release|Any CPU.Build.0 = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Release|Mixed Platforms.ActiveCfg = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Release|Mixed Platforms.Build.0 = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Release|x64.ActiveCfg = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Release|x86.ActiveCfg = Release|Win32 {C1D40624-A484-438A-B846-052F321C89D1}.Release|x86.Build.0 = Release|Win32 {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.CIBuild|Any CPU.ActiveCfg = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.CIBuild|Any CPU.Build.0 = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.CIBuild|Mixed Platforms.ActiveCfg = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.CIBuild|Mixed Platforms.Build.0 = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.CIBuild|x64.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.CIBuild|x64.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.CIBuild|x86.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.CIBuild|x86.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Coverage|Any CPU.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Coverage|Any CPU.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Coverage|Mixed Platforms.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Coverage|Mixed Platforms.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Coverage|x64.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Coverage|x64.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Coverage|x86.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Coverage|x86.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Debug|Any CPU.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Debug|x64.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Debug|x64.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Debug|x86.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Debug|x86.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Debug|Any CPU.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Debug|Any CPU.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Debug|Mixed Platforms.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Debug|x64.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Debug|x64.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Debug|x86.ActiveCfg = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Debug|x86.Build.0 = Debug|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Release|Any CPU.ActiveCfg = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Release|Any CPU.Build.0 = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Release|Mixed Platforms.ActiveCfg = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Release|Mixed Platforms.Build.0 = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Release|x64.ActiveCfg = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Release|x64.Build.0 = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Release|x86.ActiveCfg = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Mono Release|x86.Build.0 = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|Any CPU.ActiveCfg = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|Any CPU.Build.0 = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|Mixed Platforms.Build.0 = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|x64.ActiveCfg = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|x64.Build.0 = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|x86.ActiveCfg = Release|Any CPU {1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|x86.Build.0 = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|Any CPU.ActiveCfg = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|Any CPU.Build.0 = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|Mixed Platforms.ActiveCfg = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|Mixed Platforms.Build.0 = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|x64.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|x64.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|x86.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|x86.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|Any CPU.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|Any CPU.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|Mixed Platforms.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|Mixed Platforms.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|x64.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|x64.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|x86.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|x86.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|x64.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|x64.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|x86.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|x86.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|Any CPU.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|Any CPU.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|Mixed Platforms.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|x64.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|x64.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|x86.ActiveCfg = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|x86.Build.0 = Debug|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|Any CPU.ActiveCfg = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|Any CPU.Build.0 = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|Mixed Platforms.ActiveCfg = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|Mixed Platforms.Build.0 = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|x64.ActiveCfg = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|x64.Build.0 = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|x86.ActiveCfg = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|x86.Build.0 = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|Any CPU.Build.0 = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|Mixed Platforms.Build.0 = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|x64.ActiveCfg = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|x64.Build.0 = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|x86.ActiveCfg = Release|Any CPU {EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|x86.Build.0 = Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.CIBuild|Any CPU.ActiveCfg = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.CIBuild|Any CPU.Build.0 = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.CIBuild|Mixed Platforms.ActiveCfg = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.CIBuild|Mixed Platforms.Build.0 = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.CIBuild|x64.ActiveCfg = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.CIBuild|x64.Build.0 = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.CIBuild|x86.ActiveCfg = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.CIBuild|x86.Build.0 = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Coverage|Any CPU.ActiveCfg = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Coverage|Any CPU.Build.0 = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Coverage|Mixed Platforms.ActiveCfg = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Coverage|Mixed Platforms.Build.0 = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Coverage|x64.ActiveCfg = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Coverage|x64.Build.0 = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Coverage|x86.ActiveCfg = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Coverage|x86.Build.0 = Coverage|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Debug|Any CPU.Build.0 = Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Debug|x64.ActiveCfg = Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Debug|x64.Build.0 = Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Debug|x86.ActiveCfg = Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Debug|x86.Build.0 = Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Debug|Any CPU.ActiveCfg = Mono Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Debug|Any CPU.Build.0 = Mono Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Debug|Mixed Platforms.ActiveCfg = Mono Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Debug|Mixed Platforms.Build.0 = Mono Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Debug|x64.ActiveCfg = Mono Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Debug|x64.Build.0 = Mono Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Debug|x86.ActiveCfg = Mono Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Debug|x86.Build.0 = Mono Debug|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Release|Any CPU.ActiveCfg = Mono Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Release|Any CPU.Build.0 = Mono Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Release|Mixed Platforms.ActiveCfg = Mono Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Release|Mixed Platforms.Build.0 = Mono Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Release|x64.ActiveCfg = Mono Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Release|x64.Build.0 = Mono Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Release|x86.ActiveCfg = Mono Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Mono Release|x86.Build.0 = Mono Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Release|Any CPU.ActiveCfg = Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Release|Any CPU.Build.0 = Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Release|Mixed Platforms.Build.0 = Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Release|x64.ActiveCfg = Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Release|x64.Build.0 = Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Release|x86.ActiveCfg = Release|Any CPU {F879F274-EFA0-4157-8404-33A19B4E6AEC}.Release|x86.Build.0 = Release|Any CPU {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|Any CPU.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|Any CPU.Build.0 = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|Mixed Platforms.ActiveCfg = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|Mixed Platforms.Build.0 = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|x64.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|x64.Build.0 = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|x86.ActiveCfg = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|x86.Build.0 = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Coverage|Any CPU.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Coverage|Any CPU.Build.0 = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Coverage|Mixed Platforms.ActiveCfg = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Coverage|Mixed Platforms.Build.0 = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Coverage|x64.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Coverage|x64.Build.0 = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Coverage|x86.ActiveCfg = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Coverage|x86.Build.0 = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Debug|Any CPU.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Debug|Any CPU.Build.0 = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Debug|Mixed Platforms.Build.0 = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Debug|x64.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Debug|x86.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Debug|x86.Build.0 = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Debug|Any CPU.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Debug|Any CPU.Build.0 = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Debug|Mixed Platforms.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Debug|Mixed Platforms.Build.0 = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Debug|x64.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Debug|x64.Build.0 = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Debug|x86.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Debug|x86.Build.0 = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Release|Any CPU.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Release|Any CPU.Build.0 = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Release|Mixed Platforms.ActiveCfg = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Release|Mixed Platforms.Build.0 = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Release|x64.ActiveCfg = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Release|x64.Build.0 = Debug|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Release|x86.ActiveCfg = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Mono Release|x86.Build.0 = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Release|Any CPU.ActiveCfg = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Release|Any CPU.Build.0 = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Release|Mixed Platforms.ActiveCfg = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Release|Mixed Platforms.Build.0 = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Release|x64.ActiveCfg = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Release|x86.ActiveCfg = Release|Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136}.Release|x86.Build.0 = Release|Win32 {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.CIBuild|Any CPU.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.CIBuild|Any CPU.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.CIBuild|Mixed Platforms.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.CIBuild|Mixed Platforms.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.CIBuild|x64.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.CIBuild|x64.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.CIBuild|x86.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.CIBuild|x86.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Coverage|Any CPU.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Coverage|Any CPU.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Coverage|Mixed Platforms.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Coverage|Mixed Platforms.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Coverage|x64.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Coverage|x64.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Coverage|x86.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Coverage|x86.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Debug|x64.ActiveCfg = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Debug|x64.Build.0 = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Debug|x86.ActiveCfg = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Debug|x86.Build.0 = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Debug|Any CPU.Build.0 = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Debug|Mixed Platforms.Build.0 = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Debug|x64.ActiveCfg = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Debug|x64.Build.0 = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Debug|x86.ActiveCfg = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Debug|x86.Build.0 = Debug|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Release|Any CPU.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Release|Any CPU.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Release|Mixed Platforms.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Release|Mixed Platforms.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Release|x64.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Release|x64.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Release|x86.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Mono Release|x86.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Release|Any CPU.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Release|Mixed Platforms.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Release|x64.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Release|x64.Build.0 = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Release|x86.ActiveCfg = Release|Any CPU {5B4BC824-73FC-49D7-BD9D-CE53AA1AA86E}.Release|x86.Build.0 = Release|Any CPU {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.CIBuild|Any CPU.ActiveCfg = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.CIBuild|Any CPU.Build.0 = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.CIBuild|Mixed Platforms.ActiveCfg = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.CIBuild|Mixed Platforms.Build.0 = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.CIBuild|x64.ActiveCfg = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.CIBuild|x64.Build.0 = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.CIBuild|x86.ActiveCfg = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.CIBuild|x86.Build.0 = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Coverage|Any CPU.ActiveCfg = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Coverage|Any CPU.Build.0 = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Coverage|Mixed Platforms.ActiveCfg = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Coverage|Mixed Platforms.Build.0 = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Coverage|x64.ActiveCfg = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Coverage|x64.Build.0 = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Coverage|x86.ActiveCfg = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Coverage|x86.Build.0 = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Debug|Any CPU.ActiveCfg = Debug|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Debug|Any CPU.Build.0 = Debug|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Debug|Mixed Platforms.Build.0 = Debug|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Debug|x64.ActiveCfg = Debug|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Debug|x64.Build.0 = Debug|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Debug|x86.ActiveCfg = Debug|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Debug|x86.Build.0 = Debug|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Debug|Any CPU.ActiveCfg = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Debug|Any CPU.Build.0 = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Debug|Mixed Platforms.ActiveCfg = Debug|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Debug|Mixed Platforms.Build.0 = Debug|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Debug|x64.ActiveCfg = Debug|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Debug|x64.Build.0 = Debug|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Debug|x86.ActiveCfg = Debug|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Debug|x86.Build.0 = Debug|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Release|Any CPU.ActiveCfg = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Release|Any CPU.Build.0 = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Release|Mixed Platforms.ActiveCfg = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Release|Mixed Platforms.Build.0 = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Release|x64.ActiveCfg = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Release|x64.Build.0 = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Release|x86.ActiveCfg = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Mono Release|x86.Build.0 = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|Any CPU.ActiveCfg = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|Any CPU.Build.0 = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|Mixed Platforms.ActiveCfg = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|Mixed Platforms.Build.0 = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|x64.ActiveCfg = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|x64.Build.0 = Release|x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|x86.ActiveCfg = Release|Win32 {C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {68CA987A-9BAB-4C75-8EEB-4596BA6BBD07} EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0 $0.TextStylePolicy = $1 $1.inheritsSet = VisualStudio $1.inheritsScope = text/plain $1.scope = text/plain $0.CSharpFormattingPolicy = $2 $2.IndentSwitchBody = True $2.IndentBlocksInsideExpressions = True $2.AnonymousMethodBraceStyle = NextLine $2.PropertyBraceStyle = NextLine $2.PropertyGetBraceStyle = NextLine $2.PropertySetBraceStyle = NextLine $2.EventBraceStyle = NextLine $2.EventAddBraceStyle = NextLine $2.EventRemoveBraceStyle = NextLine $2.StatementBraceStyle = NextLine $2.ElseNewLinePlacement = NewLine $2.CatchNewLinePlacement = NewLine $2.FinallyNewLinePlacement = NewLine $2.WhileNewLinePlacement = DoNotCare $2.ArrayInitializerWrapping = DoNotChange $2.ArrayInitializerBraceStyle = NextLine $2.BeforeMethodDeclarationParentheses = False $2.BeforeMethodCallParentheses = False $2.BeforeConstructorDeclarationParentheses = False $2.NewLineBeforeConstructorInitializerColon = NewLine $2.NewLineAfterConstructorInitializerColon = SameLine $2.BeforeDelegateDeclarationParentheses = False $2.NewParentheses = False $2.SpacesBeforeBrackets = False $2.inheritsSet = Mono $2.inheritsScope = text/x-csharp $2.scope = text/x-csharp EndGlobalSection EndGlobal ================================================ FILE: devbuild.cmd ================================================ @echo off setlocal pushd %~dp0 :parse_args if /i "%1"=="release" set _C=/p:Configuration=Release if /i "%1"=="init" set _INIT=1 if /i "%1"=="initialize" set _INIT=1 if /i "%1"=="inc" set _INCREMENTAL=1 if /i "%1"=="incremental" set _INCREMENTAL=1 if /i "%1"=="clean" set _INCREMENTAL= & set _CLEAN=1 if not "%1"=="" shift & goto parse_args if not "%_INCREMENTAL"=="1" rd /s /q build packages 2> nul if not "%_CLEAN%"=="" goto end if "%_INIT%"=="1" git submodule update --init --recursive nuget restore msbuild -Restore %_C% -m -nr:false -v:m :end popd endlocal ================================================ FILE: docs/contributing/branching-strategy.md ================================================ | [docs](..) / [contributing](.) / branching-strategy.md |:---| # tl;dr 1. Fork Squirrel.Windows on GitHub 2. Send features/fixes via pull request targeting the default branch: `develop` # Branching Strategy Squirrel.Windows uses a very lightweight rendition of [gitflow](https://nvie.com/posts/a-successful-git-branching-model/). * `master` branch - the `master` branch is where official releases of squirrel are built. Changes to `master` are made only via merge commits from the `develop` branch. Tags are made for each each release. * `develop` branch - the `develop` branch is where the next version of squirrel is under development. Changes to `develop` are made via pull request from forks and feature branches. So `develop` is the default branch on GitHub. * fork - your development takes place in fork. When a feature/fix is ready, a pull request is sent to Squirrel.Windows targeting the `develop` branch. **Why gitflow?** This lightweight rendition of giflow adds minimal "overhead" in the `develop` branch. The `develop` branch allows us to experiment with new ideas and iterate on features. When "enough" work is completed for a release, complete integration testing--including multi-version upgrades--is done on the `develop` branch. When the testing is completed successfully, the `develop` branch is integrated into `master` and a release is automatically built and released. ## See Also * [Building Squirrel](building-squirrel.md) - steps to build squirrel for the impatient. * [VS Solution Overview](vs-solution-overview.md) - overview of the various projects in the Squirrel.Windows Visual Studio solution. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/contributing/building-squirrel.md ================================================ | [docs](..) / [contributing](.) / building-squirrel.md |:---| # Building Squirrel Squirrel.Windows is a fairly typical C# / C++ project, the only special part is making sure to clone submodules via the command shown below. For the Impatient: ```sh git clone https://github.com/squirrel/squirrel.windows cd squirrel.windows git submodule update --init --recursive ## THIS IS THE PART YOU PROBABLY FORGOT devbuild.cmd ``` **Tip:** You can compile the Squirrel.Windows solution with Visual Studio version 2019 and above (including community edition). **Tip:** For Visual Studio versions that use the Visual Studio Installer (2017/2019 and above), you will need to have at least both Desktop .NET development and Desktop C++ development workloads checked in the Visual Studio Installer. You will also need to make sure that the individual package for the VC++ version used by Squirrel is checked. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/contributing/contributing.md ================================================ | [docs](..) / [contributing](.) / contributing.md |:---| # Contributing Why not give back and help make Squirrel even better? Here is an overview of ways you can become more involved. * **Contribute Documentation** - improve the documentation or provide additional code examples to benefit others. * **Subscribe to Issues on GitHub** - have some experience using Squirrel? Help answer questions under issues or post a Pull Request fixing a bug. * **Contribute Code** - have a great feature that you feel is a good fit for Squirrel? Send a Pull Request. ## See Also * [Building Squirrel](building-squirrel.md) - steps to build squirrel for the impatient. * [VS Solution Overview](vs-solution-overview.md) - overview of the various projects in the Squirrel.Windows Visual Studio solution. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/contributing/vs-solution-overview.md ================================================ | [docs](..) / [contributing](.) / vs-solution-overview.md |:---| # Visual Studio Solution Overview An overview of the various projects in the Squirrel.Windows Visual Studio solution and how they relate to different aspects of the update process. | Project / Assembly Name | Libraries (NuGet) | Libraries (NuGet) | Releases Directory (releasify output) | MyApp (install location) | |--------------------------------|---------|-----|------------------|-------------| | **Core** NuGet.Squirrel.dll | NuGet.Squirrel.dll | | | | | **Squirrel** Squirrel.dll | Squirrel.dll | | | | | **SyncRelease** SyncRelease.exe | | SyncRelease.exe | | | | **Update** Update.exe | | Squirrel.exe | | Update.exe | | **Setup** Setup.exe | | Setup.exe | Setup.exe (+MyApp.Latest.nupkg) | | | **WriteZipToSetup** WriteZipToSetup.exe | | WriteZipToSetup.exe | | | * **Project / Assembly Name**: Solution project name (from Squirrel.sln) and output assembly name. * **Libraries (NuGet)**: Program libraries installed added as references in your MyApp solution when adding the Squirrel.Windows NuGet package to your project. * **Libraries (NuGet)**: Executable tools included in the Squirrel.Windows NuGet package used to prepare deployments via the Package Manager Console (e.g., Squirrel.exe). * **Releases Directory (releasify output)**: Directory where the Squirrel --releasify process outputs the packages and Setup application for your project (e.g., MyAppSourceCode/Releases). * **MyApp (install location)**: MyApp install directory (e.g., %LOCALAPPDATA%\MyApp) where the application is actually installed and updated via Squirrel.Windows. **Note**: Note that the Squirrel.exe application found in the tools directory of the Squirrel.Windows NuGet package is actually a renamed version of the Update.exe application (see Squirrel.Windows\src\Squirrel.nuspec) --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/faq.md ================================================ | [docs](.) / faq.md | |:---| # Frequently Asked Questions (FAQ) Frequently Asked Questions for Squirrel.Windows, organized by area below. ## Integrating 1. **Can Squirrel.Windows be used on applications that aren't made with .Net?** Yes, you can package a non-c# application in the same manner as described in the Getting Started guide. For additional customization, see [custom squirrel events for non-c# apps](using/custom-squirrel-events-non-cs.md). 1. **How do I migrate a ClickOnce app to Squirrel?** You may want to look into the [ClickOnceToSquirrelMigrator](https://github.com/flagbug/ClickOnceToSquirrelMigrator) migration helper. 1. **How can I determine if my app is a Squirrel app? I provide a squirrel and non-squirrel install version and want to know which is running.** You can check for the `Update.exe` in the parent directory to determine if the app is using Squirrel ([see #574](https://github.com/Squirrel/Squirrel.Windows/issues/574#issuecomment-176043311)). ``` var assembly = Assembly.GetEntryAssembly(); var updateDotExe = Path.Combine(Path.GetDirectoryName(assembly.Location), "..", "Update.exe"); var isSquirrelInstall = File.Exists(updateDotExe); ``` ## Packaging 1. **How can I tell what is going wrong with the releasify?** Check `packages\Squirrel.Windows.VERSION\tools\SquirrelSetup.log` for logging information when creating packages. 2. **Do I really have to add all the Squirrel DLLs to my app ?** Yes, you have to add them all to the NuGet package, however, [others](https://github.com/Squirrel/Squirrel.Windows/issues/531) have used [ILMerge](https://www.microsoft.com/en-us/research/people/mbarnett/#ilmerge) to generate a single assembly. ## Distributing 1. **Can I distribute update files on IIS?** Yes you can, see [Microsoft IIS](using/microsoft-iis.md) for details. ## Installing 1. **The Initial Install via `Setup.exe` is failing. How do I learn what is going wrong?** Check `%LocalAppData%\SquirrelTemp\SquirrelSetup.log` for logs related to the initial install. 1. **Installer application doesn't do anything. The animation flashes but the application never starts.** The app is likely crashing on the first run (see [Debugging Installs](using/debugging-installs.md) for details). 1. **The Installer seems to be blocked in Enterprise environments. How can I confirm this?** Squirrel may be prevented from installing if Group Policy disallows the running of executables from `%LocalAppData%`. In this case, the "show log" button on the "installation failed" dialog will fail because `Update.exe` can not run to create a log file. The `Setup.exe` for your application should still copy files to `%LocalAppData%\SquirrelTemp` as a pre-installation step. To verify that Group Policy is restricting you, execute `Update.exe` from the command line as follows: ``` C:\>%LocalAppData\MyApp\Update.exe This program is blocked by group policy. For more information, contact your system administrator. ``` The best course of action is to request that executables for Squirrel and your application be whitelisted by your corporate overlords. 1. **No Shortcuts are Created for my Application** Verify that the NuGet Package Metadata `id` property doesn't have a [space or \[dot\]](https://github.com/Squirrel/Squirrel.Windows/issues/530) in it. 1. **Can I use a different name for the `Setup.exe` install application?** Yes, you can rename the `Setup.exe` to what ever you wish (e.g., `MyAppSetup.exe`) ([see #611](https://github.com/Squirrel/Squirrel.Windows/issues/611)) 1. **Virus scanner is returning false positives on `MyApp.exe` or `Update.exe`. What can I do?** [Application Signing](using/application-signing.md) will help. In addition, you can submit false positives to the various antivirus authors (e.g., [Symantec](https://submit.symantec.com/false_positive/), [Microsoft](https://www.microsoft.com/security/portal/Submission/Submit.aspx), [AVG](http://www.avg.com/submit-sample), [Comodo](https://www.comodo.com/home/internet-security/submit.php), [McAfee](https://support.mcafeesaas.com/MCAFEE/_cs/AnswerDetail.aspx?aid=65), [List of Submission Locations](http://www.techsupportalert.com/content/how-report-malware-or-false-positives-multiple-antivirus-vendors.htm), [see #218](https://github.com/Squirrel/Squirrel.Windows/issues/218#issuecomment-166406180)). 1. **Why is my application icon mangled after installation?** Application icons specified in the [NuGet Package Metadata](using/nuget-package-metadata.md) must be of type icon (.ICO) rather than an image file (source: [issue #745](https://github.com/Squirrel/Squirrel.Windows/issues/745)) ## Updating 1. **How do I determine what is going wrong with the UpdateManager in MyApp?** You can setup your `\bin` directory so you can execute MyApp in the Visual Studio debugger and simply step through the update process as well as catch exceptions and log the results (see [Debugging Updates](using/debugging-updates.md) for details) 2. **I've Distributed a Broken Copy of Update.exe. How can I fix this?** Sometimes, you might ship a broken copy of `Update.exe` that succeeds the initial install, but doesn't do what you want for some reason. To fix this, you can force an update of the `Update.exe` by including a copy of `Squirrel.exe` in your app update package. If Squirrel sees this, it will copy in this latest version to the local app installation. 3. **How can you replace DLLs while they're loaded? Impossible!** You can't. So, how can you do it? The basic trick that ClickOnce uses is, you have a folder of EXEs and DLLs, and an Application Shortcut. When ClickOnce goes to update its stuff, it builds a completely *new* folder of binaries, then the last thing it does is rewrite the app shortcut to point to the new folder. 4. **My previous application version is still around after the update. Doesn't Squirrel clean up old versions?** The current and immediately previous version of your application are not deleted on clean up (see [issue #589](https://github.com/Squirrel/Squirrel.Windows/issues/589)). 5. **How can I persist the [.NET Application Settings](https://docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/application-settings-overview) after an update?** See (https://github.com/Squirrel/Squirrel.Windows/issues/198#issuecomment-299262613) for a simple workaround if you want to keep using .NET Application Settings. Alternatively, consider using a solution that lets you control where the settings are persisted, and store settings in an app-specific location under `%LOCALAPPDATA%`. --- | Return: [Table of Contents](readme.md) | |:---| ================================================ FILE: docs/getting-started/0-overview.md ================================================ | [docs](..) / [getting-started](.) / 0-overview.md| |:---| # Getting Started Guide Getting Started will walk you through the integration of Squirrel.Windows for a basic c# Windows Forms application named MyApp. ## MyApp MyApp simply displays the assembly location and application version on a simple form. ![](images/1-MyApp.png) For simplicity, any unneeded references and files have been removed from the solution. ![](images/1-MyApp-Solution.png) If you wish to follow along, you can [download](example/MyApp.zip) a zip file of the MyApp solution. ## Overview This guide will go over the following steps to demonstrate using Squirrel.Windows to distribute and update MyApp. 1. [Integrating](1-integrating.md) - integrating Squirrel `UpdateManager` into your application. 1. [Packaging](2-packaging.md) - packaging application files and preparing them for release. 1. [Distributing](3-distributing.md) - providing install and update files for users. 1. [Installing](4-installing.md) - process of initial installation of your application. 1. [Updating](5-updating.md) - process of updating an existing install. --- | Next: [1. Integrating](1-integrating.md)| |:---| ================================================ FILE: docs/getting-started/1-integrating.md ================================================ | [docs](..) / [getting-started](.) / 1-integrating.md | |:---| # Step 1. Integrating The first step is to configure MyApp to work with Squirrel.Windows. This requires you to install the Squirrel.Windows NuGet Package into the `MyApp.sln`. ## Installing Squirrel.Windows The easiest way to install the Squirrel.Windows is using the [Package Manager Console](https://docs.NuGet.org/consume/package-manager-console) in Visual Studio after loading the MyApp solution. ~~~powershell PM> Install-Package Squirrel.Windows ~~~ ### Squirrel.Windows References The package will install a number of dependent packages as well as tools that will be used to prepare MyApp to be released. The References in the Solution Explorer of the MyApp project now looks like the following (as of Squirrel.Windows version 1.2.2): ![](images/1.1-post-package-install.png) **Tip:** Alternatively, you can use the "Manage NuGet Packages" GUI to install Squirrel.Windows (right clicking on your project in the Solution Explorer of Visual Studio and select "Manage NuGet Packages..."). ## Basic Updating For the basic example we are going to have MyApp update from your local file system rather than distributing the files via the web. See section [Packaging](2-packaging.md) for additional options related to the distributing the update files. ### Basic Squirrel.Windows Update Code The following code is added to MyApp `Program.cs` to cause the application to check for, download, and install any new releases of MyApp in the background while you use the application. **`Program.cs`** ~~~cs using Squirrel; using System.Threading.Tasks; ~~~ **`static async Task Main()`** ~~~cs using (var mgr = new UpdateManager("C:\\Projects\\MyApp\\Releases")) { await mgr.UpdateApp(); } ~~~ The code above demonstrates the most basic update mechanism using the `UpdateApp()` method in an asynchronous task. The actions it takes will be discussed further in section [Updating](5-updating.md). **Caution:** The path you provide the `UpdateManager` is the path to the directory where the `RELEASES` file is located (which is also named `Releases` by default), and not the actual `RELEASES` file. **Tip:** By default, the files for updating MyApp will be placed in the same directory as your `MyApp.sln` file under a `Releases` directory (e.g., `C:\Projects\MyApp\Releases`). **Tip:** In this example we simply put the code in the `Program.cs` file. For a production application, place the update code later in start-up process so as to avoid slowing down your program start. **Tip:** If you attempt to debug the application via Visual Studio, you will get an exception of `Update.exe not found, not a Squirrel-installed app?`. You can resolve this by placing a copy of the Update.exe in your bin directory (see [Debugging Updates: Update.exe not found?](../using/debugging-updates.md) section for details). --- | Previous: [Getting Started Guide](0-overview.md) | Next: [2. Packaging](2-packaging.md)| |:---|:---| ================================================ FILE: docs/getting-started/2-packaging.md ================================================ | [docs](..) / [getting-started](.) / 2-packaging.md | |:---| # Step 2. Packaging Packaging is the process of building, packing, and preparing MyApp release packages for distribution. ## Building The first step in preparing the application for distribution is to build the application. 1. **Set MyApp Version** - set the initial application version. **`Properties\AssemblyInfo.cs`** ~~~cs [assembly: AssemblyVersion("1.0.0")] [assembly: AssemblyFileVersion("1.0.0")] ~~~ 2. **Switch to Release** - switch your build configuration to `Release`. 3. **Build MyApp** - build your application to ensure the latest changes are included in the package we will be creating. ## Packing Squirrel uses [NuGet](https://www.NuGet.org/) for bundling application files and various application properties (e.g., application name, version, description) in a single release package. Section [NuGet Package Metadata](../using/nuget-package-metadata.md) provides additional details on using NuGet and `.nuspec` files to automate the packing of your application. We will be going through the process using the [NuGet Package Explorer](https://github.com/NuGetPackageExplorer/NuGetPackageExplorer) to manually create a NuGet package. 1. **Creating a New NuGet Package** - the first step is to create a new NuGet package. 2. **Edit Metadata** - update package metadata for MyApp. * **Id** - name of the application (no spaces) * **Version** - version specified in `Properties\Assembly.cs` * **Dependencies** - Squirrel expects no dependencies in the package (all files should be explicitly added to the package) 3. **Add lib & net45** - add the `lib` folder and the `net45` folder to the project. Squirrel is expecting a single `lib / net45` directory provided regardless of whether your app is a `net45` application. 4. **Add Release Files** - add all the files from `bin\Release` needed by MyApp to execute (including the various files required by Squirrel). * **Include MyApp Files:** MyApp.exe, MyApp.exe.config, any non-standard .NET dll's needed by MyApp.exe. * **Include Squirrel Files:** Squirrel.dll, Splat.dll, NuGet.Squirrel.dll, Mono.Cecil.\*, DeltaCompressionDotNet.\*, * **Exclude:** *.vshost.\*, *.pdb files 5. **Save the NuGet Package File** - save the NuGet package file to where you can easily access later (e.g., `MyApp.sln` directory). Follow the given naming format (e.g., `MyApp.1.0.0.nupkg`). ![](images/1.2-nuget-package-explorer.png) ## Releasifying Releasifying is the process of preparing the `MyApp.1.0.0.nupkg` for distribution. ### Using Releasify You use the `Squirrel.exe` tool that was included in the Squirrel.Windows package you installed in the `MyApp.sln` previously. Use the [Package Manager Console](https://docs.NuGet.org/consume/package-manager-console) to execute `Squirrel.exe --releasify` command. ~~~powershell PM> Squirrel --releasify MyApp.1.0.0.nupkg ~~~ **Tip:** If you get an error stating that `...'Squirrel' is not recognized...` then you may simply need to restart Visual Studio so the `Package Manager Console` will have loaded all the package tools. ### Releasify Output The `Squirrel --releasify` command completes the following: * **Create `Releases` Directory** - creates a Releases directory (in the `MyApp.sln` directory by default). * **Create `Setup.exe`** - creates a `Setup.exe` file which includes the latest version of the application to be installed. * **Create `RELEASES` File** - creates a file that provides a list of all release files for MyApp to be used during the update process * **Create `MyApp.1.0.0-full.nupkg`** - copies the package you created to the `Releases` directory. * **Create `MyApp.*.*.*-delta.nupkg`** - if you are releasing an update, releasify creates a delta file package to reduce the update package size (see [Updating](5-updating.md) for details). **`C:\Projects\MyApp\Releases`** ![](images/1.2-releases-directory.png) ## See Also * [Visual Studio Build Packaging](../using/visual-studio-packaging.md) - integrating NuGet packaging into your visual studio build process to include packing and releasifying. --- | Previous: [1. Integrating](1-integrating.md) | Next: [3. Distributing](3-distributing.md)| |:---|:---| ================================================ FILE: docs/getting-started/3-distributing.md ================================================ | [docs](..) / [getting-started](.) / 3-distributing.md | |:---| # Step 3. Distributing After packaging MyApp for distribution, the various files in the `Releases` directory are used to distribute MyApp to users. * **Setup Application** - the `Setup.exe` application is provided to new users to install the current version of MyApp (see [Installing](4-installing.md) for details). * **Update Files** - the `RELEASES` file, along with versioned full and delta packages, are used by the update process (see [Updating](5-updating.md) for details). ## Local File Distribution For simplicity, this Getting Started guide uses a local file system location for updates. The location is defined in the update location provided to the `UpdateManager` (see code in [Integrating: Basic Updating](1-integrating.md)). This generally is not practical for updates, unless all your users have access to similar network path where the files could be easily placed. --- | Previous: [2. Packaging](2-packaging.md) | Next: [4. Installing](4-installing.md)| |:---|:---| ================================================ FILE: docs/getting-started/4-installing.md ================================================ | [docs](..) / [getting-started](.) / 4-installing.md | |:---| # Step 4. Installing The process to install MyApp is as simple as executing the `Setup.exe` application. `Setup.exe` is generated by the `Squirrel --releasify` process and is located in the `Releases` directory. ## Setup.exe `Setup.exe` is a C++ bootstrapper application used to install MyApp on the user's local system. It includes the latest full version of the MyApp package files embedded in the exe file (see [Install Process](../using/install-process.md) for details). ## Install Process Overview The `Setup.exe` application does the following (see [Install Process](../using/install-process.md) for details): * Creates a `%LocalAppData%\MyApp` directory for the MyApp to be installed. * Extracts and prepares the MyApp files under an `app-1.0.0` directory. * Launches `app-1.0.0\MyApp.exe` at the end of the setup process. ### Installed File Structure An installation for MyApp will look like the following after the initial installation. #### `%LocalAppData%\MyApp` Directory ![](images/1.3-local-app-data-dir.png) --- | Previous: [3. Distributing](3-distributing.md) | Next: [5. Updating](5-updating.md)| |:---|:---| ================================================ FILE: docs/getting-started/5-updating.md ================================================ | [docs](..) / [getting-started](.) / 5-updating.md | |:---| # Step 5. Updating The update process uses the update files generated by the `Squirrel --releasify` process. This includes the `RELEASES` file as well as versioned full and delta packages as required. The location of where to look for the distributed update files is provided to the `UpdateManager` in the MyApp code (see code in [Integrating: Basic Updating](1-integrating.md)). Updating MyApp to a new version is the culmination of integrating, packaging, and distributing after installing MyApp. The process will cause you to revisit the packaging and distributing steps. HH To release a new update, you must first build, pack, and releasify your updated application. ### Building 1. **Update MyApp Version** - update the application version. **`Properties\AssemblyInfo.cs`** ~~~cs [assembly: AssemblyVersion("1.0.1")] [assembly: AssemblyFileVersion("1.0.1")] ~~~ 2. **Switch to Release** - switch your build configuration to `Release`. 3. **Build MyApp** - build your application to ensure the latest changes are included in the package we will be creating. ### Packing Using [NuGet Package Explorer](https://npe.codeplex.com/) complete the following: 1. **Open Previous NuGet Package** - open the previous NuGet package you created for MyApp version 1.0.0. 2. **Update Version** - update the version in the metadata. 4. **Replace Release Files** - replace the changed files under `lib\net45`. You can simply drag and drop any program specific files that have changed (i.e., the `MyApp.exe` file is the only one that has updated in the example). 5. **Save the NuGet Package File as New Version** - use the "Save As..." feature to save the new version of the package `MyApp.1.0.1.nupkg`. ### Releasifying Use the [Package Manager Console](https://docs.NuGet.org/consume/package-manager-console) to execute `Squirrel.exe --releasify` command using the new `MyApp.1.0.1.nupkg` package. ~~~powershell PM> Squirrel --releasify MyApp.1.0.1.nupkg ~~~ **Tip:** If you get an error stating that `...'Squirrel' is not recognized...` then you may simply need to restart Visual Studio so the `Package Manager Console` will have loaded all the package tools. This behavior has been seen on the Community Edition of VS 2013 and 2015. #### Releasify Output After packaging the new MyApp version 1.0.1, the `Releases` directory has been updated as follows: * **Updated Setup Application** - the `Setup.exe` application has been updated to include the latest MyApp version 1.0.1 package. * **Updated Files** - the `RELEASES` file has been appended to include the newly created full and delta packages. ## Distributing the New Release The `Releases` directory now includes the updated files to distribute to your users. **`Releases` Directory** ![](images/1.5-releases-directory.png) The `RELEASES` file contains SHA1 hash, filename, and file size for each package. This information is utilized by the application update process. **`RELEASES` File** ~~~ E3F67244E4166A65310C816221A12685C83F8E6F MyApp-1.0.0-full.nupkg 600725 0D777EA94C612E8BF1EA7379164CAEFBA6E24463 MyApp-1.0.1-delta.nupkg 6030 85F4D657F8424DD437D1B33CC4511EA7AD86B1A7 MyApp-1.0.1-full.nupkg 600752 ~~~ ## Application Updating In [Step 1. Integrating](1-integrating.md), we configured MyApp to look for and install any updates in the background each time MyApp is executed. In the MyApp example, a path to the `Releases` directory on the file system was specified. ### Updating Process Overview The following steps are performed by the `UpdateManager` each time MyApp is executed (see [Update Process](../using/update-process.md) for details): * The `UpdateManager` checks the `RELEASES` file at the distribution location for any updates. * Any update packages are downloaded and the new MyApp is prepared for execution. * App shortcuts are updated and old versions of MyApp are cleaned up. ### MyApp Example The first time I run MyApp after providing the update the application is executed like normal. ![](images/1-MyApp.png) In the background, MyApp has obtained and applied the updates in the installation directory. ![](images/1.5-local-app-data-dir.png) The next time MyApp is executed, it will be the newly installed version. ![](images/1.5-MyApp.png) --- | Previous: [4. Installing](4-installing.md) | Return: [Table of Contents](../readme.md)| |:---|:---| ================================================ FILE: docs/goals.md ================================================ | [docs](.) / goals.md | |:---| # What Do We Want? Deployment and Updates for Desktop applications are a real drag. ClickOnce almost works, but has some glaring bugs that don't seem like they'll ever be fixed. So let's own our own future and build a new one. Windows apps should be as fast and as easy to install and update as apps like Google Chrome. From an app developer's side, it should be really straightforward to create an installer for my app, and publish updates to it, without having to jump through insane hoops ## Configuring * Integrating the installer for an existing .NET application should be really easy. * The client API should be able to check for updates and receive a (preferably in HTML) ChangeLog. * Developer should have control over custom actions and events during installing and updating. * Uninstall gives a chance for the application to clean up (i.e. I get to run a chunk of code on uninstall) ## Packaging * Generating an installer given an existing .NET application should be really easy, like it is for ClickOnce. * Creating an update for my app should be a very simple process that is easily automated. * Packaging will support delta files to reduce the size of update packages. ## Distributing * Hosting an update server should be really straightforward, and should be able to be done using simple HTTP (i.e. I should be able to host my installer and update feed via S3). * Support for multiple "channels" (a-la Chrome Dev/Beta/Release). ## Installing * Install is Wizard-Free™ and doesn't look awful (or at least, it should have the *possibility* to not look awful). * No UAC dialogs, which means.... * ...installs to the local user account (i.e. under `%LocalAppData%`). * No Reboots. None! * Can pull down the .NET Framework if need be. ## Updating * Updates should be able to be applied while the application is running. * At no time should the user ever be forced to stop what he or she is doing. * No Reboots. None! --- | Return: [Table of Contents](readme.md) | |:---| ================================================ FILE: docs/readme.md ================================================ | [docs](.) / readme.md | |:---| ![](artwork/Squirrel-Logo.png) # Table of Contents This document provides a table of contents for all the Squirrel documentation. ## General Documentation * **[Squirrel Goals](goals.md)** - overview of the goals of the Squirrel.Windows project. * **[Frequently Asked Questions (FAQ)](faq.md)** - list of frequently asked questions. * **[Squirrel.Windows License](../COPYING)** - copyright and license for using Squirrel.Windows ## Getting Started Guide The **[Getting Started Guide](getting-started/0-overview.md)** provides a step-by-step guide for integrating Squirrel into a simple Windows Forms application named MyApp. 1. **[Integrating](getting-started/1-integrating.md)** - integrating Squirrel `UpdateManager` into MyApp. 1. **[Packaging](getting-started/2-packaging.md)** - packaging MyApp files and preparing them for release. 1. **[Distributing](getting-started/3-distributing.md)** - providing install and update files for MyApp. 1. **[Installing](getting-started/4-installing.md)** - process of initial installation of MyApp. 1. **[Updating](getting-started/5-updating.md)** - process of updating an existing install of MyApp. ## Using Squirrel * **Installing** - documentation related to the initial installation of your application via Setup.exe (and Setup.msi). * [Install Process](using/install-process.md) - overview of the steps in the install process. * [Custom Squirrel Events](using/custom-squirrel-events.md) - preforming custom actions for Squirrel events. * [Custom Squirrel Events (non-c# apps)](using/custom-squirrel-events-non-cs.md) - steps on making a non-c# application Squirrel Aware and handling custom events. * [Loading GIF](using/loading-gif.md) - specify a "loading" image during initial install of large applications. * [GitHub](using/github.md) - overview of using GitHub for installing, distributing, and updating. * [Machine-wide Installs](using/machine-wide-installs.md) - generating an MSI file suitable for installation via Group Policy. * [Debugging Installs](using/debugging-installs.md) - tips for debugging Squirrel.Windows initial installs. * **Packaging** - documentation related to packaging app files and preparing them for release. * [Naming Conventions](using/naming.md) - overview of sources used in naming (e.g., shortcut name). * [NuGet Package Metadata](using/nuget-package-metadata.md) - overview of the NuGet metadata and its uses by Squirrel. * [Packaging Tools](using/packaging-tools.md) - tools available to assist in the process of packaging your application (e.g., NuGet, OctoPack, Auto.Squirrel) * [Squirrel Command Line](using/squirrel-command-line.md) - command line options for `Squirrel --releasify` * [Delta Packages](using/delta-packages.md) - an overview of how `Squirrel.exe` creates delta packages. * [Application Signing](using/application-signing.md) - adding code signing to `Setup.exe` and your application. * **Distributing** - documentation related to distributing the Setup.exe and update package files. * [Microsoft IIS](using/microsoft-iis.md) - overview of using Microsoft IIS for distributing your application. * [Amazon S3](using/amazon-s3.md) - overview of using Amazon S3 for distributing your application. * [GitHub](using/github.md) - overview of using GitHub for installing, distributing, and updating. * **Updating** - documentation related to updating an existing install via the `UpdateManager`. * [Update Process](using/update-process.md) - overview of the steps in the update process. * [Update Manager](using/update-manager.md) - reference guide for the `UpdateManager`. * [GitHub](using/github.md) - overview of using GitHub for installing, distributing, and updating. * [Debugging Updates](using/debugging-updates.md) - tips for debugging Squirrel.Windows updates. * [Staged Rollouts](using/staged-rollouts.md) - how to use staged rollouts to ramp up install distribution over time ## Contributing Why not give back and help make Squirrel even better by contributing to the project. * [Contributing](contributing/contributing.md) - overview of ways you can become more involved with Squirrel.Windows. * [Building Squirrel](contributing/building-squirrel.md) - steps to build squirrel for the impatient. * [VS Solution Overview](contributing/vs-solution-overview.md) - overview of the various projects in the Squirrel.Windows Visual Studio solution. * [Branching Strategy](contributing/branching-strategy.md) - overview of the different branches used in squirrel development. ================================================ FILE: docs/using/amazon-s3.md ================================================ | [docs](..) / [using](.) / amazon-s3.md |:---| # Amazon S3 Amazon S3 can be used as an easy mechanism to host your releases ## Amazon S3 Setup The following steps setup an S3 account and prepares MyApp for distribution. 1. **Register for Amazon AWS** - if you haven't already, register for an Amazon AWS account and go to the AWS Console. 2. **Create Bucket** - create a new bucket to hold your application updates 3. **Update the Package Location** - update the package location on the `UpdateManager` in MyApp to use the S3 `Link` address for the files minus the actual file name. This is the address for downloading the file and is similar to the following address: `https://s3-us-west-2.amazonaws.com/myapp.bucket` 4. **Build, Pack, Releasify** - perform the necessary steps to build, package, and releasify MyApp for distribution. 3. **Upload Files** - upload the files from the Squirrel `Releases` directory into the S3 bucket. 4. **Make Public** - make the files public by selecting the files and performing the "Make Public" action. ## Amazon S3 Updates After you have setup your S3 account, the following steps will distribute a new package for release. 4. **Build, Pack, Releasify** - perform the necessary steps to build, package, and releasify MyApp for distribution. 3. **Upload Files** - upload the new files from the Squirrel `Releases` directory. Make sure to include the new `Setup.exe` and `RELEASES` file along with any full and delta files for the new version. 4. **Make Public** - make the new files public by selecting the files and performing the "Make Public" action. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/application-signing.md ================================================ | [docs](..) / [using](.) / application-signing.md |:---| # Application Signing Signing your installer with a valid code signing certificate is one of the most important things that you need to do for production apps. Both IE SmartScreen as well as virus scanning software will give a significant amount of "points" to apps that are signed correctly, preventing your users from getting scary dialogs. Acquire a code-signing certificate - it's recommended to get a Windows Error Reporting-compatible certificate, see this [MSDN article](https://msdn.microsoft.com/library/windows/hardware/hh801887.aspx) for more information, then pass the -n parameter, which are the parameters you would pass to `signtool.exe sign` to sign the app. Squirrel makes signing easy, as it signs all of your application's executables *as well* as the final generated Setup.exe. An example invocation including both of these features would be something like: ~~~powershell PM> Squirrel --releasify MyApp.1.0.0.nupkg -n "/a /f CodeCert.pfx /p MySecretCertPassword /fd sha256 /tr http://timestamp.digicert.com /td sha256" ~~~ If you are using the [Visual Studio Build Packaging](visual-studio-packaging.md) process be careful how you escape your quotation marks in the `XML` of your `.csproj` file for the -n, --signWithParams argument. The wrapping quotation marks must be defined in `XML` safe ampersand escape strings or `"`. Within this command you will likely need quotation marks around your certificate password. Since this is already within a quoted string you will need to double quote the password: `/p ""PASSWORD""`. ~~~xml ~~~ ## See Also * [Squirrel Command Line](squirrel-command-line.md) - command line options for `Squirrel --releasify` * [Visual Studio Build Packaging](visual-studio-packaging.md) - integrating Squirrel packaging into your build process --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/custom-squirrel-events-non-cs.md ================================================ | [docs](..) / [using](.) / custom-squirrel-events-non-cs.md |:---| # Custom Squirrel Events (Non-C# Apps) Squirrel events allow you to handle custom events around the installation and updating process. ### Making Your App Squirrel Aware Add an entry to the *English* Version Block info called "SquirrelAwareVersion" with a value of "1". Typically this is done via the "App.rc" resource file. Here's a typical entry: ``` BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "Installer for Squirrel-based applications" VALUE "FileVersion", "0.5.0.0" VALUE "InternalName", "Setup.exe" VALUE "LegalCopyright", "Copyright (C) 2014" VALUE "OriginalFilename", "Setup.exe" VALUE "ProductName", "Squirrel-based application" VALUE "ProductVersion", "0.5.0.0" VALUE "SquirrelAwareVersion", "1" END END ``` ### Application Startup Commands This means that this EXE will be executed by the installer in a number of different scenarios, with special flags - you should handle them correctly: * `--squirrel-install x.y.z.m` - called when your app is installed. Exit as soon as you're finished setting up the app * `--squirrel-firstrun` - called after everything is set up. You should treat this like a normal app run (maybe show the "Welcome" screen) * `--squirrel-updated x.y.z.m` - called when your app is updated to the given version. Exit as soon as you're finished. * `--squirrel-obsolete x.y.z.m` - called when your out-of-date app is no longer the newest version. Exit as soon as you're finished. * `--squirrel-uninstall x.y.z.m` - called when your app is uninstalled. Exit as soon as you're finished. ## See Also * [Custom Squirrel Events for c# Apps](custom-squirrel-events.md) - steps on making a c# application Squirrel Aware and handling custom events. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/custom-squirrel-events.md ================================================ | [docs](..) / [using](.) / custom-squirrel-events.md |:---| # Custom Squirrel Events ## Handling Squirrel Events Squirrel events allow you to handle custom events around the installation and updating process, which is important because Squirrel doesn't do much of anything at installation time automatically. However, since the code is executing inside your application, it's way easier to do stuff than other systems where you're writing custom "installer DLLs". ### Overriding Default Behaviors When none of the apps in your package are "Squirrel-Aware", Squirrel does some things on your behalf to make your life easier, the primary one being that every EXE in your app package automatically gets a shortcut on both the Desktop and the Start Menu. Once you enable Squirrel events *for even a single EXE file*, you must do this yourself. ### Making Your App Squirrel Aware In your app's `AssemblyInfo.cs`, add the following line: ``` [assembly: AssemblyMetadata("SquirrelAwareVersion", "1")] ``` ### Using the `SquirrelAwareApp` Helper If you are writing a C# app, it is **highly encouraged** to use the `SquirrelAwareApp` helper class to implement this. Here's an implementation that is similar to the default (i.e. non-squirrel-aware) behavior: ```cs static bool ShowTheWelcomeWizard; ... static int Main(string[] args) { // NB: Note here that HandleEvents is being called as early in startup // as possible in the app. This is very important! Do _not_ call this // method as part of your app's "check for updates" code. using (var mgr = new UpdateManager(updateUrl)) { // Note, in most of these scenarios, the app exits after this method // completes! SquirrelAwareApp.HandleEvents( onInitialInstall: v => mgr.CreateShortcutForThisExe(), onAppUpdate: v => mgr.CreateShortcutForThisExe(), onAppUninstall: v => mgr.RemoveShortcutForThisExe(), onFirstRun: () => ShowTheWelcomeWizard = true); } } ``` ## App Setup Helper Methods These methods help you to set up your application in Squirrel events - if you're not using custom Squirrel events, you probably don't need to call these methods. * `[Create/Remove]ShortcutsForExecutable` - creates and removes shortcuts on the desktop or in the Start Menu. * `[Create/Remove]UninstallerRegistryEntry` - creates and removes the uninstaller entry. Normally called by `Update.exe`. ## See Also * [Custom Squirrel Events for non-c# Apps](custom-squirrel-events-non-cs.md) - steps on making a non-c# application Squirrel Aware and handling custom events. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/debugging-installs.md ================================================ | [docs](..) / [using](.) / debugging-installs.md |:---| # Debugging Installs The following tips will help you to debug the installation of an Squirrel app. ## Simulating an Install and First Run If the install of your application doesn't seem to be working, you can explore the behavior by executing the install steps from the command line: ~~~ps C:\user\AppData\Local\MyApp> Update.exe --squirrel-install 1.0.0 C:\user\AppData\Local\MyApp> Update.exe --squirrel-firstrun ~~~ The first cmd should create some shortcuts then immediately exit, then the 2nd one should start your app ([source](https://github.com/Squirrel/Squirrel.Windows/issues/525)) --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/debugging-updates.md ================================================ | [docs](..) / [using](.) / debugging-updates.md |:---| # Debugging Updates The following tips will help you to debug the update process in your application. ## Update.exe not found? Executing MyApp from Visual Studio will execute the update process and you will get the following exception from the `UpdateManager`: ~~~ Update.exe not found, not a Squirrel-installed app? ~~~ The `UpdateManager` is expecting to find the `Update.exe` application installed one directory up from the EXE (e.g., the `\bin` directory for default Visual Studio projects). To resolve this, you can simply place a file named `Update.exe` or you can copy the `Squirrel.exe` from the `MyApp\packages\squirrel.windows.1.2.2.tools` directory and rename it Update.exe (this is the actual Update.exe packaged inside `Setup.exe`). Executing MyApp from Visual Studio will now cause it to complete the update process and your `\bin` directory will resemble the `%LocalAppData\MyApp%` install directory: ![](images/debugging-update-dir.png) **Tip:** If you want to ensure that the Update.exe is always available in your output directory, you can add the Update.exe file to the Visual Studio project and set its Properties > Copy To Output Directory to 'Copy if newer'. ## Catching Update Exceptions You can catch thrown exceptions and log the results. ~~~cs using (var mgr = new UpdateManager("C:\\Projects\\MyApp\\Releases")) { await mgr.UpdateApp(); } ~~~ Alternatively, set up Splat Logging, see [here](https://github.com/Squirrel/Squirrel.Windows.Next/blob/6d7ae23602a3d9a7636265403d42c1090260e6dc/src/Update/Program.cs#L53) for an example. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/delta-packages.md ================================================ | [docs](..) / [using](.) / delta-packages.md |:---| # Delta Packages Now, once we've got a full package, we need to generate a Delta package. To do this, we'll replace all the DLL/EXEs in the NuGet packages with bsdiff files. [bspatch/bsdiff](http://code.logos.com/blog/2010/12/binary_patching_with_bsdiff.html) is a mostly efficient algorithm for calculating diffs between binary files (especially Native binaries, but it works well for .NET ones too), and a way to apply them. So, this is pretty easy: 1. Extract the previous NuGet package 1. Extract the current NuGet package 1. Replace every EXE/DLL with the bsdiff. So, `lib\net40\MyCoolApp.exe` becomes `lib\net40\MyCoolApp.exe.diff`. Create a file that contains a SHA1 of the expected resulting file and its filesize called `lib\net40\MyCoolApp.exe.shasum` 1. New DLLs in current get put in verbatim 1. Zip it back up The .shasum file has the same format as the Releases file described in the "'Latest' Pointer" section, except that it will only have one entry. So now we've got all of the *metadata* of the original package, just none of its *contents*. To get the final package, we do the following: 1. Take the previous version, expand it out 1. Take the delta version, do the same 1. For each DLL in the previous package, we bspatch it, then check the shasum file to ensure we created the correct resulting file 1. If we find a DLL in the new package, just copy it over 1. If we can't find a bspatch for a file, nuke it (it doesn't exist in the new rev) 1. Zip it back up --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/github.md ================================================ | [docs](..) / [using](.) / github.md |:---| # Using GitHub GitHub release assets can be used to distribute the necessary Squirrel files for the Squirrel install and update process. It still requires you to upload all the release files as assets for each release, but provides you a means of hosting your update files via your GitHub repository. **Important:** GitHub since February 22, 2018 [only support TLS 1.2 connections](https://githubengineering.com/crypto-removal-notice/). The host application is therefore required to use .NET framework 4.6.1, otherwise TLS 1.1 is the default protocol and check for update won't work. ## Installing from GitHub GitHub allows you to provide a [static link](https://help.github.com/articles/linking-to-releases/) to a repositories latest release page. You can direct your users to download the `Setup.exe` from the list of assets you uploaded for the release. ~~~ https://github.com/myuser/MyApp/releases/latest ~~~ **Tip:** This link simply redirects to the repositories latest release page, and cannot be used to download an asset directly (i.e., you can't simply make a static link to ".../releases/latest/Setup.exe"). However, you can use the [GitHub API with ajax](http://stackoverflow.com/a/26454035) to provide a direct link on your website and avoid the user having to select the correct file or navigate to the GitHub website. ## Distributing from GitHub The following steps are required to distribute your RELEASES and update NuGet packages with GitHub: 1. **Commit Latest Code** - In order for GitHub to mark a new release as the `Latest`, you have at least one additional commit since the last release tag was added (i.e., releases tags must not share the same commit). 1. **Create a New Release** - [Create a new GitHub release](https://help.github.com/articles/creating-releases/) in your MyApp repository matching your current release version (e.g., 1.0.0). 2. **Upload Release Files** - upload all of the files from `Releases` as assets of the GitHub release (e.g., RELEASES, MyApp.1.0.0-full.nupkg, MyApp.1.0.1-delta.nupkg, MyApp.1.0.1-full.nupkg). 3. **Set Pre-release (optional)** - if desired, set the release as a pre-release. 4. **Publish the Release** - click the "Publish Release" to make the release available to the general public and your users. **Important:** You must upload all packages as assets you wish to be available for update (i.e., the GitHubUpdateManager doesn't look back to previous GitHub releases for previous version packages). If you only include the latest packages, Squirrel will be forced to download the latest full package for each update. ## Updating with GitHub The Updating process requires you to build, package, releasify, and distribute the update files. **Important:** You must ensure there is at least one additional commit since the last version release before adding a new release. GitHub will not update the latest release if the new release tag is tied to the same last commit as a previous release tag. ### GitHub Update Manager To use GitHub release assets as your distribution mechanism you need to replace `UpdateManager` with `GitHubUpdateManager` when integrating Squirrel in your app: **`Program.cs`** ~~~cs using Squirrel; using System.Threading.Tasks; ~~~ **`static async Task Main()`** ~~~cs using (var mgr = UpdateManager.GitHubUpdateManager("https://github.com/myuser/myapp")) { await mgr.Result.UpdateApp(); } ~~~ **Important:** Make sure your url doesn't end in a forward slash ("/"). Adding the trailing forward slash will cause it to fail with a 404 error ([see #641](https://github.com/Squirrel/Squirrel.Windows/issues/641#issuecomment-201478324)). **Tip:** You can also specify that the update manager should use `prerelease` for updating (see method signature for details). **Source:** See [Issue #442](https://github.com/Squirrel/Squirrel.Windows/issues/442) for more information. ### How it Works The GitHub API is used by the `GitHubUpdateManager` to obtain the correct release and asset files for downloading. The following actions are performed: 1. Extracts the username and repository from the url (e.g., "myuser" and "myapp"). 2. Uses the GitHub API to get the latest release information. For example, the following url obtains a json list of all release information for the Squirrel.Windows repository: [https://api.github.com/repos/Squirrel/Squirrel.Windows/releases](https://api.github.com/repos/Squirrel/Squirrel.Windows/releases) 3. Obtains the correct download path from the `html_url` attribute of the latest release (or pre-release if specified) to be used as the path for downloading assets. 4. Follows the normal Squirrel update process by looking for a `RELEASES` file and downloading the necessary delta or full application packages. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/install-process.md ================================================ | [docs](..) / [using](.) / install-process.md |:---| # Install Process This section goes into detail about the install process. ## Setup.exe `Setup.exe` is a C++ bootstrapper application used to install your app on the user's local system. It is generated as part of the `Squirrel --releasify` process. The `Setup.exe` file includes the `Update.exe` application and the latest version of the MyApp package to be installed. A single executable file can be provided due to the `WriteZipToSetup.exe` tool injecting the appropriate files into the exe. In addition, the `Setup.exe` will also ensure that .NET 4.5 is installed on the user's computer. ## Install Location The `Setup.exe`, and later the `UpdateManager` in MyApp must have the ability to write files to and execute files from the application install location. To ensure permission for all types of users, the user's application data directory is selected as the install location (i.e., `%LocalAppData%\MyApp`). The installation root really only needs to consist of two types of folders: * **Packages** - folder used to download and assemble the update package files. * **App Folders** - the "installed" application files for a given version of MyApp. ``` \%LocalAppData%\MyApp \packages MyApp-1.0.0.nupkg MyApp-1.0.1-delta.nupkg MyApp-1.0.1.nupkg \app-1.0.0 MyApp.exe \app-1.0.1 MyApp.exe ``` The packages directory is effectively immutable, it simply consists of the packages we've downloaded. Using the user's local application data directory means that we the needed write-access to the install directory on a per-user basis. **Tip:** See [Machine-wide Installs](machine-wide-installs.md) for more information on ensuring your application pushed to all users in an enterprise environment. ## Install Process Overview The `Setup.exe` application preforms the following: 1. **Ensures .NET Framework Installed** - determines if .NET Framework is available, and if not relaunches itself with `/installfx45` to download and launch the .NET Framework installer. 1. **Create `%LocalAppData%\MyApp` Directory** - creates a directory for the MyApp to be installed. 2. **Extracts `Update.exe`** - extracts the `Update.exe` application to the application directory (`%LocalAppData%\MyApp`). 3. **Extracts `MyApp.1.0.0-full.nupkg`** - extracts the MyApp full application package to the `%LocalAppData%\MyApp\packages\temp` directory. 4. **Executes `Update.exe` to Finish Install** - executes the `Update.exe` application with the `/install` switch to finish the application installation and then launch the application. 1. **Copy MyApp to `app-1.0.0` Directory** - copy the full version of MyApp files to a application sub-directory (e.g., `MyApp\app-1.0.0`). 2. **Launch MyApp** - at the end of the setup process, the Updater launches the newly installed version of MyApp. 6. **MyApp Creates Shortcuts** - the first execution of the application will cause shortcuts to be created on the desktop and Windows start menu for MyApp. ## Desktop & Windows Start Shortcuts By default, application shortcuts are created on the desktop and the Windows Start menu that point to the `Update.exe` application with additional arguments pointing to the correct application to execute. **`MyApp.lnk` (Application Shortcut)** * **Target:** `C:\Users\kbailey\AppData\Local\MyApp\Update.exe --processStart MyApp.exe` * **Start in:** `C:\Users\kbailey\AppData\Local\MyApp\app-1.0.0` ## See Also * [Loading GIF](loading-gif.md) - specify a "loading" image during initial install of large applications. * [Machine-wide Installs](machine-wide-installs.md) - generating an MSI file suitable for installation via Group Policy. * [NuGet Package Metadata](nuget-package-metadata.md) - overview of the NuGet metadata and its uses by Squirrel. * [Naming Conventions](naming.md) - A more complete view of how Squirrel names everything. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/loading-gif.md ================================================ | [docs](..) / [using](.) / loading-gif.md |:---| # Loading GIF Squirrel installers don't have any UI - the goal of a Squirrel installer is to install so blindingly fast that double-clicking on Setup.exe *feels* like double-clicking on an app shortcut. Make your installer **fast**. However, for large applications, this isn't possible. For these apps, Squirrel will optionally display a graphic as a "splash screen" while installation is processing, but only if installation takes more than a pre-set amount of time. This will be centered, backed by a transparent window, and can optionally be an animated GIF. Specify this via the `-g` parameter. ~~~powershell PM> Squirrel --releasify MyApp.1.0.0.nupkg -g .\loading.gif ~~~ ## See Also * [Squirrel Command Line](squirrel-command-line.md) - command line options for `Squirrel --releasify` --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/machine-wide-installs.md ================================================ | [docs](..) / [using](.) / machine-wide-installs.md |:---| # Machine-wide Installs Squirrel's Releasify command generates an MSI file suitable for installation via Group Policy. This MSI isn't a general-purpose installer, this means that once you run the MSI, users from now on will get the app installed, on next Login. So, most normal users should continue to run the Setup.exe's generated by Releasify, but if you want to have an IT Admin Friendly version, you can hand off the MSI ## Common pitfalls ### Missing data in `.nuspec` Most users of Squirrel won't have to do anything new to enable this behavior, though certain NuGet package IDs / names might cause problems with MSI. **Source:** See [issue #466](https://github.com/Squirrel/Squirrel.Windows/issues/466) for more details. ### Nothing happens on login In cases where the end user has previously installed your application, the installer that runs on login will not re-install your application on every login. This can easily be the case if you as a developer is testing out both the EXE and the MSI. Squirrel leaves behind an almost-empty `%LocalAppData%\MyApp` folder after an uninstall. Deleting this folder (the entire folder, not just the contents) will allow the installer that runs on login to install successfully. **Source:**: See [issue #555](https://github.com/Squirrel/Squirrel.Windows/issues/555#issuecomment-253265130) for details. ## Disabling MSI Generation Generating MSIs can be disabled via the --no-msi flag as shown below: ~~~powershell PM> Squirrel --releasify MyApp.1.0.0.nupkg --no-msi ~~~ --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/microsoft-iis.md ================================================ | [docs](..) / [using](.) / microsoft-iis.md |:---| # Microsoft IIS If you use Microsoft IIS to distribute the necessary Squirrel files, you must provide a custom `Web.config` file as described below. ## Hosting on IIS All versions of IIS (including Microsoft Azure PaaS) deny serving files when the extension MIME type is unknown. If you are hosting your updates in this manner then you will need to add a `Web.config` to your downloads repository as follows: **`~/downloads/Web.config` File** ~~~xml ~~~ --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/naming.md ================================================ | [docs](..) / [using](.) / naming.md |:---| # Naming Conventions In addition to the [NuGet Package Metadata](nuget-package-metadata.md), there are other places that squirrel pulls naming information from. Here is the logic: ## Shortcut name The shortcut name is selected from the first non-null item below: 1. `[assembly: AssemblyProduct("MyApp")` (from `AssemblyInfo.cs`) 2. Squirrel NuGet Package Metadata `title` property. 3. `[assembly: AssemblyDescription("MyApp")` (from `AssemblyInfo.cs`) 4. Filename of the Exe (e.g., MyApp) ## Local Install location The local install location is determined by the `id` in the NuGet package metadata. * `%LocalAppData%\` **Warning:** Using \[dots\] (i.e., "."'s) in your package id will cause issues ([see issue #523](https://github.com/Squirrel/Squirrel.Windows/issues/523)). ## Program and Features Entry The entry in the Windows Uninstall is determined as follows: * Squirrel NuGet Package Metadata `title` property ## Releases Folder The `Squirrel --releasify` command will create update packages based on the following: * `--delta.nupkg` * `--full.nupkg` ![](images/naming-releases.png) ## See Also * [NuGet Package Metadata](nuget-package-metadata.md) - naming from the NuGet Package Metadata perspective. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/nuget-package-metadata.md ================================================ | [docs](..) / [using](.) / nuget-package-metadata.md |:---| # NuGet Package Metadata Squirrel uses information from your app's EXE as well as the NuGet package Metadata for the setup and uninstall UI. * **Id** - name of the application (**warning:** you must **[avoid using spaces and dots](https://github.com/Squirrel/Squirrel.Windows/issues/523)** in the Id). * Name of the release packages (e.g., **MyApp**-1.0.0-full.nupkg). * Local installation directory (e.g., `%LocalAppData%\MyApp`). * **Title** - used for the name of the application in the Windows Application Uninstaller. * **Version** - version specified in `Properties\Assembly.cs`. * Name of the release package (e.g., MyApp-**1.0.0**-full.nupkg). * Version number in the Windows Uninstaller (see screenshot below). * **Icon Url** - url to an icon to be used for the application. Used for the shortcuts and Windows Uninstaller icons. This must be an icon file (*.ICO) to work correctly. Note that the icon is fetched at installation time rather than packaging (source: [issue #745](https://github.com/Squirrel/Squirrel.Windows/issues/745)) * **Language** Changes the codepage in to support non english characters. Defaults to 1252 if not present. ![](images/uninstall-app.png) ## See Also * [Naming Conventions](naming.md) - overview of sources used naming (including those outside of the NuGet Package Metadata). --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/octopack.md ================================================ | [docs](..) / [using](.) / octopack.md |:---| # Using OctoPack In order to automatically construct your nuget packages you can use [OctoPack](https://github.com/OctopusDeploy/OctoPack). Octopack allows you to specify a .nuspec file which will be used to specify how your .nupkg should be created. Follow the core instructions for creating your .nuspec file on the [OctoPack](https://github.com/OctopusDeploy/OctoPack) page. You'll then need to add a files specification to match Squirrel's expected .nupkg structure: ~~~ ~~~ If you're building using Visual Studio, you will also need to edit your .csproj file to include a property group. ~~~ true ~~~ If you're using a build server, see OctoPack's guides on how to trigger it to be run. --- | Return: [Packaging Tools](packaging-tools.md) | |----| ================================================ FILE: docs/using/packaging-tools.md ================================================ | [docs](..) / [using](.) / packaging-tools.md |:---| # Packaging Tools The following tools can simplify and/or automate the packaging process. * [NuGet Docs](http://docs.nuget.org/) - documentation for NuGet packaging manager. * [NuGet Package Explorer](https://npe.codeplex.com/) - GUI tool for building NuGet packages. * [Visual Studio Build Packaging](visual-studio-packaging.md) - integrating NuGet packaging into your visual studio build process. * [OctoPack](octopack.md) - steps to use OctoPack to build the source NuGet package to provide to `squirrel --releasify`. * [Auto.Squirrel Package Manager](https://github.com/tenacious/Auto.Squirrel) - tool to fully automatize the application deploy, from build to upload the updated files. * [Paket](http://fsprojects.github.io/Paket/template-files.html) - dependency manager for .NET and mono projects, which is designed to work well with NuGet packages and also enables referencing files directly from Git repositories or any HTTP resource. * [TeamCity](teamcity.md) - tips on using the TeamCity build server to package your app. ## See Also * [Step 2. Packaging](../getting-started/2-packaging.md) - step from getting started guide on using NuGet Package Explorer. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/squirrel-command-line.md ================================================ | [docs](..) / [using](.) / squirrel-command-line.md |:---| # Squirrel Command Line Here is a simplified help output specifically around creating releases: ``` Usage: Squirrel.exe command [OPTS] Creates Squirrel packages Commands --releasify=VALUE Update or generate a releases directory with a given NuGet package Options: -h, -?, --help Display Help and exit -r, --releaseDir=VALUE Path to a release directory to use with Releasify -p, --packagesDir=VALUE Path to the NuGet Packages directory for C# apps --bootstrapperExe=VALUE Path to the Setup.exe to use as a template -g, --loadingGif=VALUE Path to an animated GIF to be displayed during installation -n, --signWithParams=VALUE Sign the installer via SignTool.exe with the parameters given --setupIcon=VALUE Path to an ICO file that will be used for the Setup executable's icon -b --baseUrl=VALUE Provides a base URL to prefix the RELEASES file packages with --no-msi Don't generate an MSI package --msi-win64 Mark the MSI as 64-bit, which is useful in Enterprise deployment scenarios --no-delta Don't generate delta packages to save time --framework-version=VALUE Set the required .NET framework version, e.g. net461 ``` ## See Also * [Loading GIF](loading-gif.md) - specify a "loading" image during initial install of large applications. * [Application Signing](application-signing.md) - adding code signing to `Setup.exe` and your application. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/staged-rollouts.md ================================================ | [docs](..) / [using](.) / staged-rollouts.md |:---| # Staged Rollouts Staged rollouts allow you to distribute the latest version of your app to a subset of users that you can increase over time, similar to rollouts on platforms like Google Play. This feature requires Squirrel.Windows 1.4.0 or above. ### How to use Staged rollouts are controlled by manually editing your `RELEASES` file. Here's an example: ~~~ e3f67244e4166a65310c816221a12685c83f8e6f myapp-1.0.0-full.nupkg 600725 ~~~ Now let's ship a new version to 10% of our userbase. ``` e3f67244e4166a65310c816221a12685c83f8e6f myapp-1.0.0-full.nupkg 600725 0d777ea94c612e8bf1ea7379164caefba6e24463 myapp-1.0.1-delta.nupkg 6030# 10% 85f4d657f8424dd437d1b33cc4511ea7ad86b1a7 myapp-1.0.1-full.nupkg 600752# 10% ``` Note that the syntax is `# nn%` - due to a bug in earlier versions of Squirrel.Windows, for now, you *must* put the `#` immediately following the file size, no spaces. Once all of your users have Squirrel 1.4.0 or higher, you can add a space after the `#` (similar to a comment). Assuming that this rollout is going well, at some point you can upload a new version of the `RELEASES` file: ``` e3f67244e4166a65310c816221a12685c83f8e6f myapp-1.0.0-full.nupkg 600725 0d777ea94c612e8bf1ea7379164caefba6e24463 myapp-1.0.1-delta.nupkg 6030# 50% 85f4d657f8424dd437d1b33cc4511ea7ad86b1a7 myapp-1.0.1-full.nupkg 600752# 50% ``` When you're confident that this release has gone successfully, you can remove the comment so that 100% of users get the file: ``` e3f67244e4166a65310c816221a12685c83f8e6f myapp-1.0.0-full.nupkg 600725 0d777ea94c612e8bf1ea7379164caefba6e24463 myapp-1.0.1-delta.nupkg 6030 85f4d657f8424dd437d1b33cc4511ea7ad86b1a7 myapp-1.0.1-full.nupkg 600752 ``` ### Handling failed rollouts If you want to pull a staged release because it hasn't gone well, you should hand-edit the RELEASES file to completely remove the bad version: ~~~ e3f67244e4166a65310c816221a12685c83f8e6f myapp-1.0.0-full.nupkg 600725 ~~~ Once you do this, you **must** increment the version number higher than your broken release (in this example, we would need to release MyApp 1.0.2). Because some of your users will be on the broken 1.0.1, releasing a _new_ 1.0.1 would result in them staying on a broken version. ================================================ FILE: docs/using/teamcity.md ================================================ | [docs](..) / [using](.) / teamcity.md |:---| # Team City Packaging ## Adding the Packaging Step When TeamCity pulls down your code, the squirrel.exe will sit under packages if it was added to your solution using NuGet. 1. Add a NuGet Pack process which will create the .nupkg based on a .nuspec file to ensure the package is correct. 2. Create a command line build process and add the following: ~~~ %system.teamcity.build.workingDir%\packages\squirrel.windows.1.4.0\tools\squirrel --releasify [BUILD_SERVER_NUPKG_PATH]\%system.build.number%.nupkg -r [OUTPUT_PATH] ~~~ **Note:** Paths may vary depending on your structure so make sure to update the path information above correctly. This will cause the appropriate files to be created just as if you had run it from the Package Manager Console. **Source:** [Issue #737](https://github.com/Squirrel/Squirrel.Windows/issues/737) ## See Also * [Packaging Tools](packaging-tools.md) - list of packaging tools to simplify and/or automate the packaging process. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/update-manager.md ================================================ | [docs](..) / [using](.) / update-manager.md |:---| # Update Manager Reference ## Basic Updating The "Easy Mode" method that does everything all in one go. * `UpdateApp` - downloads and updates the app to the latest version. ## Advanced Updating The following methods are provided to allow you to have more control of the update process (i.e., to interact with app updates and apply them if desired). * `CheckForUpdate` - checks on the server if there are updates available. Returns an `UpdateInfo` object that contains information about any pending updates. * `DownloadReleases` - downloads release files (the `nupkg` file deltas) from the server to the local machine * `ApplyReleases` - installs the downloaded packages, and returns the new `app-[version]` directory path. ### UpdateInfo The `UpdateInfo` class contains information about available and installed releases. ~~~cs public class UpdateInfo { public ReleaseEntry CurrentlyInstalledVersion; public ReleaseEntry FutureReleaseEntry; public List ReleasesToApply; } ~~~ ### ReleaseEntry The `ReleaseEntry` class contains the specifics of each release. ~~~cs public interface ReleaseEntry { public string SHA1; public string Filename; public long Filesize; public bool IsDelta; } ~~~ ## See Also * [Update Process](update-process.md) - overview of the steps in the update process. * [GitHub Update Manager](github.md) - process of using `GitHubUpdateManager`. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/update-process.md ================================================ | [docs](..) / [using](.) / update-process.md |:---| # Update Process The following steps are performed by the `UpdateManager` each time your app is executed: 1. **Check for Updates** - the `RELEASES` file at the distribution location is downloaded and compared to local `RELEASES` file to check for any updates. 2. **Download & Verify Update Packages** - if there is a new release, the `UpdateManager` determines whether to download the deltas or the latest full package (by calculating which one requires less total downloading) to update to the current version. The packages are compared against their SHA1 in the `RELEASES` file for verification. 3. **Build Full Package from Deltas** - if delta packages were downloaded, a new full package is created from the previous full package and the downloaded delta file. 3. **Install New Version** - the current version of MyApp is extracted from the full package and placed in a new `%LocalAppData%\MyApp` install directory based on the version number (e.g., `app-1.0.1`). 4. **Update Shortcuts** - desktop and Windows Start Menu shortcuts are updated to point to the new MyApp version (via the `--processStart` command line parameter passed to `Update.exe`). 5. **Previous Version Clean-up** - on the next startup of MyApp, all but current and immediately previous version of your app are deleted as part of clean up (e.g., after updating to app-1.0.5, app-1.0.4 will remain, but app-1.0.3 and before will be deleted - see [issue #589](https://github.com/Squirrel/Squirrel.Windows/issues/589)). ## Rollback Currently, there is no built-in support for rolling back to a previous version. ## See Also * [Update Manager](update-manager.md) - reference guide for the `UpdateManager`. * [Debugging Updates](debugging-updates.md) - tips on debugging your Squirrel application. --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: docs/using/visual-studio-packaging.md ================================================ | [docs](..) / [using](.) / visual-studio-packaging.md |:---| # Visual Studio Build Packaging Squirrel packaging can be easily integrated directly into your build process using only NuGet and Squirrel. ## Define Build Target The first step is to define a build target in your `.csproj` file. ```xml ``` This will generate a NuGet package from .nuspec file setting version from AssemblyInfo.cs and place it in OutDir (by default bin\Release). Then it will generate release files from it. ## Example .nuspec file for MyApp Here is an example `MyApp.nuspec` file for the above build target example. ```xml MyApp 0.0.0.0 title authors description false Copyright 2016 ``` ## Additional Notes Please be aware of the following when using this solution: * Solution needs to have nuget.exe available which can be accomplished by installing `NuGet.CommandLine` package in your solution. ```pm PM> Install-Package NuGet.CommandLine ``` * It suffers from a bug when sometimes NuGet packages are not loaded properly and throws nuget/squirrel is not recogized (9009) errors. **Tip:** In this case you may simply need to restart Visual Studio so the Package Manager Console will have loaded all the package tools * If you get the following error you may need add the full path to squirrel.exe in the build target `Exec Command` call. `'squirrel' is not recognized as an internal or external command` **Source:** [Issue #630](https://github.com/Squirrel/Squirrel.Windows/issues/630) --- | Return: [Packaging Tools](packaging-tools.md) | |----| ================================================ FILE: docs/using/x-doc-template.md ================================================ | [docs](..) / [using](.) / filename.md |:---| # Title text ## Sub-title text ~~~cs code ~~~ **Tip:** text ## See Also * [seealso]() - text --- | Return: [Table of Contents](../readme.md) | |----| ================================================ FILE: src/Directory.Build.props ================================================ Debug false $(MSBuildProjectName) $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) $(BaseOutputPath)obj\$(Configuration)\$(ProjectName)\ $(BaseOutputPath)$(Configuration)\ GitHub Copyright © GitHub 2013-2015 MS-RL Squirrel Win32 $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ $(OutputPath)$(Platform)\ ================================================ FILE: src/Setup/FxHelper.cpp ================================================ #include "stdafx.h" #include "FxHelper.h" #include "resource.h" // http://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx#net_b static const wchar_t* ndpPath = L"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full"; static const int fx45ReleaseVersion = 378389; static const int fx451ReleaseVersion = 378675; //Minimum version for .NET 4.5.1 static const int fx452ReleaseVersion = 379893; static const int fx46ReleaseVersion = 393295; //Windows 10 version, other systems are higher static const int fx461ReleaseVersion = 394254; // Minimum version for .NET 4.6.1 static const int fx462ReleaseVersion = 394802; // Minimum version for .NET 4.6.2 static const int fx47ReleaseVersion = 460798; // Minimum version for .NET 4.7 static const int fx471ReleaseVersion = 461308; // Minimum version for .NET 4.7.1 static const int fx472ReleaseVersion = 461808; // Minimum version for .NET 4.7.2 static const int fx48ReleaseVersion = 528040; // Minimum version for .NET 4.8 // According to https://msdn.microsoft.com/en-us/library/8z6watww%28v=vs.110%29.aspx, // to install .NET 4.5 we must be Vista SP2+, Windows 7 SP1+, or later. // However Anas thinks this is just for customer support, anything >= Vista will generally work. bool CFxHelper::CanInstallDotNet4_5() { return IsWindowsVistaOrGreater(); } NetVersion CFxHelper::GetRequiredDotNetVersion() { wchar_t* versionFlag = (wchar_t*)LoadResource(NULL, FindResource(NULL, (LPCWSTR)IDR_FX_VERSION_FLAG, L"FLAGS")); CString resourceFlag(versionFlag); if (resourceFlag.Compare(L"net451") == 0) return NetVersion::net451; if (resourceFlag.Compare(L"net452") == 0) return NetVersion::net452; if (resourceFlag.Compare(L"net46") == 0) return NetVersion::net46; if (resourceFlag.Compare(L"net461") == 0) return NetVersion::net461; if (resourceFlag.Compare(L"net462") == 0) return NetVersion::net462; if (resourceFlag.Compare(L"net47") == 0) return NetVersion::net47; if (resourceFlag.Compare(L"net471") == 0) return NetVersion::net471; if (resourceFlag.Compare(L"net472") == 0) return NetVersion::net472; if (resourceFlag.Compare(L"net48") == 0) return NetVersion::net48; //Default to standard net45 return NetVersion::net45; } bool CFxHelper::IsDotNetInstalled(NetVersion required) { ATL::CRegKey key; if (key.Open(HKEY_LOCAL_MACHINE, ndpPath, KEY_READ) != ERROR_SUCCESS) { return false; } DWORD dwReleaseInfo = 0; if (key.QueryDWORDValue(L"Release", dwReleaseInfo) != ERROR_SUCCESS || dwReleaseInfo < GetDotNetVersionReleaseNumber(required)) { return false; } return true; } UINT CFxHelper::GetDotNetVersionReleaseNumber(NetVersion version) { switch (version) { case NetVersion::net451: return fx451ReleaseVersion; case NetVersion::net452: return fx452ReleaseVersion; case NetVersion::net46: return fx46ReleaseVersion; case NetVersion::net461: return fx461ReleaseVersion; case NetVersion::net462: return fx462ReleaseVersion; case NetVersion::net47: return fx47ReleaseVersion; case NetVersion::net471: return fx471ReleaseVersion; case NetVersion::net472: return fx472ReleaseVersion; case NetVersion::net48: return fx48ReleaseVersion; case NetVersion::net45: default: return fx45ReleaseVersion; } } class ATL_NO_VTABLE CDownloadProgressCallback : public CComObjectRoot, public IBindStatusCallback { public: CDownloadProgressCallback() { } DECLARE_NOT_AGGREGATABLE(CDownloadProgressCallback) BEGIN_COM_MAP(CDownloadProgressCallback) COM_INTERFACE_ENTRY(IBindStatusCallback) END_COM_MAP() DECLARE_PROTECT_FINAL_CONSTRUCT() HRESULT FinalConstruct() { return S_OK; } void FinalRelease() { } void SetProgressDialog(IProgressDialog* pd) { m_spProgressDialog = pd; } STDMETHOD(OnProgress)(ULONG ulProgress, ULONG ulProgressMax, ULONG /*ulStatusCode*/, LPCWSTR /*szStatusText*/) { if (m_spProgressDialog != nullptr) { if (m_spProgressDialog->HasUserCancelled()) { return E_ABORT; } m_spProgressDialog->SetProgress(ulProgress, ulProgressMax); } return S_OK; } STDMETHOD(OnStartBinding)(DWORD /*dwReserved*/, IBinding *pBinding) { return E_NOTIMPL; } STDMETHOD(GetPriority)(LONG *pnPriority) { return E_NOTIMPL; } STDMETHOD(OnLowResource)(DWORD /*reserved*/) { return E_NOTIMPL; } STDMETHOD(OnStopBinding)(HRESULT /*hresult*/, LPCWSTR /*szError*/) { return E_NOTIMPL; } STDMETHOD(GetBindInfo)(DWORD *pgrfBINDF, BINDINFO *pbindInfo) { return E_NOTIMPL; } STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC * /*pformatetc*/, STGMEDIUM *pstgmed) { return E_NOTIMPL; } STDMETHOD(OnObjectAvailable)(REFIID /*riid*/, IUnknown * /*punk*/) { return E_NOTIMPL; } private: CComPtr m_spProgressDialog; }; HRESULT CFxHelper::InstallDotNetFramework(NetVersion version, bool isQuiet) { if (!isQuiet) { CTaskDialog dlg; TASKDIALOG_BUTTON buttons[] = { { 1, L"Install", }, { 2, L"Cancel", }, }; dlg.SetButtons(buttons, 2); dlg.SetMainInstructionText(GetInstallerMainInstructionForVersion(version)); dlg.SetContentText(GetInstallerContentForVersion(version)); dlg.SetMainIcon(TD_INFORMATION_ICON); dlg.SetExpandedInformationText(GetInstallerExpandedInfoForVersion(version)); int nButton; if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton)) || nButton != 1) { return S_FALSE; } } HRESULT hr = E_FAIL; WCHAR szFinalTempFileName[_MAX_PATH] = L""; CComPtr bscb; CComPtr pd; SHELLEXECUTEINFO execInfo = { sizeof(execInfo), }; CString url; url.LoadString(GetInstallerUrlForVersion(version)); WCHAR szTempPath[_MAX_PATH]; DWORD dwTempPathResult = GetTempPath(_MAX_PATH, szTempPath); if (dwTempPathResult == 0) { hr = AtlHresultFromLastError(); goto out; } else if (dwTempPathResult > _MAX_PATH) { hr = DISP_E_BUFFERTOOSMALL; goto out; } WCHAR szTempFileName[_MAX_PATH]; if (!GetTempFileName(szTempPath, L"NDP", 0, szTempFileName)) { hr = AtlHresultFromLastError(); goto out; } szTempFileName[_countof(szTempFileName) - 1] = L'\0'; if (wcscpy_s(szFinalTempFileName, _countof(szFinalTempFileName), szTempFileName) != 0) { hr = E_FAIL; goto out; } WCHAR* pLastDot = wcsrchr(szFinalTempFileName, L'.'); if (pLastDot == nullptr) { if (wcscat_s(szFinalTempFileName, _countof(szFinalTempFileName), L".exe") != 0) { hr = E_FAIL; goto out; } } else { if (wcscpy_s(pLastDot, _countof(szFinalTempFileName) - (pLastDot - szFinalTempFileName), L".exe") != 0) { hr = E_FAIL; goto out; } } if (!MoveFile(szTempFileName, szFinalTempFileName)) { hr = AtlHresultFromLastError(); goto out; } if (!isQuiet) { pd.CoCreateInstance(CLSID_ProgressDialog); if (pd != nullptr) { pd->SetTitle(L"Downloading"); pd->SetLine(1, L"Downloading the .NET Framework installer", FALSE, nullptr); pd->StartProgressDialog(nullptr, nullptr, 0, nullptr); CComObject* bscbObj = nullptr; if (SUCCEEDED(CComObject::CreateInstance(&bscbObj))) { bscbObj->SetProgressDialog(pd); bscb = bscbObj; } } } hr = URLDownloadToFile(nullptr, url, szFinalTempFileName, 0, bscb); if (pd != nullptr) { pd->StopProgressDialog(); } if (hr != S_OK) { goto out; } execInfo.fMask = SEE_MASK_NOCLOSEPROCESS; execInfo.lpVerb = L"open"; execInfo.lpFile = szFinalTempFileName; if (isQuiet) { execInfo.lpParameters = L"/q /norestart"; } else { execInfo.lpParameters = L"/passive /norestart /showrmui"; } execInfo.nShow = SW_SHOW; if (!ShellExecuteEx(&execInfo)) { hr = AtlHresultFromLastError(); goto out; } WaitForSingleObject(execInfo.hProcess, INFINITE); DWORD exitCode; if (!GetExitCodeProcess(execInfo.hProcess, &exitCode)) { hr = AtlHresultFromLastError(); goto out; } if (exitCode == 1641 || exitCode == 3010) { // The framework installer wants a reboot before we can continue // See https://msdn.microsoft.com/en-us/library/ee942965%28v=vs.110%29.aspx hr = HandleRebootRequirement(isQuiet); // Exit as a failure, so that setup doesn't carry on now } else { hr = exitCode != 0 ? E_FAIL : S_OK; } out: if (execInfo.hProcess != NULL && execInfo.hProcess != INVALID_HANDLE_VALUE) { CloseHandle(execInfo.hProcess); } if (*szFinalTempFileName != L'\0') { DeleteFile(szFinalTempFileName); } return hr; } UINT CFxHelper::GetInstallerMainInstructionForVersion(NetVersion version) { if (version >= NetVersion::net48) { return IDS_FXINSTRUCTION48; } if (version >= NetVersion::net47) { return IDS_FXINSTRUCTION47; } if (version >= NetVersion::net46) { return IDS_FXINSTRUCTION46; } return IDS_FXINSTRUCTION; } UINT CFxHelper::GetInstallerContentForVersion(NetVersion version) { if (version >= NetVersion::net48) { return IDS_FXCONTENT48; } if (version >= NetVersion::net47) { return IDS_FXCONTENT47; } if (version >= NetVersion::net46) { return IDS_FXCONTENT46; } return IDS_FXCONTENT; } UINT CFxHelper::GetInstallerExpandedInfoForVersion(NetVersion version) { if (version >= NetVersion::net48) { return IDS_FXEXPANDEDINFO48; } if (version >= NetVersion::net47) { return IDS_FXEXPANDEDINFO47; } if (version >= NetVersion::net46) { return IDS_FXEXPANDEDINFO46; } return IDS_FXEXPANDEDINFO; } UINT CFxHelper::GetInstallerUrlForVersion(NetVersion version) { if (version >= NetVersion::net48) { return IDS_FXDOWNLOADURL48; } if (version >= NetVersion::net47) { return IDS_FXDOWNLOADURL47; } if (version >= NetVersion::net46) { return IDS_FXDOWNLOADURL46; } return IDS_FXDOWNLOADURL; } // Deal with the aftermath of the framework installer telling us that we need to reboot HRESULT CFxHelper::HandleRebootRequirement(bool isQuiet) { if (isQuiet) { // Don't silently reboot - just error-out fprintf_s(stderr, "A reboot is required following .NET installation - reboot then run installer again.\n"); return E_FAIL; } CTaskDialog dlg; TASKDIALOG_BUTTON buttons[] = { { 1, L"Restart Now", }, { 2, L"Cancel", }, }; dlg.SetButtons(buttons, 2); dlg.SetMainInstructionText(L"Restart System"); dlg.SetContentText(L"To finish installing the .NET Framework, the system now needs to restart. The installation will finish after you restart and log-in again."); dlg.SetMainIcon(TD_INFORMATION_ICON); dlg.SetExpandedInformationText(L"If you click 'Cancel', you'll need to re-run this setup program yourself, after restarting your system."); int nButton; if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton)) || nButton != 1) { return S_FALSE; } // We need to set up a runonce entry to restart this installer once the reboot has happened if (!WriteRunOnceEntry()) { return E_FAIL; } // And now, reboot if (!RebootSystem()) { return E_FAIL; } // About to reboot, but just in case... return S_FALSE; } // // Write a runonce entry to the registry to tell it to continue with // setup after a reboot // bool CFxHelper::WriteRunOnceEntry() { ATL::CRegKey key; if (key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", KEY_WRITE) != ERROR_SUCCESS) { return false; } TCHAR exePath[MAX_PATH]; GetModuleFileName(NULL, exePath, MAX_PATH); if (key.SetStringValue(L"SquirrelInstall", exePath) != ERROR_SUCCESS) { return false; } return true; } bool CFxHelper::RebootSystem() { // First we need to enable the SE_SHUTDOWN_NAME privilege LUID luid; if (!LookupPrivilegeValue(L"", SE_SHUTDOWN_NAME, &luid)) { return false; } HANDLE hToken = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) { return false; } TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, 0)) { CloseHandle(hToken); return false; } // Now we have that privilege, we can ask Windows to restart return ExitWindowsEx(EWX_REBOOT, 0) != 0; } ================================================ FILE: src/Setup/FxHelper.h ================================================ #pragma once enum class NetVersion {net45=0, net451=1, net452=2, net46=3, net461=4, net462=5, net47=6, net471=7, net472=8, net48=9}; class CFxHelper { public: static NetVersion GetRequiredDotNetVersion(); static bool CanInstallDotNet4_5(); static bool IsDotNetInstalled(NetVersion requiredVersion); static HRESULT InstallDotNetFramework(NetVersion version, bool isQuiet); private: static HRESULT HandleRebootRequirement(bool isQuiet); static bool WriteRunOnceEntry(); static bool RebootSystem(); static UINT GetDotNetVersionReleaseNumber(NetVersion version); static UINT GetInstallerUrlForVersion(NetVersion version); static UINT GetInstallerMainInstructionForVersion(NetVersion version); static UINT GetInstallerContentForVersion(NetVersion version); static UINT GetInstallerExpandedInfoForVersion(NetVersion version); }; ================================================ FILE: src/Setup/MachineInstaller.cpp ================================================ #include "stdafx.h" #include "unzip.h" #include "MachineInstaller.h" #include "resource.h" #include bool directoryExists(wchar_t* path) { DWORD dwResult = GetFileAttributes(path); if (dwResult != INVALID_FILE_ATTRIBUTES) { return true; } // NB: The directory could exist but we can't access it, let's check DWORD dwLastError = GetLastError(); if (dwLastError == ERROR_FILE_NOT_FOUND) return false; if (dwLastError == ERROR_PATH_NOT_FOUND) return false; return true; } bool MachineInstaller::ShouldSilentInstall() { // Figure out the package name from our own EXE name // The name consist of [$pkgName]DeploymentTool.exe wchar_t ourFile[MAX_PATH]; HMODULE hMod = GetModuleHandle(NULL); GetModuleFileName(hMod, ourFile, _countof(ourFile)); CString fullPath = CString(ourFile); CString pkgName = CString(ourFile + fullPath.ReverseFind(L'\\')); pkgName.Replace(L"DeploymentTool.exe", L""); wchar_t installFolder[MAX_PATH]; // NB: Users often get into the sitch where they install the MSI, then try to // install the standalone package on top of that. In previous versions we tried // to detect if the app was properly installed, but now we're taking the much // more conservative approach, that if the package dir exists in any way, we're // bailing out // C:\Users\Username\AppData\Local\$pkgName SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, installFolder); wcscat(installFolder, L"\\"); wcscat(installFolder, pkgName); if (directoryExists(installFolder)) { return false; } // C:\ProgramData\$pkgName\$username wchar_t username[512]; DWORD unamesize = _countof(username); SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, installFolder); GetUserName(username, &unamesize); wcscat(installFolder, L"\\"); wcscat(installFolder, pkgName); wcscat(installFolder, L"\\"); wcscat(installFolder, username); if (directoryExists(installFolder)) { return false; } // None of these exist, we should install return true; } ================================================ FILE: src/Setup/MachineInstaller.h ================================================ #pragma once class MachineInstaller { public: static bool ShouldSilentInstall(); }; ================================================ FILE: src/Setup/Setup.h ================================================ #pragma once #include "resource.h" ================================================ FILE: src/Setup/Setup.vcxproj ================================================  Debug Win32 Release Win32 {C1D40624-A484-438A-B846-052F321C89D1} Application Win32Proj v142 Unicode Setup 10.0 true false false true true $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)/wtl90 false $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)/wtl90 Use Level3 Disabled WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) true Windows true urlmon.lib comctl32.dll;shell32.dll;shlwapi.dll;urlmon.dll;%(DelayLoadDLLs) compat.manifest Level3 Use MinSpace true true WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true Size true MultiThreaded true Windows true true true AsInvoker urlmon.lib comctl32.dll;shell32.dll;shlwapi.dll;urlmon.dll;%(DelayLoadDLLs) compat.manifest true Create ================================================ FILE: src/Setup/Setup.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Source Files Resource Files Resource Files Resource Files Resource Files Resource Files ================================================ FILE: src/Setup/UpdateRunner.cpp ================================================ #include "stdafx.h" #include "unzip.h" #include "Resource.h" #include "UpdateRunner.h" #include void CUpdateRunner::DisplayErrorMessage(CString& errorMessage, wchar_t* logFile) { CTaskDialog dlg; TASKDIALOG_BUTTON buttons[] = { { 1, L"Open Setup Log", }, { 2, L"Close", }, }; // TODO: Something about contacting support? if (logFile == NULL) { dlg.SetButtons(&buttons[1], 1, 1); } else { dlg.SetButtons(buttons, 2, 1); } dlg.SetMainInstructionText(L"Installation has failed"); dlg.SetContentText(errorMessage); dlg.SetMainIcon(TD_ERROR_ICON); int nButton; if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton))) { return; } if (nButton == 1 && logFile != NULL) { ShellExecute(NULL, NULL, logFile, NULL, NULL, SW_SHOW); } } HRESULT CUpdateRunner::AreWeInWine() { // NB: Behaving differently in Wine is *usually* discouraged // https://wiki.winehq.org/Developer_FAQ#How_can_I_detect_Wine.3F HMODULE hntdll = GetModuleHandle(L"ntdll.dll"); if (!hntdll) { // NB: This can never fail but we'll be pedantic return E_FAIL; } return GetProcAddress(hntdll, "wine_get_version") != NULL ? S_OK : S_FALSE; } HRESULT CUpdateRunner::AreWeUACElevated() { HANDLE hProcess = GetCurrentProcess(); HANDLE hToken = 0; HRESULT hr; if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto out; } TOKEN_ELEVATION_TYPE elevType; DWORD dontcare; if (!GetTokenInformation(hToken, TokenElevationType, &elevType, sizeof(TOKEN_ELEVATION_TYPE), &dontcare)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto out; } hr = (elevType == TokenElevationTypeFull ? S_OK : S_FALSE); out: if (hToken) { CloseHandle(hToken); } return hr; } HRESULT FindDesktopFolderView(REFIID riid, void **ppv) { HRESULT hr; CComPtr spShellWindows; spShellWindows.CoCreateInstance(CLSID_ShellWindows); CComVariant vtLoc(CSIDL_DESKTOP); CComVariant vtEmpty; long lhwnd; CComPtr spdisp; hr = spShellWindows->FindWindowSW( &vtLoc, &vtEmpty, SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp); if (FAILED(hr)) return hr; CComPtr spBrowser; hr = CComQIPtr(spdisp)->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&spBrowser)); if (FAILED(hr)) return hr; CComPtr spView; hr = spBrowser->QueryActiveShellView(&spView); if (FAILED(hr)) return hr; hr = spView->QueryInterface(riid, ppv); if (FAILED(hr)) return hr; return S_OK; } HRESULT GetDesktopAutomationObject(REFIID riid, void **ppv) { HRESULT hr; CComPtr spsv; hr = FindDesktopFolderView(IID_PPV_ARGS(&spsv)); if (FAILED(hr)) return hr; CComPtr spdispView; hr = spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView)); if (FAILED(hr)) return hr; return spdispView->QueryInterface(riid, ppv); } HRESULT CUpdateRunner::ShellExecuteFromExplorer(LPWSTR pszFile, LPWSTR pszParameters) { HRESULT hr; CComPtr spFolderView; hr = GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView)); if (FAILED(hr)) return hr; CComPtr spdispShell; hr = spFolderView->get_Application(&spdispShell); if (FAILED(hr)) return hr; return CComQIPtr(spdispShell)->ShellExecute( CComBSTR(pszFile), CComVariant(pszParameters ? pszParameters : L""), CComVariant(L""), CComVariant(L""), CComVariant(SW_SHOWDEFAULT)); } bool CUpdateRunner::DirectoryExists(wchar_t* szPath) { DWORD dwAttrib = GetFileAttributes(szPath); return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); } bool CUpdateRunner::DirectoryIsWritable(wchar_t * szPath) { wchar_t szTempFileName[MAX_PATH]; UINT uRetVal = GetTempFileNameW(szPath, L"Squirrel", 0, szTempFileName); if (uRetVal == 0) { return false; } DeleteFile(szTempFileName); return true; } int CUpdateRunner::ExtractUpdaterAndRun(wchar_t* lpCommandLine, bool useFallbackDir) { PROCESS_INFORMATION pi = { 0 }; STARTUPINFO si = { 0 }; CResource zipResource; wchar_t targetDir[MAX_PATH] = { 0 }; wchar_t logFile[MAX_PATH]; std::vector to_delete; wchar_t* envSquirrelTemp = _wgetenv(L"SQUIRREL_TEMP"); if (envSquirrelTemp && DirectoryExists(envSquirrelTemp) && DirectoryIsWritable(envSquirrelTemp) && !PathIsUNCW(envSquirrelTemp)) { _swprintf_c(targetDir, _countof(targetDir), L"%s", envSquirrelTemp); goto gotADir; } if (!useFallbackDir) { SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, targetDir); goto gotADir; } wchar_t username[512]; wchar_t appDataDir[MAX_PATH]; ULONG unameSize = _countof(username); SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, appDataDir); GetUserName(username, &unameSize); _swprintf_c(targetDir, _countof(targetDir), L"%s\\%s", appDataDir, username); if (!CreateDirectory(targetDir, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { wchar_t err[4096]; _swprintf_c(err, _countof(err), L"Unable to write to %s - IT policies may be restricting access to this folder", targetDir); DisplayErrorMessage(CString(err), NULL); return -1; } gotADir: wcscat_s(targetDir, _countof(targetDir), L"\\SquirrelTemp"); if (!CreateDirectory(targetDir, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { wchar_t err[4096]; _swprintf_c(err, _countof(err), L"Unable to write to %s - IT policies may be restricting access to this folder", targetDir); if (useFallbackDir) { DisplayErrorMessage(CString(err), NULL); } goto failedExtract; } swprintf_s(logFile, L"%s\\SquirrelSetup.log", targetDir); if (!zipResource.Load(L"DATA", IDR_UPDATE_ZIP)) { goto failedExtract; } DWORD dwSize = zipResource.GetSize(); if (dwSize < 0x100) { goto failedExtract; } BYTE* pData = (BYTE*)zipResource.Lock(); HZIP zipFile = OpenZip(pData, dwSize, NULL); SetUnzipBaseDir(zipFile, targetDir); // NB: This library is kind of a disaster ZRESULT zr; int index = 0; do { ZIPENTRY zentry; wchar_t targetFile[MAX_PATH]; zr = GetZipItem(zipFile, index, &zentry); if (zr != ZR_OK && zr != ZR_MORE) { break; } // NB: UnzipItem won't overwrite data, we need to do it ourselves swprintf_s(targetFile, L"%s\\%s", targetDir, zentry.name); DeleteFile(targetFile); if (UnzipItem(zipFile, index, zentry.name) != ZR_OK) break; to_delete.push_back(CString(targetFile)); index++; } while (zr == ZR_MORE || zr == ZR_OK); CloseZip(zipFile); zipResource.Release(); // nfi if the zip extract actually worked, check for Update.exe wchar_t updateExePath[MAX_PATH]; swprintf_s(updateExePath, L"%s\\%s", targetDir, L"Update.exe"); if (GetFileAttributes(updateExePath) == INVALID_FILE_ATTRIBUTES) { goto failedExtract; } // Run Update.exe si.cb = sizeof(STARTUPINFO); si.wShowWindow = SW_SHOW; si.dwFlags = STARTF_USESHOWWINDOW; if (!lpCommandLine || wcsnlen_s(lpCommandLine, MAX_PATH) < 1) { lpCommandLine = L""; } wchar_t cmd[MAX_PATH]; swprintf_s(cmd, L"\"%s\" --install . %s", updateExePath, lpCommandLine); if (!CreateProcess(NULL, cmd, NULL, NULL, false, 0, NULL, targetDir, &si, &pi)) { goto failedExtract; } WaitForSingleObject(pi.hProcess, INFINITE); DWORD dwExitCode; if (!GetExitCodeProcess(pi.hProcess, &dwExitCode)) { dwExitCode = (DWORD)-1; } if (dwExitCode != 0) { DisplayErrorMessage(CString( L"There was an error while installing the application. " L"Check the setup log for more information and contact the author."), logFile); } for (unsigned int i = 0; i < to_delete.size(); i++) { DeleteFile(to_delete[i]); } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return (int) dwExitCode; failedExtract: if (!useFallbackDir) { // Take another pass at it, using C:\ProgramData instead return ExtractUpdaterAndRun(lpCommandLine, true); } DisplayErrorMessage(CString(L"Failed to extract installer"), NULL); return (int) dwExitCode; } ================================================ FILE: src/Setup/UpdateRunner.h ================================================ #pragma once class CUpdateRunner { public: static void DisplayErrorMessage(CString& errorMessage, wchar_t* logFile); static HRESULT AreWeInWine(); static HRESULT AreWeUACElevated(); static HRESULT ShellExecuteFromExplorer(LPWSTR pszFile, LPWSTR pszParameters); static bool DirectoryExists(wchar_t* szPath); static bool DirectoryIsWritable(wchar_t* szPath); static int ExtractUpdaterAndRun(wchar_t* lpCommandLine, bool useFallbackDir); }; ================================================ FILE: src/Setup/compat.manifest ================================================ ================================================ FILE: src/Setup/stdafx.cpp ================================================ // stdafx.cpp : source file that includes just the standard includes // Setup.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" // TODO: reference any additional headers you need in STDAFX.H // and not in this file ================================================ FILE: src/Setup/stdafx.h ================================================ // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #define _CRT_SECURE_NO_WARNINGS 1 #pragma once #include "targetver.h" #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #define SECURITY_WIN32 // Windows Header Files: #include #include #include #include // C RunTime Header Files #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined _M_IX86 #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") #elif defined _M_IA64 #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") #elif defined _M_X64 #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") #else #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #endif ================================================ FILE: src/Setup/targetver.h ================================================ #pragma once // Including SDKDDKVer.h defines the highest available Windows platform. // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. #include #define _WIN32_WINNT 0x0600 #include ================================================ FILE: src/Setup/unzip.cpp ================================================ #include "stdafx.h" #include "unzip.h" // THIS FILE is almost entirely based upon code by Jean-loup Gailly // and Mark Adler. It has been modified by Lucian Wischik. // The modifications were: incorporate the bugfixes of 1.1.4, allow // unzipping to/from handles/pipes/files/memory, encryption, unicode, // a windowsish api, and putting everything into a single .cpp file. // The original code may be found at http://www.gzip.org/zlib/ // The original copyright text follows. // // // // zlib.h -- interface of the 'zlib' general purpose compression library // version 1.1.3, July 9th, 1998 // // Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // // Jean-loup Gailly Mark Adler // jloup@gzip.org madler@alumni.caltech.edu // // // The data format used by the zlib library is described by RFCs (Request for // Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt // (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). // // // The 'zlib' compression library provides in-memory compression and // decompression functions, including integrity checks of the uncompressed // data. This version of the library supports only one compression method // (deflation) but other algorithms will be added later and will have the same // stream interface. // // Compression can be done in a single step if the buffers are large // enough (for example if an input file is mmap'ed), or can be done by // repeated calls of the compression function. In the latter case, the // application must provide more input and/or consume the output // (providing more output space) before each call. // // The library also supports reading and writing files in gzip (.gz) format // with an interface similar to that of stdio. // // The library does not install any signal handler. The decoder checks // the consistency of the compressed data, so the library should never // crash even in case of corrupted input. // // for more info about .ZIP format, see ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip // PkWare has also a specification at ftp://ftp.pkware.com/probdesc.zip #define ZIP_HANDLE 1 #define ZIP_FILENAME 2 #define ZIP_MEMORY 3 #define zmalloc(len) malloc(len) #define zfree(p) free(p) /* void *zmalloc(unsigned int len) { char *buf = new char[len+32]; for (int i=0; i<16; i++) { buf[i]=i; buf[len+31-i]=i; } *((unsigned int*)buf) = len; char c[1000]; wsprintf(c,"malloc 0x%lx - %lu",buf+16,len); OutputDebugString(c); return buf+16; } void zfree(void *buf) { char c[1000]; wsprintf(c,"free 0x%lx",buf); OutputDebugString(c); char *p = ((char*)buf)-16; unsigned int len = *((unsigned int*)p); bool blown=false; for (int i=0; i<16; i++) { char lo = p[i]; char hi = p[len+31-i]; if (hi!=i || (lo!=i && i>4)) blown=true; } if (blown) { OutputDebugString("BLOWN!!!"); } delete[] p; } */ typedef struct tm_unz_s { unsigned int tm_sec; // seconds after the minute - [0,59] unsigned int tm_min; // minutes after the hour - [0,59] unsigned int tm_hour; // hours since midnight - [0,23] unsigned int tm_mday; // day of the month - [1,31] unsigned int tm_mon; // months since January - [0,11] unsigned int tm_year; // years - [1980..2044] } tm_unz; // unz_global_info structure contain global data about the ZIPfile typedef struct unz_global_info_s { unsigned long number_entry; // total number of entries in the central dir on this disk unsigned long size_comment; // size of the global comment of the zipfile } unz_global_info; // unz_file_info contain information about a file in the zipfile typedef struct unz_file_info_s { unsigned long version; // version made by 2 bytes unsigned long version_needed; // version needed to extract 2 bytes unsigned long flag; // general purpose bit flag 2 bytes unsigned long compression_method; // compression method 2 bytes unsigned long dosDate; // last mod file date in Dos fmt 4 bytes unsigned long crc; // crc-32 4 bytes unsigned long compressed_size; // compressed size 4 bytes unsigned long uncompressed_size; // uncompressed size 4 bytes unsigned long size_filename; // filename length 2 bytes unsigned long size_file_extra; // extra field length 2 bytes unsigned long size_file_comment; // file comment length 2 bytes unsigned long disk_num_start; // disk number start 2 bytes unsigned long internal_fa; // internal file attributes 2 bytes unsigned long external_fa; // external file attributes 4 bytes tm_unz tmu_date; } unz_file_info; #define UNZ_OK (0) #define UNZ_END_OF_LIST_OF_FILE (-100) #define UNZ_ERRNO (Z_ERRNO) #define UNZ_EOF (0) #define UNZ_PARAMERROR (-102) #define UNZ_BADZIPFILE (-103) #define UNZ_INTERNALERROR (-104) #define UNZ_CRCERROR (-105) #define UNZ_PASSWORD (-106) #define ZLIB_VERSION "1.1.3" // Allowed flush values; see deflate() for details #define Z_NO_FLUSH 0 #define Z_SYNC_FLUSH 2 #define Z_FULL_FLUSH 3 #define Z_FINISH 4 // compression levels #define Z_NO_COMPRESSION 0 #define Z_BEST_SPEED 1 #define Z_BEST_COMPRESSION 9 #define Z_DEFAULT_COMPRESSION (-1) // compression strategy; see deflateInit2() for details #define Z_FILTERED 1 #define Z_HUFFMAN_ONLY 2 #define Z_DEFAULT_STRATEGY 0 // Possible values of the data_type field #define Z_BINARY 0 #define Z_ASCII 1 #define Z_UNKNOWN 2 // The deflate compression method (the only one supported in this version) #define Z_DEFLATED 8 // for initializing zalloc, zfree, opaque #define Z_NULL 0 // case sensitivity when searching for filenames #define CASE_SENSITIVE 1 #define CASE_INSENSITIVE 2 // Return codes for the compression/decompression functions. Negative // values are errors, positive values are used for special but normal events. #define Z_OK 0 #define Z_STREAM_END 1 #define Z_NEED_DICT 2 #define Z_ERRNO (-1) #define Z_STREAM_ERROR (-2) #define Z_DATA_ERROR (-3) #define Z_MEM_ERROR (-4) #define Z_BUF_ERROR (-5) #define Z_VERSION_ERROR (-6) // Basic data types typedef unsigned char Byte; // 8 bits typedef unsigned int uInt; // 16 bits or more typedef unsigned long uLong; // 32 bits or more typedef void *voidpf; typedef void *voidp; typedef long z_off_t; typedef voidpf (*alloc_func) (voidpf opaque, uInt items, uInt size); typedef void (*free_func) (voidpf opaque, voidpf address); struct internal_state; typedef struct z_stream_s { Byte *next_in; // next input byte uInt avail_in; // number of bytes available at next_in uLong total_in; // total nb of input bytes read so far Byte *next_out; // next output byte should be put there uInt avail_out; // remaining free space at next_out uLong total_out; // total nb of bytes output so far char *msg; // last error message, NULL if no error struct internal_state *state; // not visible by applications alloc_func zalloc; // used to allocate the internal state free_func zfree; // used to free the internal state voidpf opaque; // private data object passed to zalloc and zfree int data_type; // best guess about the data type: ascii or binary uLong adler; // adler32 value of the uncompressed data uLong reserved; // reserved for future use } z_stream; typedef z_stream *z_streamp; // The application must update next_in and avail_in when avail_in has // dropped to zero. It must update next_out and avail_out when avail_out // has dropped to zero. The application must initialize zalloc, zfree and // opaque before calling the init function. All other fields are set by the // compression library and must not be updated by the application. // // The opaque value provided by the application will be passed as the first // parameter for calls of zalloc and zfree. This can be useful for custom // memory management. The compression library attaches no meaning to the // opaque value. // // zalloc must return Z_NULL if there is not enough memory for the object. // If zlib is used in a multi-threaded application, zalloc and zfree must be // thread safe. // // The fields total_in and total_out can be used for statistics or // progress reports. After compression, total_in holds the total size of // the uncompressed data and may be saved for use in the decompressor // (particularly if the decompressor wants to decompress everything in // a single step). // // basic functions const char *zlibVersion (); // The application can compare zlibVersion and ZLIB_VERSION for consistency. // If the first character differs, the library code actually used is // not compatible with the zlib.h header file used by the application. // This check is automatically made by inflateInit. int inflate (z_streamp strm, int flush); // // inflate decompresses as much data as possible, and stops when the input // buffer becomes empty or the output buffer becomes full. It may some // introduce some output latency (reading input without producing any output) // except when forced to flush. // // The detailed semantics are as follows. inflate performs one or both of the // following actions: // // - Decompress more input starting at next_in and update next_in and avail_in // accordingly. If not all input can be processed (because there is not // enough room in the output buffer), next_in is updated and processing // will resume at this point for the next call of inflate(). // // - Provide more output starting at next_out and update next_out and avail_out // accordingly. inflate() provides as much output as possible, until there // is no more input data or no more space in the output buffer (see below // about the flush parameter). // // Before the call of inflate(), the application should ensure that at least // one of the actions is possible, by providing more input and/or consuming // more output, and updating the next_* and avail_* values accordingly. // The application can consume the uncompressed output when it wants, for // example when the output buffer is full (avail_out == 0), or after each // call of inflate(). If inflate returns Z_OK and with zero avail_out, it // must be called again after making room in the output buffer because there // might be more output pending. // // If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much // output as possible to the output buffer. The flushing behavior of inflate is // not specified for values of the flush parameter other than Z_SYNC_FLUSH // and Z_FINISH, but the current implementation actually flushes as much output // as possible anyway. // // inflate() should normally be called until it returns Z_STREAM_END or an // error. However if all decompression is to be performed in a single step // (a single call of inflate), the parameter flush should be set to // Z_FINISH. In this case all pending input is processed and all pending // output is flushed; avail_out must be large enough to hold all the // uncompressed data. (The size of the uncompressed data may have been saved // by the compressor for this purpose.) The next operation on this stream must // be inflateEnd to deallocate the decompression state. The use of Z_FINISH // is never required, but can be used to inform inflate that a faster routine // may be used for the single inflate() call. // // If a preset dictionary is needed at this point (see inflateSetDictionary // below), inflate sets strm-adler to the adler32 checksum of the // dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise // it sets strm->adler to the adler32 checksum of all output produced // so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or // an error code as described below. At the end of the stream, inflate() // checks that its computed adler32 checksum is equal to that saved by the // compressor and returns Z_STREAM_END only if the checksum is correct. // // inflate() returns Z_OK if some progress has been made (more input processed // or more output produced), Z_STREAM_END if the end of the compressed data has // been reached and all uncompressed output has been produced, Z_NEED_DICT if a // preset dictionary is needed at this point, Z_DATA_ERROR if the input data was // corrupted (input stream not conforming to the zlib format or incorrect // adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent // (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not // enough memory, Z_BUF_ERROR if no progress is possible or if there was not // enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR // case, the application may then call inflateSync to look for a good // compression block. // int inflateEnd (z_streamp strm); // // All dynamically allocated data structures for this stream are freed. // This function discards any unprocessed input and does not flush any // pending output. // // inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state // was inconsistent. In the error case, msg may be set but then points to a // static string (which must not be deallocated). // Advanced functions // The following functions are needed only in some special applications. int inflateSetDictionary (z_streamp strm, const Byte *dictionary, uInt dictLength); // // Initializes the decompression dictionary from the given uncompressed byte // sequence. This function must be called immediately after a call of inflate // if this call returned Z_NEED_DICT. The dictionary chosen by the compressor // can be determined from the Adler32 value returned by this call of // inflate. The compressor and decompressor must use exactly the same // dictionary. // // inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a // parameter is invalid (such as NULL dictionary) or the stream state is // inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the // expected one (incorrect Adler32 value). inflateSetDictionary does not // perform any decompression: this will be done by subsequent calls of // inflate(). int inflateSync (z_streamp strm); // // Skips invalid compressed data until a full flush point can be found, or until all // available input is skipped. No output is provided. // // inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR // if no more input was provided, Z_DATA_ERROR if no flush point has been found, // or Z_STREAM_ERROR if the stream structure was inconsistent. In the success // case, the application may save the current current value of total_in which // indicates where valid compressed data was found. In the error case, the // application may repeatedly call inflateSync, providing more input each time, // until success or end of the input data. int inflateReset (z_streamp strm); // This function is equivalent to inflateEnd followed by inflateInit, // but does not free and reallocate all the internal decompression state. // The stream will keep attributes that may have been set by inflateInit2. // // inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source // stream state was inconsistent (such as zalloc or state being NULL). // // checksum functions // These functions are not related to compression but are exported // anyway because they might be useful in applications using the // compression library. uLong adler32 (uLong adler, const Byte *buf, uInt len); // Update a running Adler-32 checksum with the bytes buf[0..len-1] and // return the updated checksum. If buf is NULL, this function returns // the required initial value for the checksum. // An Adler-32 checksum is almost as reliable as a CRC32 but can be computed // much faster. Usage example: // // uLong adler = adler32(0L, Z_NULL, 0); // // while (read_buffer(buffer, length) != EOF) { // adler = adler32(adler, buffer, length); // } // if (adler != original_adler) error(); uLong ucrc32 (uLong crc, const Byte *buf, uInt len); // Update a running crc with the bytes buf[0..len-1] and return the updated // crc. If buf is NULL, this function returns the required initial value // for the crc. Pre- and post-conditioning (one's complement) is performed // within this function so it shouldn't be done by the application. // Usage example: // // uLong crc = crc32(0L, Z_NULL, 0); // // while (read_buffer(buffer, length) != EOF) { // crc = crc32(crc, buffer, length); // } // if (crc != original_crc) error(); const char *zError (int err); int inflateSyncPoint (z_streamp z); const uLong *get_crc_table (void); typedef unsigned char uch; typedef uch uchf; typedef unsigned short ush; typedef ush ushf; typedef unsigned long ulg; const char * const z_errmsg[10] = { // indexed by 2-zlib_error "need dictionary", // Z_NEED_DICT 2 "stream end", // Z_STREAM_END 1 "", // Z_OK 0 "file error", // Z_ERRNO (-1) "stream error", // Z_STREAM_ERROR (-2) "data error", // Z_DATA_ERROR (-3) "insufficient memory", // Z_MEM_ERROR (-4) "buffer error", // Z_BUF_ERROR (-5) "incompatible version",// Z_VERSION_ERROR (-6) ""}; #define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] #define ERR_RETURN(strm,err) \ return (strm->msg = (char*)ERR_MSG(err), (err)) // To be used only when the state is known to be valid // common constants #define STORED_BLOCK 0 #define STATIC_TREES 1 #define DYN_TREES 2 // The three kinds of block type #define MIN_MATCH 3 #define MAX_MATCH 258 // The minimum and maximum match lengths #define PRESET_DICT 0x20 // preset dictionary flag in zlib header // target dependencies #define OS_CODE 0x0b // Window 95 & Windows NT // functions #define zmemzero(dest, len) memset(dest, 0, len) // Diagnostic functions #define LuAssert(cond,msg) #define LuTrace(x) #define LuTracev(x) #define LuTracevv(x) #define LuTracec(c,x) #define LuTracecv(c,x) typedef uLong (*check_func) (uLong check, const Byte *buf, uInt len); voidpf zcalloc (voidpf opaque, unsigned items, unsigned size); void zcfree (voidpf opaque, voidpf ptr); #define ZALLOC(strm, items, size) \ (*((strm)->zalloc))((strm)->opaque, (items), (size)) #define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) //void ZFREE(z_streamp strm,voidpf addr) //{ *((strm)->zfree))((strm)->opaque, addr); //} #define TRY_FREE(s, p) {if (p) ZFREE(s, p);} // Huffman code lookup table entry--this entry is four bytes for machines // that have 16-bit pointers (e.g. PC's in the small or medium model). typedef struct inflate_huft_s inflate_huft; struct inflate_huft_s { union { struct { Byte Exop; // number of extra bits or operation Byte Bits; // number of bits in this code or subcode } what; uInt pad; // pad structure to a power of 2 (4 bytes for } word; // 16-bit, 8 bytes for 32-bit int's) uInt base; // literal, length base, distance base, or table offset }; // Maximum size of dynamic tree. The maximum found in a long but non- // exhaustive search was 1004 huft structures (850 for length/literals // and 154 for distances, the latter actually the result of an // exhaustive search). The actual maximum is not known, but the // value below is more than safe. #define MANY 1440 int inflate_trees_bits ( uInt *, // 19 code lengths uInt *, // bits tree desired/actual depth inflate_huft * *, // bits tree result inflate_huft *, // space for trees z_streamp); // for messages int inflate_trees_dynamic ( uInt, // number of literal/length codes uInt, // number of distance codes uInt *, // that many (total) code lengths uInt *, // literal desired/actual bit depth uInt *, // distance desired/actual bit depth inflate_huft * *, // literal/length tree result inflate_huft * *, // distance tree result inflate_huft *, // space for trees z_streamp); // for messages int inflate_trees_fixed ( uInt *, // literal desired/actual bit depth uInt *, // distance desired/actual bit depth const inflate_huft * *, // literal/length tree result const inflate_huft * *, // distance tree result z_streamp); // for memory allocation struct inflate_blocks_state; typedef struct inflate_blocks_state inflate_blocks_statef; inflate_blocks_statef * inflate_blocks_new ( z_streamp z, check_func c, // check function uInt w); // window size int inflate_blocks ( inflate_blocks_statef *, z_streamp , int); // initial return code void inflate_blocks_reset ( inflate_blocks_statef *, z_streamp , uLong *); // check value on output int inflate_blocks_free ( inflate_blocks_statef *, z_streamp); void inflate_set_dictionary ( inflate_blocks_statef *s, const Byte *d, // dictionary uInt n); // dictionary length int inflate_blocks_sync_point ( inflate_blocks_statef *s); struct inflate_codes_state; typedef struct inflate_codes_state inflate_codes_statef; inflate_codes_statef *inflate_codes_new ( uInt, uInt, const inflate_huft *, const inflate_huft *, z_streamp ); int inflate_codes ( inflate_blocks_statef *, z_streamp , int); void inflate_codes_free ( inflate_codes_statef *, z_streamp ); typedef enum { IBM_TYPE, // get type bits (3, including end bit) IBM_LENS, // get lengths for stored IBM_STORED, // processing stored block IBM_TABLE, // get table lengths IBM_BTREE, // get bit lengths tree for a dynamic block IBM_DTREE, // get length, distance trees for a dynamic block IBM_CODES, // processing fixed or dynamic block IBM_DRY, // output remaining window bytes IBM_DONE, // finished last block, done IBM_BAD} // got a data error--stuck here inflate_block_mode; // inflate blocks semi-private state struct inflate_blocks_state { // mode inflate_block_mode mode; // current inflate_block mode // mode dependent information union { uInt left; // if STORED, bytes left to copy struct { uInt table; // table lengths (14 bits) uInt index; // index into blens (or border) uInt *blens; // bit lengths of codes uInt bb; // bit length tree depth inflate_huft *tb; // bit length decoding tree } trees; // if DTREE, decoding info for trees struct { inflate_codes_statef *codes; } decode; // if CODES, current state } sub; // submode uInt last; // true if this block is the last block // mode independent information uInt bitk; // bits in bit buffer uLong bitb; // bit buffer inflate_huft *hufts; // single malloc for tree space Byte *window; // sliding window Byte *end; // one byte after sliding window Byte *read; // window read pointer Byte *write; // window write pointer check_func checkfn; // check function uLong check; // check on output }; // defines for inflate input/output // update pointers and return #define UPDBITS {s->bitb=b;s->bitk=k;} #define UPDIN {z->avail_in=n;z->total_in+=(uLong)(p-z->next_in);z->next_in=p;} #define UPDOUT {s->write=q;} #define UPDATE {UPDBITS UPDIN UPDOUT} #define LEAVE {UPDATE return inflate_flush(s,z,r);} // get bytes and bits #define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} #define NEEDBYTE {if(n)r=Z_OK;else LEAVE} #define NEXTBYTE (n--,*p++) #define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} // output bytes #define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) #define LOADOUT {q=s->write;m=(uInt)WAVAIL;m;} #define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} #define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} #define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} #define OUTBYTE(a) {*q++=(Byte)(a);m--;} // load local pointers #define LOAD {LOADIN LOADOUT} // masks for lower bits (size given to avoid silly warnings with Visual C++) // And'ing with mask[n] masks the lower n bits const uInt inflate_mask[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; // copy as much as possible from the sliding window to the output area int inflate_flush (inflate_blocks_statef *, z_streamp, int); int inflate_fast (uInt, uInt, const inflate_huft *, const inflate_huft *, inflate_blocks_statef *, z_streamp ); const uInt fixed_bl = 9; const uInt fixed_bd = 5; const inflate_huft fixed_tl[] = { {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} }; const inflate_huft fixed_td[] = { {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} }; // copy as much as possible from the sliding window to the output area int inflate_flush(inflate_blocks_statef *s,z_streamp z,int r) { uInt n; Byte *p; Byte *q; // local copies of source and destination pointers p = z->next_out; q = s->read; // compute number of bytes to copy as far as end of window n = (uInt)((q <= s->write ? s->write : s->end) - q); if (n > z->avail_out) n = z->avail_out; if (n && r == Z_BUF_ERROR) r = Z_OK; // update counters z->avail_out -= n; z->total_out += n; // update check information if (s->checkfn != Z_NULL) z->adler = s->check = (*s->checkfn)(s->check, q, n); // copy as far as end of window if (n!=0) // check for n!=0 to avoid waking up CodeGuard { memcpy(p, q, n); p += n; q += n; } // see if more to copy at beginning of window if (q == s->end) { // wrap pointers q = s->window; if (s->write == s->end) s->write = s->window; // compute bytes to copy n = (uInt)(s->write - q); if (n > z->avail_out) n = z->avail_out; if (n && r == Z_BUF_ERROR) r = Z_OK; // update counters z->avail_out -= n; z->total_out += n; // update check information if (s->checkfn != Z_NULL) z->adler = s->check = (*s->checkfn)(s->check, q, n); // copy if (n!=0) {memcpy(p,q,n); p+=n; q+=n;} } // update pointers z->next_out = p; s->read = q; // done return r; } // simplify the use of the inflate_huft type with some defines #define exop word.what.Exop #define bits word.what.Bits typedef enum { // waiting for "i:"=input, "o:"=output, "x:"=nothing START, // x: set up for LEN LEN, // i: get length/literal/eob next LENEXT, // i: getting length extra (have base) DIST, // i: get distance next DISTEXT, // i: getting distance extra COPY, // o: copying bytes in window, waiting for space LIT, // o: got literal, waiting for output space WASH, // o: got eob, possibly still output waiting END, // x: got eob and all data flushed BADCODE} // x: got error inflate_codes_mode; // inflate codes private state struct inflate_codes_state { // mode inflate_codes_mode mode; // current inflate_codes mode // mode dependent information uInt len; union { struct { const inflate_huft *tree; // pointer into tree uInt need; // bits needed } code; // if LEN or DIST, where in tree uInt lit; // if LIT, literal struct { uInt get; // bits to get for extra uInt dist; // distance back to copy from } copy; // if EXT or COPY, where and how much } sub; // submode // mode independent information Byte lbits; // ltree bits decoded per branch Byte dbits; // dtree bits decoder per branch const inflate_huft *ltree; // literal/length/eob tree const inflate_huft *dtree; // distance tree }; inflate_codes_statef *inflate_codes_new( uInt bl, uInt bd, const inflate_huft *tl, const inflate_huft *td, // need separate declaration for Borland C++ z_streamp z) { inflate_codes_statef *c; if ((c = (inflate_codes_statef *) ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) { c->mode = START; c->lbits = (Byte)bl; c->dbits = (Byte)bd; c->ltree = tl; c->dtree = td; LuTracev((stderr, "inflate: codes new\n")); } return c; } int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) { uInt j; // temporary storage const inflate_huft *t; // temporary pointer uInt e; // extra bits or operation uLong b; // bit buffer uInt k; // bits in bit buffer Byte *p; // input data pointer uInt n; // bytes available there Byte *q; // output window write pointer uInt m; // bytes to end of window or read pointer Byte *f; // pointer to copy strings from inflate_codes_statef *c = s->sub.decode.codes; // codes state // copy input/output information to locals (UPDATE macro restores) LOAD // process input and output based on current state for(;;) switch (c->mode) { // waiting for "i:"=input, "o:"=output, "x:"=nothing case START: // x: set up for LEN #ifndef SLOW if (m >= 258 && n >= 10) { UPDATE r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); LOAD if (r != Z_OK) { c->mode = r == Z_STREAM_END ? WASH : BADCODE; break; } } #endif // !SLOW c->sub.code.need = c->lbits; c->sub.code.tree = c->ltree; c->mode = LEN; case LEN: // i: get length/literal/eob next j = c->sub.code.need; NEEDBITS(j) t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); DUMPBITS(t->bits) e = (uInt)(t->exop); if (e == 0) // literal { c->sub.lit = t->base; LuTracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", t->base)); c->mode = LIT; break; } if (e & 16) // length { c->sub.copy.get = e & 15; c->len = t->base; c->mode = LENEXT; break; } if ((e & 64) == 0) // next table { c->sub.code.need = e; c->sub.code.tree = t + t->base; break; } if (e & 32) // end of block { LuTracevv((stderr, "inflate: end of block\n")); c->mode = WASH; break; } c->mode = BADCODE; // invalid code z->msg = (char*)"invalid literal/length code"; r = Z_DATA_ERROR; LEAVE case LENEXT: // i: getting length extra (have base) j = c->sub.copy.get; NEEDBITS(j) c->len += (uInt)b & inflate_mask[j]; DUMPBITS(j) c->sub.code.need = c->dbits; c->sub.code.tree = c->dtree; LuTracevv((stderr, "inflate: length %u\n", c->len)); c->mode = DIST; case DIST: // i: get distance next j = c->sub.code.need; NEEDBITS(j) t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); DUMPBITS(t->bits) e = (uInt)(t->exop); if (e & 16) // distance { c->sub.copy.get = e & 15; c->sub.copy.dist = t->base; c->mode = DISTEXT; break; } if ((e & 64) == 0) // next table { c->sub.code.need = e; c->sub.code.tree = t + t->base; break; } c->mode = BADCODE; // invalid code z->msg = (char*)"invalid distance code"; r = Z_DATA_ERROR; LEAVE case DISTEXT: // i: getting distance extra j = c->sub.copy.get; NEEDBITS(j) c->sub.copy.dist += (uInt)b & inflate_mask[j]; DUMPBITS(j) LuTracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); c->mode = COPY; case COPY: // o: copying bytes in window, waiting for space f = q - c->sub.copy.dist; while (f < s->window) // modulo window size-"while" instead f += s->end - s->window; // of "if" handles invalid distances while (c->len) { NEEDOUT OUTBYTE(*f++) if (f == s->end) f = s->window; c->len--; } c->mode = START; break; case LIT: // o: got literal, waiting for output space NEEDOUT OUTBYTE(c->sub.lit) c->mode = START; break; case WASH: // o: got eob, possibly more output if (k > 7) // return unused byte, if any { //Assert(k < 16, "inflate_codes grabbed too many bytes") k -= 8; n++; p--; // can always return one } FLUSH if (s->read != s->write) LEAVE c->mode = END; case END: r = Z_STREAM_END; LEAVE case BADCODE: // x: got error r = Z_DATA_ERROR; LEAVE default: r = Z_STREAM_ERROR; LEAVE } } void inflate_codes_free(inflate_codes_statef *c,z_streamp z) { ZFREE(z, c); LuTracev((stderr, "inflate: codes free\n")); } // infblock.c -- interpret and process block types to last block // Copyright (C) 1995-1998 Mark Adler // For conditions of distribution and use, see copyright notice in zlib.h //struct inflate_codes_state {int dummy;}; // for buggy compilers // Table for deflate from PKZIP's appnote.txt. const uInt border[] = { // Order of the bit length code lengths 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; // // Notes beyond the 1.93a appnote.txt: // // 1. Distance pointers never point before the beginning of the output stream. // 2. Distance pointers can point back across blocks, up to 32k away. // 3. There is an implied maximum of 7 bits for the bit length table and // 15 bits for the actual data. // 4. If only one code exists, then it is encoded using one bit. (Zero // would be more efficient, but perhaps a little confusing.) If two // codes exist, they are coded using one bit each (0 and 1). // 5. There is no way of sending zero distance codes--a dummy must be // sent if there are none. (History: a pre 2.0 version of PKZIP would // store blocks with no distance codes, but this was discovered to be // too harsh a criterion.) Valid only for 1.93a. 2.04c does allow // zero distance codes, which is sent as one code of zero bits in // length. // 6. There are up to 286 literal/length codes. Code 256 represents the // end-of-block. Note however that the static length tree defines // 288 codes just to fill out the Huffman codes. Codes 286 and 287 // cannot be used though, since there is no length base or extra bits // defined for them. Similarily, there are up to 30 distance codes. // However, static trees define 32 codes (all 5 bits) to fill out the // Huffman codes, but the last two had better not show up in the data. // 7. Unzip can check dynamic Huffman blocks for complete code sets. // The exception is that a single code would not be complete (see #4). // 8. The five bits following the block type is really the number of // literal codes sent minus 257. // 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits // (1+6+6). Therefore, to output three times the length, you output // three codes (1+1+1), whereas to output four times the same length, // you only need two codes (1+3). Hmm. //10. In the tree reconstruction algorithm, Code = Code + Increment // only if BitLength(i) is not zero. (Pretty obvious.) //11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) //12. Note: length code 284 can represent 227-258, but length code 285 // really is 258. The last length deserves its own, short code // since it gets used a lot in very redundant files. The length // 258 is special since 258 - 3 (the min match length) is 255. //13. The literal/length and distance code bit lengths are read as a // single stream of lengths. It is possible (and advantageous) for // a repeat code (16, 17, or 18) to go across the boundary between // the two sets of lengths. void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) { if (c != Z_NULL) *c = s->check; if (s->mode == IBM_BTREE || s->mode == IBM_DTREE) ZFREE(z, s->sub.trees.blens); if (s->mode == IBM_CODES) inflate_codes_free(s->sub.decode.codes, z); s->mode = IBM_TYPE; s->bitk = 0; s->bitb = 0; s->read = s->write = s->window; if (s->checkfn != Z_NULL) z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); LuTracev((stderr, "inflate: blocks reset\n")); } inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) { inflate_blocks_statef *s; if ((s = (inflate_blocks_statef *)ZALLOC (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) return s; if ((s->hufts = (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) { ZFREE(z, s); return Z_NULL; } if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) { ZFREE(z, s->hufts); ZFREE(z, s); return Z_NULL; } s->end = s->window + w; s->checkfn = c; s->mode = IBM_TYPE; LuTracev((stderr, "inflate: blocks allocated\n")); inflate_blocks_reset(s, z, Z_NULL); return s; } int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) { uInt t; // temporary storage uLong b; // bit buffer uInt k; // bits in bit buffer Byte *p; // input data pointer uInt n; // bytes available there Byte *q; // output window write pointer uInt m; // bytes to end of window or read pointer // copy input/output information to locals (UPDATE macro restores) LOAD // process input based on current state for(;;) switch (s->mode) { case IBM_TYPE: NEEDBITS(3) t = (uInt)b & 7; s->last = t & 1; switch (t >> 1) { case 0: // stored LuTracev((stderr, "inflate: stored block%s\n", s->last ? " (last)" : "")); DUMPBITS(3) t = k & 7; // go to byte boundary DUMPBITS(t) s->mode = IBM_LENS; // get length of stored block break; case 1: // fixed LuTracev((stderr, "inflate: fixed codes block%s\n", s->last ? " (last)" : "")); { uInt bl, bd; const inflate_huft *tl, *td; inflate_trees_fixed(&bl, &bd, &tl, &td, z); s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); if (s->sub.decode.codes == Z_NULL) { r = Z_MEM_ERROR; LEAVE } } DUMPBITS(3) s->mode = IBM_CODES; break; case 2: // dynamic LuTracev((stderr, "inflate: dynamic codes block%s\n", s->last ? " (last)" : "")); DUMPBITS(3) s->mode = IBM_TABLE; break; case 3: // illegal DUMPBITS(3) s->mode = IBM_BAD; z->msg = (char*)"invalid block type"; r = Z_DATA_ERROR; LEAVE } break; case IBM_LENS: NEEDBITS(32) if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) { s->mode = IBM_BAD; z->msg = (char*)"invalid stored block lengths"; r = Z_DATA_ERROR; LEAVE } s->sub.left = (uInt)b & 0xffff; b = k = 0; // dump bits LuTracev((stderr, "inflate: stored length %u\n", s->sub.left)); s->mode = s->sub.left ? IBM_STORED : (s->last ? IBM_DRY : IBM_TYPE); break; case IBM_STORED: if (n == 0) LEAVE NEEDOUT t = s->sub.left; if (t > n) t = n; if (t > m) t = m; memcpy(q, p, t); p += t; n -= t; q += t; m -= t; if ((s->sub.left -= t) != 0) break; LuTracev((stderr, "inflate: stored end, %lu total out\n", z->total_out + (q >= s->read ? q - s->read : (s->end - s->read) + (q - s->window)))); s->mode = s->last ? IBM_DRY : IBM_TYPE; break; case IBM_TABLE: NEEDBITS(14) s->sub.trees.table = t = (uInt)b & 0x3fff; // remove this section to workaround bug in pkzip if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) { s->mode = IBM_BAD; z->msg = (char*)"too many length or distance symbols"; r = Z_DATA_ERROR; LEAVE } // end remove t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) { r = Z_MEM_ERROR; LEAVE } DUMPBITS(14) s->sub.trees.index = 0; LuTracev((stderr, "inflate: table sizes ok\n")); s->mode = IBM_BTREE; case IBM_BTREE: while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) { NEEDBITS(3) s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; DUMPBITS(3) } while (s->sub.trees.index < 19) s->sub.trees.blens[border[s->sub.trees.index++]] = 0; s->sub.trees.bb = 7; t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, &s->sub.trees.tb, s->hufts, z); if (t != Z_OK) { r = t; if (r == Z_DATA_ERROR) { ZFREE(z, s->sub.trees.blens); s->mode = IBM_BAD; } LEAVE } s->sub.trees.index = 0; LuTracev((stderr, "inflate: bits tree ok\n")); s->mode = IBM_DTREE; case IBM_DTREE: while (t = s->sub.trees.table, s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) { inflate_huft *h; uInt i, j, c; t = s->sub.trees.bb; NEEDBITS(t) h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); t = h->bits; c = h->base; if (c < 16) { DUMPBITS(t) s->sub.trees.blens[s->sub.trees.index++] = c; } else // c == 16..18 { i = c == 18 ? 7 : c - 14; j = c == 18 ? 11 : 3; NEEDBITS(t + i) DUMPBITS(t) j += (uInt)b & inflate_mask[i]; DUMPBITS(i) i = s->sub.trees.index; t = s->sub.trees.table; if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || (c == 16 && i < 1)) { ZFREE(z, s->sub.trees.blens); s->mode = IBM_BAD; z->msg = (char*)"invalid bit length repeat"; r = Z_DATA_ERROR; LEAVE } c = c == 16 ? s->sub.trees.blens[i - 1] : 0; do { s->sub.trees.blens[i++] = c; } while (--j); s->sub.trees.index = i; } } s->sub.trees.tb = Z_NULL; { uInt bl, bd; inflate_huft *tl, *td; inflate_codes_statef *c; bl = 9; // must be <= 9 for lookahead assumptions bd = 6; // must be <= 9 for lookahead assumptions t = s->sub.trees.table; t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), s->sub.trees.blens, &bl, &bd, &tl, &td, s->hufts, z); if (t != Z_OK) { if (t == (uInt)Z_DATA_ERROR) { ZFREE(z, s->sub.trees.blens); s->mode = IBM_BAD; } r = t; LEAVE } LuTracev((stderr, "inflate: trees ok\n")); if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) { r = Z_MEM_ERROR; LEAVE } s->sub.decode.codes = c; } ZFREE(z, s->sub.trees.blens); s->mode = IBM_CODES; case IBM_CODES: UPDATE if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) return inflate_flush(s, z, r); r = Z_OK; inflate_codes_free(s->sub.decode.codes, z); LOAD LuTracev((stderr, "inflate: codes end, %lu total out\n", z->total_out + (q >= s->read ? q - s->read : (s->end - s->read) + (q - s->window)))); if (!s->last) { s->mode = IBM_TYPE; break; } s->mode = IBM_DRY; case IBM_DRY: FLUSH if (s->read != s->write) LEAVE s->mode = IBM_DONE; case IBM_DONE: r = Z_STREAM_END; LEAVE case IBM_BAD: r = Z_DATA_ERROR; LEAVE default: r = Z_STREAM_ERROR; LEAVE } } int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) { inflate_blocks_reset(s, z, Z_NULL); ZFREE(z, s->window); ZFREE(z, s->hufts); ZFREE(z, s); LuTracev((stderr, "inflate: blocks freed\n")); return Z_OK; } // inftrees.c -- generate Huffman trees for efficient decoding // Copyright (C) 1995-1998 Mark Adler // For conditions of distribution and use, see copyright notice in zlib.h // extern const char inflate_copyright[] = " inflate 1.1.3 Copyright 1995-1998 Mark Adler "; // If you use the zlib library in a product, an acknowledgment is welcome // in the documentation of your product. If for some reason you cannot // include such an acknowledgment, I would appreciate that you keep this // copyright string in the executable of your product. int huft_build ( uInt *, // code lengths in bits uInt, // number of codes uInt, // number of "simple" codes const uInt *, // list of base values for non-simple codes const uInt *, // list of extra bits for non-simple codes inflate_huft **,// result: starting table uInt *, // maximum lookup bits (returns actual) inflate_huft *, // space for trees uInt *, // hufts used in space uInt * ); // space for values // Tables for deflate from PKZIP's appnote.txt. const uInt cplens[31] = { // Copy lengths for literal codes 257..285 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; // see note #13 above about 258 const uInt cplext[31] = { // Extra bits for literal codes 257..285 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; // 112==invalid const uInt cpdist[30] = { // Copy offsets for distance codes 0..29 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; const uInt cpdext[30] = { // Extra bits for distance codes 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; // // Huffman code decoding is performed using a multi-level table lookup. // The fastest way to decode is to simply build a lookup table whose // size is determined by the longest code. However, the time it takes // to build this table can also be a factor if the data being decoded // is not very long. The most common codes are necessarily the // shortest codes, so those codes dominate the decoding time, and hence // the speed. The idea is you can have a shorter table that decodes the // shorter, more probable codes, and then point to subsidiary tables for // the longer codes. The time it costs to decode the longer codes is // then traded against the time it takes to make longer tables. // // This results of this trade are in the variables lbits and dbits // below. lbits is the number of bits the first level table for literal/ // length codes can decode in one step, and dbits is the same thing for // the distance codes. Subsequent tables are also less than or equal to // those sizes. These values may be adjusted either when all of the // codes are shorter than that, in which case the longest code length in // bits is used, or when the shortest code is *longer* than the requested // table size, in which case the length of the shortest code in bits is // used. // // There are two different values for the two tables, since they code a // different number of possibilities each. The literal/length table // codes 286 possible values, or in a flat code, a little over eight // bits. The distance table codes 30 possible values, or a little less // than five bits, flat. The optimum values for speed end up being // about one bit more than those, so lbits is 8+1 and dbits is 5+1. // The optimum values may differ though from machine to machine, and // possibly even between compilers. Your mileage may vary. // // If BMAX needs to be larger than 16, then h and x[] should be uLong. #define BMAX 15 // maximum bit length of any code int huft_build( uInt *b, // code lengths in bits (all assumed <= BMAX) uInt n, // number of codes (assumed <= 288) uInt s, // number of simple-valued codes (0..s-1) const uInt *d, // list of base values for non-simple codes const uInt *e, // list of extra bits for non-simple codes inflate_huft * *t, // result: starting table uInt *m, // maximum lookup bits, returns actual inflate_huft *hp, // space for trees uInt *hn, // hufts used in space uInt *v) // working area: values in order of bit length // Given a list of code lengths and a maximum table size, make a set of // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR // if the given code set is incomplete (the tables are still built in this // case), or Z_DATA_ERROR if the input is invalid. { uInt a; // counter for codes of length k uInt c[BMAX+1]; // bit length count table uInt f; // i repeats in table every f entries int g; // maximum code length int h; // table level register uInt i; // counter, current code register uInt j; // counter register int k; // number of bits in current code int l; // bits per table (returned in m) uInt mask; // (1 << w) - 1, to avoid cc -O bug on HP register uInt *p; // pointer into c[], b[], or v[] inflate_huft *q; // points to current table struct inflate_huft_s r; // table entry for structure assignment inflate_huft *u[BMAX]; // table stack register int w; // bits before this table == (l * h) uInt x[BMAX+1]; // bit offsets, then code stack uInt *xp; // pointer into x int y; // number of dummy codes added uInt z; // number of entries in current table // Generate counts for each bit length p = c; #define C0 *p++ = 0; #define C2 C0 C0 C0 C0 #define C4 C2 C2 C2 C2 C4; p; // clear c[]--assume BMAX+1 is 16 p = b; i = n; do { c[*p++]++; // assume all entries <= BMAX } while (--i); if (c[0] == n) // null input--all zero length codes { *t = (inflate_huft *)Z_NULL; *m = 0; return Z_OK; } // Find minimum and maximum length, bound *m by those l = *m; for (j = 1; j <= BMAX; j++) if (c[j]) break; k = j; // minimum code length if ((uInt)l < j) l = j; for (i = BMAX; i; i--) if (c[i]) break; g = i; // maximum code length if ((uInt)l > i) l = i; *m = l; // Adjust last length count to fill out codes, if needed for (y = 1 << j; j < i; j++, y <<= 1) if ((y -= c[j]) < 0) return Z_DATA_ERROR; if ((y -= c[i]) < 0) return Z_DATA_ERROR; c[i] += y; // Generate starting offsets into the value table for each length x[1] = j = 0; p = c + 1; xp = x + 2; while (--i) { // note that i == g from above *xp++ = (j += *p++); } // Make a table of values in order of bit lengths p = b; i = 0; do { if ((j = *p++) != 0) v[x[j]++] = i; } while (++i < n); n = x[g]; // set n to length of v // Generate the Huffman codes and for each, make the table entries x[0] = i = 0; // first Huffman code is zero p = v; // grab values in bit order h = -1; // no tables yet--level -1 w = -l; // bits decoded == (l * h) u[0] = (inflate_huft *)Z_NULL; // just to keep compilers happy q = (inflate_huft *)Z_NULL; // ditto z = 0; // ditto // go through the bit lengths (k already is bits in shortest code) for (; k <= g; k++) { a = c[k]; while (a--) { // here i is the Huffman code of length k bits for value *p // make tables up to required level while (k > w + l) { h++; w += l; // previous table always l bits // compute minimum size table less than or equal to l bits z = g - w; z = z > (uInt)l ? l : z; // table size upper limit if ((f = 1 << (j = k - w)) > a + 1) // try a k-w bit table { // too few codes for k-w bit table f -= a + 1; // deduct codes from patterns left xp = c + k; if (j < z) while (++j < z) // try smaller tables up to z bits { if ((f <<= 1) <= *++xp) break; // enough codes to use up j bits f -= *xp; // else deduct codes from patterns } } z = 1 << j; // table entries for j-bit table // allocate new table if (*hn + z > MANY) // (note: doesn't matter for fixed) return Z_DATA_ERROR; // overflow of MANY u[h] = q = hp + *hn; *hn += z; // connect to last table, if there is one if (h) { x[h] = i; // save pattern for backing up r.bits = (Byte)l; // bits to dump before this table r.exop = (Byte)j; // bits in this table j = i >> (w - l); r.base = (uInt)(q - u[h-1] - j); // offset to this table u[h-1][j] = r; // connect to last table } else *t = q; // first table is returned result } // set up table entry in r r.bits = (Byte)(k - w); if (p >= v + n) r.exop = 128 + 64; // out of values--invalid code else if (*p < s) { r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); // 256 is end-of-block r.base = *p++; // simple code is just the value } else { r.exop = (Byte)(e[*p - s] + 16 + 64);// non-simple--look up in lists r.base = d[*p++ - s]; } // fill code-like entries with r f = 1 << (k - w); for (j = i >> w; j < z; j += f) q[j] = r; // backwards increment the k-bit code i for (j = 1 << (k - 1); i & j; j >>= 1) i ^= j; i ^= j; // backup over finished tables mask = (1 << w) - 1; // needed on HP, cc -O bug while ((i & mask) != x[h]) { h--; // don't need to update q w -= l; mask = (1 << w) - 1; } } } // Return Z_BUF_ERROR if we were given an incomplete table return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; } int inflate_trees_bits( uInt *c, // 19 code lengths uInt *bb, // bits tree desired/actual depth inflate_huft * *tb, // bits tree result inflate_huft *hp, // space for trees z_streamp z) // for messages { int r; uInt hn = 0; // hufts used in space uInt *v; // work area for huft_build if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) return Z_MEM_ERROR; r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, tb, bb, hp, &hn, v); if (r == Z_DATA_ERROR) z->msg = (char*)"oversubscribed dynamic bit lengths tree"; else if (r == Z_BUF_ERROR || *bb == 0) { z->msg = (char*)"incomplete dynamic bit lengths tree"; r = Z_DATA_ERROR; } ZFREE(z, v); return r; } int inflate_trees_dynamic( uInt nl, // number of literal/length codes uInt nd, // number of distance codes uInt *c, // that many (total) code lengths uInt *bl, // literal desired/actual bit depth uInt *bd, // distance desired/actual bit depth inflate_huft * *tl, // literal/length tree result inflate_huft * *td, // distance tree result inflate_huft *hp, // space for trees z_streamp z) // for messages { int r; uInt hn = 0; // hufts used in space uInt *v; // work area for huft_build // allocate work area if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) return Z_MEM_ERROR; // build literal/length tree r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); if (r != Z_OK || *bl == 0) { if (r == Z_DATA_ERROR) z->msg = (char*)"oversubscribed literal/length tree"; else if (r != Z_MEM_ERROR) { z->msg = (char*)"incomplete literal/length tree"; r = Z_DATA_ERROR; } ZFREE(z, v); return r; } // build distance tree r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); if (r != Z_OK || (*bd == 0 && nl > 257)) { if (r == Z_DATA_ERROR) z->msg = (char*)"oversubscribed distance tree"; else if (r == Z_BUF_ERROR) { z->msg = (char*)"incomplete distance tree"; r = Z_DATA_ERROR; } else if (r != Z_MEM_ERROR) { z->msg = (char*)"empty distance tree with lengths"; r = Z_DATA_ERROR; } ZFREE(z, v); return r; } // done ZFREE(z, v); return Z_OK; } int inflate_trees_fixed( uInt *bl, // literal desired/actual bit depth uInt *bd, // distance desired/actual bit depth const inflate_huft * * tl, // literal/length tree result const inflate_huft * *td, // distance tree result z_streamp ) // for memory allocation { *bl = fixed_bl; *bd = fixed_bd; *tl = fixed_tl; *td = fixed_td; return Z_OK; } // inffast.c -- process literals and length/distance pairs fast // Copyright (C) 1995-1998 Mark Adler // For conditions of distribution and use, see copyright notice in zlib.h // //struct inflate_codes_state {int dummy;}; // for buggy compilers // macros for bit input with no checking and for returning unused bytes #define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} // Called with number of bytes left to write in window at least 258 // (the maximum string length) and number of input bytes available // at least ten. The ten bytes are six bytes for the longest length/ // distance pair plus four bytes for overloading the bit buffer. int inflate_fast( uInt bl, uInt bd, const inflate_huft *tl, const inflate_huft *td, // need separate declaration for Borland C++ inflate_blocks_statef *s, z_streamp z) { const inflate_huft *t; // temporary pointer uInt e; // extra bits or operation uLong b; // bit buffer uInt k; // bits in bit buffer Byte *p; // input data pointer uInt n; // bytes available there Byte *q; // output window write pointer uInt m; // bytes to end of window or read pointer uInt ml; // mask for literal/length tree uInt md; // mask for distance tree uInt c; // bytes to copy uInt d; // distance back to copy from Byte *r; // copy source pointer // load input, output, bit values LOAD // initialize masks ml = inflate_mask[bl]; md = inflate_mask[bd]; // do until not enough input or output space for fast loop do { // assume called with m >= 258 && n >= 10 // get literal/length code GRABBITS(20) // max bits for literal/length code if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) { DUMPBITS(t->bits) LuTracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? "inflate: * literal '%c'\n" : "inflate: * literal 0x%02x\n", t->base)); *q++ = (Byte)t->base; m--; continue; } for (;;) { DUMPBITS(t->bits) if (e & 16) { // get extra bits for length e &= 15; c = t->base + ((uInt)b & inflate_mask[e]); DUMPBITS(e) LuTracevv((stderr, "inflate: * length %u\n", c)); // decode distance base of block to copy GRABBITS(15); // max bits for distance code e = (t = td + ((uInt)b & md))->exop; for (;;) { DUMPBITS(t->bits) if (e & 16) { // get extra bits to add to distance base e &= 15; GRABBITS(e) // get extra bits (up to 13) d = t->base + ((uInt)b & inflate_mask[e]); DUMPBITS(e) LuTracevv((stderr, "inflate: * distance %u\n", d)); // do the copy m -= c; r = q - d; if (r < s->window) // wrap if needed { do { r += s->end - s->window; // force pointer in window } while (r < s->window); // covers invalid distances e = (uInt) (s->end - r); if (c > e) { c -= e; // wrapped copy do { *q++ = *r++; } while (--e); r = s->window; do { *q++ = *r++; } while (--c); } else // normal copy { *q++ = *r++; c--; *q++ = *r++; c--; do { *q++ = *r++; } while (--c); } } else /* normal copy */ { *q++ = *r++; c--; *q++ = *r++; c--; do { *q++ = *r++; } while (--c); } break; } else if ((e & 64) == 0) { t += t->base; e = (t += ((uInt)b & inflate_mask[e]))->exop; } else { z->msg = (char*)"invalid distance code"; UNGRAB UPDATE return Z_DATA_ERROR; } }; break; } if ((e & 64) == 0) { t += t->base; if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) { DUMPBITS(t->bits) LuTracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? "inflate: * literal '%c'\n" : "inflate: * literal 0x%02x\n", t->base)); *q++ = (Byte)t->base; m--; break; } } else if (e & 32) { LuTracevv((stderr, "inflate: * end of block\n")); UNGRAB UPDATE return Z_STREAM_END; } else { z->msg = (char*)"invalid literal/length code"; UNGRAB UPDATE return Z_DATA_ERROR; } }; } while (m >= 258 && n >= 10); // not enough input or output--restore pointers and return UNGRAB UPDATE return Z_OK; } // crc32.c -- compute the CRC-32 of a data stream // Copyright (C) 1995-1998 Mark Adler // For conditions of distribution and use, see copyright notice in zlib.h // @(#) $Id$ // Table of CRC-32's of all single-byte values (made by make_crc_table) const uLong crc_table[256] = { 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL }; const uLong * get_crc_table() { return (const uLong *)crc_table; } #define CRC_DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); #define CRC_DO2(buf) CRC_DO1(buf); CRC_DO1(buf); #define CRC_DO4(buf) CRC_DO2(buf); CRC_DO2(buf); #define CRC_DO8(buf) CRC_DO4(buf); CRC_DO4(buf); uLong ucrc32(uLong crc, const Byte *buf, uInt len) { if (buf == Z_NULL) return 0L; crc = crc ^ 0xffffffffL; while (len >= 8) {CRC_DO8(buf); len -= 8;} if (len) do {CRC_DO1(buf);} while (--len); return crc ^ 0xffffffffL; } // ============================================================= // some decryption routines #define CRC32(c, b) (crc_table[((int)(c)^(b))&0xff]^((c)>>8)) void Uupdate_keys(unsigned long *keys, char c) { keys[0] = CRC32(keys[0],c); keys[1] += keys[0] & 0xFF; keys[1] = keys[1]*134775813L +1; keys[2] = CRC32(keys[2], keys[1] >> 24); } char Udecrypt_byte(unsigned long *keys) { unsigned temp = ((unsigned)keys[2] & 0xffff) | 2; return (char)(((temp * (temp ^ 1)) >> 8) & 0xff); } char zdecode(unsigned long *keys, char c) { c^=Udecrypt_byte(keys); Uupdate_keys(keys,c); return c; } // adler32.c -- compute the Adler-32 checksum of a data stream // Copyright (C) 1995-1998 Mark Adler // For conditions of distribution and use, see copyright notice in zlib.h // @(#) $Id$ #define BASE 65521L // largest prime smaller than 65536 #define NMAX 5552 // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 #define AD_DO1(buf,i) {s1 += buf[i]; s2 += s1;} #define AD_DO2(buf,i) AD_DO1(buf,i); AD_DO1(buf,i+1); #define AD_DO4(buf,i) AD_DO2(buf,i); AD_DO2(buf,i+2); #define AD_DO8(buf,i) AD_DO4(buf,i); AD_DO4(buf,i+4); #define AD_DO16(buf) AD_DO8(buf,0); AD_DO8(buf,8); // ========================================================================= uLong adler32(uLong adler, const Byte *buf, uInt len) { unsigned long s1 = adler & 0xffff; unsigned long s2 = (adler >> 16) & 0xffff; int k; if (buf == Z_NULL) return 1L; while (len > 0) { k = len < NMAX ? len : NMAX; len -= k; while (k >= 16) { AD_DO16(buf); buf += 16; k -= 16; } if (k != 0) do { s1 += *buf++; s2 += s1; } while (--k); s1 %= BASE; s2 %= BASE; } return (s2 << 16) | s1; } // zutil.c -- target dependent utility functions for the compression library // Copyright (C) 1995-1998 Jean-loup Gailly. // For conditions of distribution and use, see copyright notice in zlib.h // @(#) $Id$ const char * zlibVersion() { return ZLIB_VERSION; } // exported to allow conversion of error code to string for compress() and // uncompress() const char * zError(int err) { return ERR_MSG(err); } voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) { if (opaque) items += size - size; // make compiler happy return (voidpf)calloc(items, size); } void zcfree (voidpf opaque, voidpf ptr) { zfree(ptr); if (opaque) return; // make compiler happy } // inflate.c -- zlib interface to inflate modules // Copyright (C) 1995-1998 Mark Adler // For conditions of distribution and use, see copyright notice in zlib.h //struct inflate_blocks_state {int dummy;}; // for buggy compilers typedef enum { IM_METHOD, // waiting for method byte IM_FLAG, // waiting for flag byte IM_DICT4, // four dictionary check bytes to go IM_DICT3, // three dictionary check bytes to go IM_DICT2, // two dictionary check bytes to go IM_DICT1, // one dictionary check byte to go IM_DICT0, // waiting for inflateSetDictionary IM_BLOCKS, // decompressing blocks IM_CHECK4, // four check bytes to go IM_CHECK3, // three check bytes to go IM_CHECK2, // two check bytes to go IM_CHECK1, // one check byte to go IM_DONE, // finished check, done IM_BAD} // got an error--stay here inflate_mode; // inflate private state struct internal_state { // mode inflate_mode mode; // current inflate mode // mode dependent information union { uInt method; // if IM_FLAGS, method byte struct { uLong was; // computed check value uLong need; // stream check value } check; // if CHECK, check values to compare uInt marker; // if IM_BAD, inflateSync's marker bytes count } sub; // submode // mode independent information int nowrap; // flag for no wrapper uInt wbits; // log2(window size) (8..15, defaults to 15) inflate_blocks_statef *blocks; // current inflate_blocks state }; int inflateReset(z_streamp z) { if (z == Z_NULL || z->state == Z_NULL) return Z_STREAM_ERROR; z->total_in = z->total_out = 0; z->msg = Z_NULL; z->state->mode = z->state->nowrap ? IM_BLOCKS : IM_METHOD; inflate_blocks_reset(z->state->blocks, z, Z_NULL); LuTracev((stderr, "inflate: reset\n")); return Z_OK; } int inflateEnd(z_streamp z) { if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) return Z_STREAM_ERROR; if (z->state->blocks != Z_NULL) inflate_blocks_free(z->state->blocks, z); ZFREE(z, z->state); z->state = Z_NULL; LuTracev((stderr, "inflate: end\n")); return Z_OK; } int inflateInit2(z_streamp z) { const char *version = ZLIB_VERSION; int stream_size = sizeof(z_stream); if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != sizeof(z_stream)) return Z_VERSION_ERROR; int w = -15; // MAX_WBITS: 32K LZ77 window. // Warning: reducing MAX_WBITS makes minigzip unable to extract .gz files created by gzip. // The memory requirements for deflate are (in bytes): // (1 << (windowBits+2)) + (1 << (memLevel+9)) // that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) // plus a few kilobytes for small objects. For example, if you want to reduce // the default memory requirements from 256K to 128K, compile with // make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" // Of course this will generally degrade compression (there's no free lunch). // // The memory requirements for inflate are (in bytes) 1 << windowBits // that is, 32K for windowBits=15 (default value) plus a few kilobytes // for small objects. // initialize state if (z == Z_NULL) return Z_STREAM_ERROR; z->msg = Z_NULL; if (z->zalloc == Z_NULL) { z->zalloc = zcalloc; z->opaque = (voidpf)0; } if (z->zfree == Z_NULL) z->zfree = zcfree; if ((z->state = (struct internal_state *) ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) return Z_MEM_ERROR; z->state->blocks = Z_NULL; // handle undocumented nowrap option (no zlib header or check) z->state->nowrap = 0; if (w < 0) { w = - w; z->state->nowrap = 1; } // set window size if (w < 8 || w > 15) { inflateEnd(z); return Z_STREAM_ERROR; } z->state->wbits = (uInt)w; // create inflate_blocks state if ((z->state->blocks = inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) == Z_NULL) { inflateEnd(z); return Z_MEM_ERROR; } LuTracev((stderr, "inflate: allocated\n")); // reset state inflateReset(z); return Z_OK; } #define IM_NEEDBYTE {if(z->avail_in==0)return r;r=f;} #define IM_NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) int inflate(z_streamp z, int f) { int r; uInt b; if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) return Z_STREAM_ERROR; f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; r = Z_BUF_ERROR; for (;;) switch (z->state->mode) { case IM_METHOD: IM_NEEDBYTE if (((z->state->sub.method = IM_NEXTBYTE) & 0xf) != Z_DEFLATED) { z->state->mode = IM_BAD; z->msg = (char*)"unknown compression method"; z->state->sub.marker = 5; // can't try inflateSync break; } if ((z->state->sub.method >> 4) + 8 > z->state->wbits) { z->state->mode = IM_BAD; z->msg = (char*)"invalid window size"; z->state->sub.marker = 5; // can't try inflateSync break; } z->state->mode = IM_FLAG; case IM_FLAG: IM_NEEDBYTE b = IM_NEXTBYTE; if (((z->state->sub.method << 8) + b) % 31) { z->state->mode = IM_BAD; z->msg = (char*)"incorrect header check"; z->state->sub.marker = 5; // can't try inflateSync break; } LuTracev((stderr, "inflate: zlib header ok\n")); if (!(b & PRESET_DICT)) { z->state->mode = IM_BLOCKS; break; } z->state->mode = IM_DICT4; case IM_DICT4: IM_NEEDBYTE z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; z->state->mode = IM_DICT3; case IM_DICT3: IM_NEEDBYTE z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; z->state->mode = IM_DICT2; case IM_DICT2: IM_NEEDBYTE z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; z->state->mode = IM_DICT1; case IM_DICT1: IM_NEEDBYTE; r; z->state->sub.check.need += (uLong)IM_NEXTBYTE; z->adler = z->state->sub.check.need; z->state->mode = IM_DICT0; return Z_NEED_DICT; case IM_DICT0: z->state->mode = IM_BAD; z->msg = (char*)"need dictionary"; z->state->sub.marker = 0; // can try inflateSync return Z_STREAM_ERROR; case IM_BLOCKS: r = inflate_blocks(z->state->blocks, z, r); if (r == Z_DATA_ERROR) { z->state->mode = IM_BAD; z->state->sub.marker = 0; // can try inflateSync break; } if (r == Z_OK) r = f; if (r != Z_STREAM_END) return r; r = f; inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); if (z->state->nowrap) { z->state->mode = IM_DONE; break; } z->state->mode = IM_CHECK4; case IM_CHECK4: IM_NEEDBYTE z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; z->state->mode = IM_CHECK3; case IM_CHECK3: IM_NEEDBYTE z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; z->state->mode = IM_CHECK2; case IM_CHECK2: IM_NEEDBYTE z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; z->state->mode = IM_CHECK1; case IM_CHECK1: IM_NEEDBYTE z->state->sub.check.need += (uLong)IM_NEXTBYTE; if (z->state->sub.check.was != z->state->sub.check.need) { z->state->mode = IM_BAD; z->msg = (char*)"incorrect data check"; z->state->sub.marker = 5; // can't try inflateSync break; } LuTracev((stderr, "inflate: zlib check ok\n")); z->state->mode = IM_DONE; case IM_DONE: return Z_STREAM_END; case IM_BAD: return Z_DATA_ERROR; default: return Z_STREAM_ERROR; } } // unzip.c -- IO on .zip files using zlib // Version 0.15 beta, Mar 19th, 1998, // Read unzip.h for more info #define UNZ_BUFSIZE (16384) #define UNZ_MAXFILENAMEINZIP (256) #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) const char unz_copyright[] = " unzip 0.15 Copyright 1998 Gilles Vollant "; // unz_file_info_interntal contain internal info about a file in zipfile typedef struct unz_file_info_internal_s { uLong offset_curfile;// relative offset of local header 4 bytes } unz_file_info_internal; typedef struct { bool is_handle; // either a handle or memory bool canseek; // for handles: HANDLE h; bool herr; unsigned long initial_offset; bool mustclosehandle; // for memory: void *buf; unsigned int len,pos; // if it's a memory block } LUFILE; LUFILE *lufopen(void *z,unsigned int len,DWORD flags,ZRESULT *err) { if (flags!=ZIP_HANDLE && flags!=ZIP_FILENAME && flags!=ZIP_MEMORY) {*err=ZR_ARGS; return NULL;} // HANDLE h=0; bool canseek=false; *err=ZR_OK; bool mustclosehandle=false; if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) { if (flags==ZIP_HANDLE) { HANDLE hf = z; h=hf; mustclosehandle=false; #ifdef DuplicateHandle BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&h,0,FALSE,DUPLICATE_SAME_ACCESS); if (!res) mustclosehandle=true; #endif } else { h=CreateFile((const TCHAR*)z,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if (h==INVALID_HANDLE_VALUE) {*err=ZR_NOFILE; return NULL;} mustclosehandle=true; } // test if we can seek on it. We can't use GetFileType(h)==FILE_TYPE_DISK since it's not on CE. DWORD res = SetFilePointer(h,0,0,FILE_CURRENT); canseek = (res!=0xFFFFFFFF); } LUFILE *lf = new LUFILE; if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) { lf->is_handle=true; lf->mustclosehandle=mustclosehandle; lf->canseek=canseek; lf->h=h; lf->herr=false; lf->initial_offset=0; if (canseek) lf->initial_offset = SetFilePointer(h,0,NULL,FILE_CURRENT); } else { lf->is_handle=false; lf->canseek=true; lf->mustclosehandle=false; lf->buf=z; lf->len=len; lf->pos=0; lf->initial_offset=0; } *err=ZR_OK; return lf; } int lufclose(LUFILE *stream) { if (stream==NULL) return EOF; if (stream->mustclosehandle) CloseHandle(stream->h); delete stream; return 0; } int luferror(LUFILE *stream) { if (stream->is_handle && stream->herr) return 1; else return 0; } long int luftell(LUFILE *stream) { if (stream->is_handle && stream->canseek) return SetFilePointer(stream->h,0,NULL,FILE_CURRENT)-stream->initial_offset; else if (stream->is_handle) return 0; else return stream->pos; } int lufseek(LUFILE *stream, long offset, int whence) { if (stream->is_handle && stream->canseek) { if (whence==SEEK_SET) SetFilePointer(stream->h,stream->initial_offset+offset,0,FILE_BEGIN); else if (whence==SEEK_CUR) SetFilePointer(stream->h,offset,NULL,FILE_CURRENT); else if (whence==SEEK_END) SetFilePointer(stream->h,offset,NULL,FILE_END); else return 19; // EINVAL return 0; } else if (stream->is_handle) return 29; // ESPIPE else { if (whence==SEEK_SET) stream->pos=offset; else if (whence==SEEK_CUR) stream->pos+=offset; else if (whence==SEEK_END) stream->pos=stream->len+offset; return 0; } } size_t lufread(void *ptr,size_t size,size_t n,LUFILE *stream) { unsigned int toread = (unsigned int)(size*n); if (stream->is_handle) { DWORD red; BOOL res = ReadFile(stream->h,ptr,toread,&red,NULL); if (!res) stream->herr=true; return red/size; } if (stream->pos+toread > stream->len) toread = stream->len-stream->pos; memcpy(ptr, (char*)stream->buf + stream->pos, toread); DWORD red = toread; stream->pos += red; return red/size; } // file_in_zip_read_info_s contain internal information about a file in zipfile, // when reading and decompress it typedef struct { char *read_buffer; // internal buffer for compressed data z_stream stream; // zLib stream structure for inflate uLong pos_in_zipfile; // position in byte on the zipfile, for fseek uLong stream_initialised; // flag set if stream structure is initialised uLong offset_local_extrafield;// offset of the local extra field uInt size_local_extrafield;// size of the local extra field uLong pos_local_extrafield; // position in the local extra field in read uLong crc32; // crc32 of all data uncompressed uLong crc32_wait; // crc32 we must obtain after decompress all uLong rest_read_compressed; // number of byte to be decompressed uLong rest_read_uncompressed;//number of byte to be obtained after decomp LUFILE* file; // io structore of the zipfile uLong compression_method; // compression method (0==store) uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) bool encrypted; // is it encrypted? unsigned long keys[3]; // decryption keys, initialized by unzOpenCurrentFile int encheadleft; // the first call(s) to unzReadCurrentFile will read this many encryption-header bytes first char crcenctest; // if encrypted, we'll check the encryption buffer against this } file_in_zip_read_info_s; // unz_s contain internal information about the zipfile typedef struct { LUFILE* file; // io structore of the zipfile unz_global_info gi; // public global information uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) uLong num_file; // number of the current file in the zipfile uLong pos_in_central_dir; // pos of the current file in the central dir uLong current_file_ok; // flag about the usability of the current file uLong central_pos; // position of the beginning of the central dir uLong size_central_dir; // size of the central directory uLong offset_central_dir; // offset of start of central directory with respect to the starting disk number unz_file_info cur_file_info; // public info about the current file in zip unz_file_info_internal cur_file_info_internal; // private info about it file_in_zip_read_info_s* pfile_in_zip_read; // structure about the current file if we are decompressing it } unz_s, *unzFile; int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity); // Compare two filename (fileName1,fileName2). z_off_t unztell (unzFile file); // Give the current position in uncompressed data int unzeof (unzFile file); // return 1 if the end of file was reached, 0 elsewhere int unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len); // Read extra field from the current file (opened by unzOpenCurrentFile) // This is the local-header version of the extra field (sometimes, there is // more info in the local-header version than in the central-header) // // if buf==NULL, it return the size of the local extra field // // if buf!=NULL, len is the size of the buffer, the extra header is copied in // buf. // the return value is the number of bytes copied in buf, or (if <0) // the error code // =========================================================================== // Read a byte from a gz_stream; update next_in and avail_in. Return EOF // for end of file. // IN assertion: the stream s has been sucessfully opened for reading. int unzlocal_getByte(LUFILE *fin,int *pi) { unsigned char c; int err = (int)lufread(&c, 1, 1, fin); if (err==1) { *pi = (int)c; return UNZ_OK; } else { if (luferror(fin)) return UNZ_ERRNO; else return UNZ_EOF; } } // =========================================================================== // Reads a long in LSB order from the given gz_stream. Sets int unzlocal_getShort (LUFILE *fin,uLong *pX) { uLong x ; int i; int err; err = unzlocal_getByte(fin,&i); x = (uLong)i; if (err==UNZ_OK) err = unzlocal_getByte(fin,&i); x += ((uLong)i)<<8; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } int unzlocal_getLong (LUFILE *fin,uLong *pX) { uLong x ; int i; int err; err = unzlocal_getByte(fin,&i); x = (uLong)i; if (err==UNZ_OK) err = unzlocal_getByte(fin,&i); x += ((uLong)i)<<8; if (err==UNZ_OK) err = unzlocal_getByte(fin,&i); x += ((uLong)i)<<16; if (err==UNZ_OK) err = unzlocal_getByte(fin,&i); x += ((uLong)i)<<24; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } // My own strcmpi / strcasecmp int strcmpcasenosensitive_internal (const char* fileName1,const char *fileName2) { for (;;) { char c1=*(fileName1++); char c2=*(fileName2++); if ((c1>='a') && (c1<='z')) c1 -= (char)0x20; if ((c2>='a') && (c2<='z')) c2 -= (char)0x20; if (c1=='\0') return ((c2=='\0') ? 0 : -1); if (c2=='\0') return 1; if (c1c2) return 1; } } // // Compare two filename (fileName1,fileName2). // If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) // If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) // int unzStringFileNameCompare (const char*fileName1,const char*fileName2,int iCaseSensitivity) { if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); else return strcmpcasenosensitive_internal(fileName1,fileName2); } #define BUFREADCOMMENT (0x400) // Locate the Central directory of a zipfile (at the end, just before // the global comment). Lu bugfix 2005.07.26 - returns 0xFFFFFFFF if not found, // rather than 0, since 0 is a valid central-dir-location for an empty zipfile. uLong unzlocal_SearchCentralDir(LUFILE *fin) { if (lufseek(fin,0,SEEK_END) != 0) return 0xFFFFFFFF; uLong uSizeFile = luftell(fin); uLong uMaxBack=0xffff; // maximum size of global comment if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; unsigned char *buf = (unsigned char*)zmalloc(BUFREADCOMMENT+4); if (buf==NULL) return 0xFFFFFFFF; uLong uPosFound=0xFFFFFFFF; uLong uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); if (lufseek(fin,uReadPos,SEEK_SET)!=0) break; if (lufread(buf,(uInt)uReadSize,1,fin)!=1) break; for (i=(int)uReadSize-3; (i--)>=0;) { if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { uPosFound = uReadPos+i; break; } } if (uPosFound!=0) break; } if (buf) zfree(buf); return uPosFound; } int unzGoToFirstFile (unzFile file); int unzCloseCurrentFile (unzFile file); // Open a Zip file. // If the zipfile cannot be opened (file don't exist or in not valid), return NULL. // Otherwise, the return value is a unzFile Handle, usable with other unzip functions unzFile unzOpenInternal(LUFILE *fin) { if (fin==NULL) return NULL; if (unz_copyright[0]!=' ') {lufclose(fin); return NULL;} int err=UNZ_OK; unz_s us; uLong central_pos,uL; central_pos = unzlocal_SearchCentralDir(fin); if (central_pos==0xFFFFFFFF) err=UNZ_ERRNO; if (lufseek(fin,central_pos,SEEK_SET)!=0) err=UNZ_ERRNO; // the signature, already checked if (unzlocal_getLong(fin,&uL)!=UNZ_OK) err=UNZ_ERRNO; // number of this disk uLong number_disk; // number of the current dist, used for spanning ZIP, unsupported, always 0 if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; // number of the disk with the start of the central directory uLong number_disk_with_CD; // number the the disk with central dir, used for spaning ZIP, unsupported, always 0 if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; // total number of entries in the central dir on this disk if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; // total number of entries in the central dir uLong number_entry_CD; // total number of entries in the central dir (same than number_entry on nospan) if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; // size of the central directory if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; // offset of start of central directory with respect to the starting disk number if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; // zipfile comment length if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; if ((central_pos+fin->initial_offsetinitial_offset - (us.offset_central_dir+us.size_central_dir); us.central_pos = central_pos; us.pfile_in_zip_read = NULL; fin->initial_offset = 0; // since the zipfile itself is expected to handle this unz_s *s = (unz_s*)zmalloc(sizeof(unz_s)); *s=us; unzGoToFirstFile((unzFile)s); return (unzFile)s; } // Close a ZipFile opened with unzipOpen. // If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), // these files MUST be closed with unzipCloseCurrentFile before call unzipClose. // return UNZ_OK if there is no problem. int unzClose (unzFile file) { unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (s->pfile_in_zip_read!=NULL) unzCloseCurrentFile(file); lufclose(s->file); if (s) zfree(s); // unused s=0; return UNZ_OK; } // Write info about the ZipFile in the *pglobal_info structure. // No preparation of the structure is needed // return UNZ_OK if there is no problem. int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) { unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; *pglobal_info=s->gi; return UNZ_OK; } // Translate date/time from Dos format to tm_unz (readable more easilty) void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) { uLong uDate; uDate = (uLong)(ulDosDate>>16); ptm->tm_mday = (uInt)(uDate&0x1f) ; ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; } // Get Info about the current file in the zipfile, with internal only info int unzlocal_GetCurrentFileInfoInternal (unzFile file, unz_file_info *pfile_info, unz_file_info_internal *pfile_info_internal, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize); int unzlocal_GetCurrentFileInfoInternal (unzFile file, unz_file_info *pfile_info, unz_file_info_internal *pfile_info_internal, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize) { unz_s* s; unz_file_info file_info; unz_file_info_internal file_info_internal; int err=UNZ_OK; uLong uMagic; long lSeek=0; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (lufseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) err=UNZ_ERRNO; // we check the magic if (err==UNZ_OK) if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x02014b50) err=UNZ_BADZIPFILE; if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) err=UNZ_ERRNO; unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) err=UNZ_ERRNO; lSeek+=file_info.size_filename; if ((err==UNZ_OK) && (szFileName!=NULL)) { uLong uSizeRead ; if (file_info.size_filename0) && (fileNameBufferSize>0)) if (lufread(szFileName,(uInt)uSizeRead,1,s->file)!=1) err=UNZ_ERRNO; lSeek -= uSizeRead; } if ((err==UNZ_OK) && (extraField!=NULL)) { uLong uSizeRead ; if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) if (lufread(extraField,(uInt)uSizeRead,1,s->file)!=1) err=UNZ_ERRNO; lSeek += file_info.size_file_extra - uSizeRead; } else lSeek+=file_info.size_file_extra; if ((err==UNZ_OK) && (szComment!=NULL)) { uLong uSizeRead ; if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) {} // unused lSeek=0; else err=UNZ_ERRNO; if ((file_info.size_file_comment>0) && (commentBufferSize>0)) if (lufread(szComment,(uInt)uSizeRead,1,s->file)!=1) err=UNZ_ERRNO; //unused lSeek+=file_info.size_file_comment - uSizeRead; } else {} //unused lSeek+=file_info.size_file_comment; if ((err==UNZ_OK) && (pfile_info!=NULL)) *pfile_info=file_info; if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) *pfile_info_internal=file_info_internal; return err; } // Write info about the ZipFile in the *pglobal_info structure. // No preparation of the structure is needed // return UNZ_OK if there is no problem. int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize) { return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,szFileName,fileNameBufferSize, extraField,extraFieldBufferSize, szComment,commentBufferSize); } // Set the current file of the zipfile to the first file. // return UNZ_OK if there is no problem int unzGoToFirstFile (unzFile file) { int err; unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; s->pos_in_central_dir=s->offset_central_dir; s->num_file=0; err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } // Set the current file of the zipfile to the next file. // return UNZ_OK if there is no problem // return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. int unzGoToNextFile (unzFile file) { unz_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; if (s->num_file+1==s->gi.number_entry) return UNZ_END_OF_LIST_OF_FILE; s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; s->num_file++; err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } // Try locate the file szFileName in the zipfile. // For the iCaseSensitivity signification, see unzStringFileNameCompare // return value : // UNZ_OK if the file is found. It becomes the current file. // UNZ_END_OF_LIST_OF_FILE if the file is not found int unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) { unz_s* s; int err; uLong num_fileSaved; uLong pos_in_central_dirSaved; if (file==NULL) return UNZ_PARAMERROR; if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; num_fileSaved = s->num_file; pos_in_central_dirSaved = s->pos_in_central_dir; err = unzGoToFirstFile(file); while (err == UNZ_OK) { char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; unzGetCurrentFileInfo(file,NULL, szCurrentFileName,sizeof(szCurrentFileName)-1, NULL,0,NULL,0); if (unzStringFileNameCompare(szCurrentFileName,szFileName,iCaseSensitivity)==0) return UNZ_OK; err = unzGoToNextFile(file); } s->num_file = num_fileSaved ; s->pos_in_central_dir = pos_in_central_dirSaved ; return err; } // Read the local header of the current zipfile // Check the coherency of the local header and info in the end of central // directory about this file // store in *piSizeVar the size of extra info in local header // (filename and size of extra field data) int unzlocal_CheckCurrentFileCoherencyHeader (unz_s *s,uInt *piSizeVar, uLong *poffset_local_extrafield, uInt *psize_local_extrafield) { uLong uMagic,uData,uFlags; uLong size_filename; uLong size_extra_field; int err=UNZ_OK; *piSizeVar = 0; *poffset_local_extrafield = 0; *psize_local_extrafield = 0; if (lufseek(s->file,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; if (err==UNZ_OK) if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x04034b50) err=UNZ_BADZIPFILE; if (unzlocal_getShort(s->file,&uData) != UNZ_OK) err=UNZ_ERRNO; // else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) // err=UNZ_BADZIPFILE; if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&uData) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) err=UNZ_BADZIPFILE; if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // date/time err=UNZ_ERRNO; if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // crc err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size compr err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size uncompr err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) err=UNZ_BADZIPFILE; *piSizeVar += (uInt)size_filename; if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) err=UNZ_ERRNO; *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_filename; *psize_local_extrafield = (uInt)size_extra_field; *piSizeVar += (uInt)size_extra_field; return err; } // Open for reading data the current file in the zipfile. // If there is no error and the file is opened, the return value is UNZ_OK. int unzOpenCurrentFile (unzFile file, const char *password) { int err; int Store; uInt iSizeVar; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; uLong offset_local_extrafield; // offset of the local extra field uInt size_local_extrafield; // size of the local extra field if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_PARAMERROR; if (s->pfile_in_zip_read != NULL) unzCloseCurrentFile(file); if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) return UNZ_BADZIPFILE; pfile_in_zip_read_info = (file_in_zip_read_info_s*)zmalloc(sizeof(file_in_zip_read_info_s)); if (pfile_in_zip_read_info==NULL) return UNZ_INTERNALERROR; pfile_in_zip_read_info->read_buffer=(char*)zmalloc(UNZ_BUFSIZE); pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; pfile_in_zip_read_info->pos_local_extrafield=0; if (pfile_in_zip_read_info->read_buffer==NULL) { if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); //unused pfile_in_zip_read_info=0; return UNZ_INTERNALERROR; } pfile_in_zip_read_info->stream_initialised=0; if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) { // unused err=UNZ_BADZIPFILE; } Store = s->cur_file_info.compression_method==0; pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; pfile_in_zip_read_info->crc32=0; pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; pfile_in_zip_read_info->file=s->file; pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; pfile_in_zip_read_info->stream.total_out = 0; if (!Store) { pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; pfile_in_zip_read_info->stream.zfree = (free_func)0; pfile_in_zip_read_info->stream.opaque = (voidpf)0; err=inflateInit2(&pfile_in_zip_read_info->stream); if (err == Z_OK) pfile_in_zip_read_info->stream_initialised=1; // windowBits is passed < 0 to tell that there is no zlib header. // Note that in this case inflate *requires* an extra "dummy" byte // after the compressed stream in order to complete decompression and // return Z_STREAM_END. // In unzip, i don't wait absolutely Z_STREAM_END because I known the // size of both compressed and uncompressed data } pfile_in_zip_read_info->rest_read_compressed = s->cur_file_info.compressed_size ; pfile_in_zip_read_info->rest_read_uncompressed = s->cur_file_info.uncompressed_size ; pfile_in_zip_read_info->encrypted = (s->cur_file_info.flag&1)!=0; bool extlochead = (s->cur_file_info.flag&8)!=0; if (extlochead) pfile_in_zip_read_info->crcenctest = (char)((s->cur_file_info.dosDate>>8)&0xff); else pfile_in_zip_read_info->crcenctest = (char)(s->cur_file_info.crc >> 24); pfile_in_zip_read_info->encheadleft = (pfile_in_zip_read_info->encrypted?12:0); pfile_in_zip_read_info->keys[0] = 305419896L; pfile_in_zip_read_info->keys[1] = 591751049L; pfile_in_zip_read_info->keys[2] = 878082192L; for (const char *cp=password; cp!=0 && *cp!=0; cp++) Uupdate_keys(pfile_in_zip_read_info->keys,*cp); pfile_in_zip_read_info->pos_in_zipfile = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + iSizeVar; pfile_in_zip_read_info->stream.avail_in = (uInt)0; s->pfile_in_zip_read = pfile_in_zip_read_info; return UNZ_OK; } // Read bytes from the current file. // buf contain buffer where data must be copied // len the size of buf. // return the number of byte copied if somes bytes are copied (and also sets *reached_eof) // return 0 if the end of file was reached. (and also sets *reached_eof). // return <0 with error code if there is an error. (in which case *reached_eof is meaningless) // (UNZ_ERRNO for IO error, or zLib error for uncompress error) int unzReadCurrentFile (unzFile file, voidp buf, unsigned len, bool *reached_eof) { int err=UNZ_OK; uInt iRead = 0; if (reached_eof!=0) *reached_eof=false; unz_s *s = (unz_s*)file; if (s==NULL) return UNZ_PARAMERROR; file_in_zip_read_info_s* pfile_in_zip_read_info = s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if ((pfile_in_zip_read_info->read_buffer == NULL)) return UNZ_END_OF_LIST_OF_FILE; if (len==0) return 0; pfile_in_zip_read_info->stream.next_out = (Byte*)buf; pfile_in_zip_read_info->stream.avail_out = (uInt)len; if (len>pfile_in_zip_read_info->rest_read_uncompressed) { pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; } while (pfile_in_zip_read_info->stream.avail_out>0) { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) { uInt uReadThis = UNZ_BUFSIZE; if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; if (uReadThis == 0) {if (reached_eof!=0) *reached_eof=true; return UNZ_EOF;} if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; if (lufread(pfile_in_zip_read_info->read_buffer,uReadThis,1,pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; pfile_in_zip_read_info->pos_in_zipfile += uReadThis; pfile_in_zip_read_info->rest_read_compressed-=uReadThis; pfile_in_zip_read_info->stream.next_in = (Byte*)pfile_in_zip_read_info->read_buffer; pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; // if (pfile_in_zip_read_info->encrypted) { char *buf = (char*)pfile_in_zip_read_info->stream.next_in; for (unsigned int i=0; ikeys,buf[i]); } } unsigned int uDoEncHead = pfile_in_zip_read_info->encheadleft; if (uDoEncHead>pfile_in_zip_read_info->stream.avail_in) uDoEncHead=pfile_in_zip_read_info->stream.avail_in; if (uDoEncHead>0) { char bufcrc=pfile_in_zip_read_info->stream.next_in[uDoEncHead-1]; pfile_in_zip_read_info->rest_read_uncompressed-=uDoEncHead; pfile_in_zip_read_info->stream.avail_in -= uDoEncHead; pfile_in_zip_read_info->stream.next_in += uDoEncHead; pfile_in_zip_read_info->encheadleft -= uDoEncHead; if (pfile_in_zip_read_info->encheadleft==0) { if (bufcrc!=pfile_in_zip_read_info->crcenctest) return UNZ_PASSWORD; } } if (pfile_in_zip_read_info->compression_method==0) { uInt uDoCopy,i ; if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) { uDoCopy = pfile_in_zip_read_info->stream.avail_out ; } else { uDoCopy = pfile_in_zip_read_info->stream.avail_in ; } for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,pfile_in_zip_read_info->stream.next_out,uDoCopy); pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; pfile_in_zip_read_info->stream.avail_in -= uDoCopy; pfile_in_zip_read_info->stream.avail_out -= uDoCopy; pfile_in_zip_read_info->stream.next_out += uDoCopy; pfile_in_zip_read_info->stream.next_in += uDoCopy; pfile_in_zip_read_info->stream.total_out += uDoCopy; iRead += uDoCopy; if (pfile_in_zip_read_info->rest_read_uncompressed==0) {if (reached_eof!=0) *reached_eof=true;} } else { uLong uTotalOutBefore,uTotalOutAfter; const Byte *bufBefore; uLong uOutThis; int flush=Z_SYNC_FLUSH; uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; bufBefore = pfile_in_zip_read_info->stream.next_out; // err=inflate(&pfile_in_zip_read_info->stream,flush); // uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; uOutThis = uTotalOutAfter-uTotalOutBefore; pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,bufBefore,(uInt)(uOutThis)); pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); if (err==Z_STREAM_END || pfile_in_zip_read_info->rest_read_uncompressed==0) { if (reached_eof!=0) *reached_eof=true; return iRead; } if (err!=Z_OK) break; } } if (err==Z_OK) return iRead; return err; } // Give the current position in uncompressed data z_off_t unztell (unzFile file) { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; return (z_off_t)pfile_in_zip_read_info->stream.total_out; } // return 1 if the end of file was reached, 0 elsewhere int unzeof (unzFile file) { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->rest_read_uncompressed == 0) return 1; else return 0; } // Read extra field from the current file (opened by unzOpenCurrentFile) // This is the local-header version of the extra field (sometimes, there is // more info in the local-header version than in the central-header) // if buf==NULL, it return the size of the local extra field that can be read // if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. // the return value is the number of bytes copied in buf, or (if <0) the error code int unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; uInt read_now; uLong size_to_read; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; size_to_read = (pfile_in_zip_read_info->size_local_extrafield - pfile_in_zip_read_info->pos_local_extrafield); if (buf==NULL) return (int)size_to_read; if (len>size_to_read) read_now = (uInt)size_to_read; else read_now = (uInt)len ; if (read_now==0) return 0; if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) return UNZ_ERRNO; if (lufread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; return (int)read_now; } // Close the file in zip opened with unzipOpenCurrentFile // Return UNZ_CRCERROR if all the file was read but the CRC is not good int unzCloseCurrentFile (unzFile file) { int err=UNZ_OK; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->rest_read_uncompressed == 0) { if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) err=UNZ_CRCERROR; } if (pfile_in_zip_read_info->read_buffer!=0) { void *buf = pfile_in_zip_read_info->read_buffer; zfree(buf); pfile_in_zip_read_info->read_buffer=0; } pfile_in_zip_read_info->read_buffer = NULL; if (pfile_in_zip_read_info->stream_initialised) inflateEnd(&pfile_in_zip_read_info->stream); pfile_in_zip_read_info->stream_initialised = 0; if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); // unused pfile_in_zip_read_info=0; s->pfile_in_zip_read=NULL; return err; } // Get the global comment string of the ZipFile, in the szComment buffer. // uSizeBuf is the size of the szComment buffer. // return the number of byte copied or an error code <0 int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) { //int err=UNZ_OK; unz_s* s; uLong uReadThis ; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; uReadThis = uSizeBuf; if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; if (lufseek(s->file,s->central_pos+22,SEEK_SET)!=0) return UNZ_ERRNO; if (uReadThis>0) { *szComment='\0'; if (lufread(szComment,(uInt)uReadThis,1,s->file)!=1) return UNZ_ERRNO; } if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; return (int)uReadThis; } int unzOpenCurrentFile (unzFile file, const char *password); int unzReadCurrentFile (unzFile file, void *buf, unsigned len); int unzCloseCurrentFile (unzFile file); typedef unsigned __int32 lutime_t; // define it ourselves since we don't include time.h FILETIME timet2filetime(const lutime_t t) { LONGLONG i = Int32x32To64(t,10000000) + 116444736000000000; FILETIME ft; ft.dwLowDateTime = (DWORD) i; ft.dwHighDateTime = (DWORD)(i >>32); return ft; } FILETIME dosdatetime2filetime(WORD dosdate,WORD dostime) { // date: bits 0-4 are day of month 1-31. Bits 5-8 are month 1..12. Bits 9-15 are year-1980 // time: bits 0-4 are seconds/2, bits 5-10 are minute 0..59. Bits 11-15 are hour 0..23 SYSTEMTIME st; st.wYear = (WORD)(((dosdate>>9)&0x7f) + 1980); st.wMonth = (WORD)((dosdate>>5)&0xf); st.wDay = (WORD)(dosdate&0x1f); st.wHour = (WORD)((dostime>>11)&0x1f); st.wMinute = (WORD)((dostime>>5)&0x3f); st.wSecond = (WORD)((dostime&0x1f)*2); st.wMilliseconds = 0; FILETIME ft; SystemTimeToFileTime(&st,&ft); return ft; } class TUnzip { public: TUnzip(const char *pwd) : uf(0), unzbuf(0), currentfile(-1), czei(-1), password(0) {if (pwd!=0) {password=new char[strlen(pwd)+1]; strcpy_s(password,MAX_PATH,pwd);}} ~TUnzip() {if (password!=0) delete[] password; password=0; if (unzbuf!=0) delete[] unzbuf; unzbuf=0;} unzFile uf; int currentfile; ZIPENTRY cze; int czei; char *password; char *unzbuf; // lazily created and destroyed, used by Unzip TCHAR rootdir[MAX_PATH]; // includes a trailing slash ZRESULT Open(void *z,unsigned int len,DWORD flags); ZRESULT Get(int index,ZIPENTRY *ze); ZRESULT Find(const TCHAR *name,bool ic,int *index,ZIPENTRY *ze); ZRESULT Unzip(int index,void *dst,unsigned int len,DWORD flags); ZRESULT SetUnzipBaseDir(const TCHAR *dir); ZRESULT Close(); }; ZRESULT TUnzip::Open(void *z,unsigned int len,DWORD flags) { if (uf!=0 || currentfile!=-1) return ZR_NOTINITED; // #ifdef GetCurrentDirectory GetCurrentDirectory(MAX_PATH,rootdir); #else _tcscpy(rootdir,_T("\\")); #endif TCHAR lastchar = rootdir[_tcslen(rootdir)-1]; if (lastchar!='\\' && lastchar!='/') _tcscat_s(rootdir,_T("\\")); // if (flags==ZIP_HANDLE) { // test if we can seek on it. We can't use GetFileType(h)==FILE_TYPE_DISK since it's not on CE. DWORD res = SetFilePointer(z,0,0,FILE_CURRENT); bool canseek = (res!=0xFFFFFFFF); if (!canseek) return ZR_SEEK; } ZRESULT e; LUFILE *f = lufopen(z,len,flags,&e); if (f==NULL) return e; uf = unzOpenInternal(f); if (uf==0) return ZR_NOFILE; return ZR_OK; } ZRESULT TUnzip::SetUnzipBaseDir(const TCHAR *dir) { _tcscpy_s(rootdir, MAX_PATH, dir); TCHAR lastchar = rootdir[_tcslen(rootdir)-1]; if (lastchar!='\\' && lastchar!='/') _tcscat_s(rootdir,_T("\\")); return ZR_OK; } ZRESULT TUnzip::Get(int index,ZIPENTRY *ze) { if (index<-1 || index>=(int)uf->gi.number_entry) return ZR_ARGS; if (currentfile!=-1) unzCloseCurrentFile(uf); currentfile=-1; if (index==czei && index!=-1) {memcpy(ze,&cze,sizeof(ZIPENTRY)); return ZR_OK;} if (index==-1) { ze->index = uf->gi.number_entry; ze->name[0]=0; ze->attr=0; ze->atime.dwLowDateTime=0; ze->atime.dwHighDateTime=0; ze->ctime.dwLowDateTime=0; ze->ctime.dwHighDateTime=0; ze->mtime.dwLowDateTime=0; ze->mtime.dwHighDateTime=0; ze->comp_size=0; ze->unc_size=0; return ZR_OK; } if (index<(int)uf->num_file) unzGoToFirstFile(uf); while ((int)uf->num_filefile,offset,SEEK_SET)!=0) return ZR_READ; unsigned char *extra = new unsigned char[extralen]; if (lufread(extra,1,(uInt)extralen,uf->file)!=extralen) {delete[] extra; return ZR_READ;} // ze->index=uf->num_file; TCHAR tfn[MAX_PATH]; #ifdef UNICODE MultiByteToWideChar(CP_UTF8,0,fn,-1,tfn,MAX_PATH); #else strcpy(tfn,fn); #endif // As a safety feature: if the zip filename had sneaky stuff // like "c:\windows\file.txt" or "\windows\file.txt" or "fred\..\..\..\windows\file.txt" // then we get rid of them all. That way, when the programmer does UnzipItem(hz,i,ze.name), // it won't be a problem. (If the programmer really did want to get the full evil information, // then they can edit out this security feature from here). // In particular, we chop off any prefixes that are "c:\" or "\" or "/" or "[stuff]\.." or "[stuff]/.." const TCHAR *sfn=tfn; for (;;) { if (sfn[0]!=0 && sfn[1]==':') {sfn+=2; continue;} if (sfn[0]=='\\') {sfn++; continue;} if (sfn[0]=='/') {sfn++; continue;} const TCHAR *c; c=_tcsstr(sfn,_T("\\..\\")); if (c!=0) {sfn=c+4; continue;} c=_tcsstr(sfn,_T("\\../")); if (c!=0) {sfn=c+4; continue;} c=_tcsstr(sfn,_T("/../")); if (c!=0) {sfn=c+4; continue;} c=_tcsstr(sfn,_T("/..\\")); if (c!=0) {sfn=c+4; continue;} break; } _tcscpy_s(ze->name, MAX_PATH, sfn); // zip has an 'attribute' 32bit value. Its lower half is windows stuff // its upper half is standard unix stat.st_mode. We'll start trying // to read it in unix mode unsigned long a = ufi.external_fa; bool isdir = (a&0x40000000)!=0; bool readonly= (a&0x00800000)==0; //bool readable= (a&0x01000000)!=0; // unused //bool executable=(a&0x00400000)!=0; // unused bool hidden=false, system=false, archive=true; // but in normal hostmodes these are overridden by the lower half... int host = ufi.version>>8; if (host==0 || host==7 || host==11 || host==14) { readonly= (a&0x00000001)!=0; hidden= (a&0x00000002)!=0; system= (a&0x00000004)!=0; isdir= (a&0x00000010)!=0; archive= (a&0x00000020)!=0; } ze->attr=0; if (isdir) ze->attr |= FILE_ATTRIBUTE_DIRECTORY; if (archive) ze->attr|=FILE_ATTRIBUTE_ARCHIVE; if (hidden) ze->attr|=FILE_ATTRIBUTE_HIDDEN; if (readonly) ze->attr|=FILE_ATTRIBUTE_READONLY; if (system) ze->attr|=FILE_ATTRIBUTE_SYSTEM; ze->comp_size = ufi.compressed_size; ze->unc_size = ufi.uncompressed_size; // WORD dostime = (WORD)(ufi.dosDate&0xFFFF); WORD dosdate = (WORD)((ufi.dosDate>>16)&0xFFFF); FILETIME ftd = dosdatetime2filetime(dosdate,dostime); FILETIME ft; LocalFileTimeToFileTime(&ftd,&ft); ze->atime=ft; ze->ctime=ft; ze->mtime=ft; // the zip will always have at least that dostime. But if it also has // an extra header, then we'll instead get the info from that. unsigned int epos=0; while (epos+4mtime = timet2filetime(mtime); } if (hasatime) { lutime_t atime = ((extra[epos+0])<<0) | ((extra[epos+1])<<8) |((extra[epos+2])<<16) | ((extra[epos+3])<<24); epos+=4; ze->atime = timet2filetime(atime); } if (hasctime) { lutime_t ctime = ((extra[epos+0])<<0) | ((extra[epos+1])<<8) |((extra[epos+2])<<16) | ((extra[epos+3])<<24); epos+=4; ze->ctime = timet2filetime(ctime); } break; } // if (extra!=0) delete[] extra; memcpy(&cze,ze,sizeof(ZIPENTRY)); czei=index; return ZR_OK; } ZRESULT TUnzip::Find(const TCHAR *tname,bool ic,int *index,ZIPENTRY *ze) { char name[MAX_PATH]; #ifdef UNICODE WideCharToMultiByte(CP_UTF8,0,tname,-1,name,MAX_PATH,0,0); #else strcpy(name,tname); #endif int res = unzLocateFile(uf,name,ic?CASE_INSENSITIVE:CASE_SENSITIVE); if (res!=UNZ_OK) { if (index!=0) *index=-1; if (ze!=NULL) {ZeroMemory(ze,sizeof(ZIPENTRY)); ze->index=-1;} return ZR_NOTFOUND; } if (currentfile!=-1) unzCloseCurrentFile(uf); currentfile=-1; int i = (int)uf->num_file; if (index!=NULL) *index=i; if (ze!=NULL) { ZRESULT zres = Get(i,ze); if (zres!=ZR_OK) return zres; } return ZR_OK; } void EnsureDirectory(const TCHAR *rootdir, const TCHAR *dir) { if (rootdir!=0 && GetFileAttributes(rootdir)==0xFFFFFFFF) CreateDirectory(rootdir,0); if (*dir==0) return; const TCHAR *lastslash=dir, *c=lastslash; while (*c!=0) {if (*c=='/' || *c=='\\') lastslash=c; c++;} const TCHAR *name=lastslash; if (lastslash!=dir) { TCHAR tmp[MAX_PATH]; memcpy(tmp,dir,sizeof(TCHAR)*(lastslash-dir)); tmp[lastslash-dir]=0; EnsureDirectory(rootdir,tmp); name++; } TCHAR cd[MAX_PATH]; *cd=0; if (rootdir!=0) _tcscpy_s(cd, MAX_PATH, rootdir); _tcscat_s(cd,dir); if (GetFileAttributes(cd)==0xFFFFFFFF) CreateDirectory(cd,NULL); } ZRESULT TUnzip::Unzip(int index,void *dst,unsigned int len,DWORD flags) { if (flags!=ZIP_MEMORY && flags!=ZIP_FILENAME && flags!=ZIP_HANDLE) return ZR_ARGS; if (flags==ZIP_MEMORY) { if (index!=currentfile) { if (currentfile!=-1) unzCloseCurrentFile(uf); currentfile=-1; if (index>=(int)uf->gi.number_entry) return ZR_ARGS; if (index<(int)uf->num_file) unzGoToFirstFile(uf); while ((int)uf->num_file0) return ZR_MORE; if (res==UNZ_PASSWORD) return ZR_PASSWORD; return ZR_FLATE; } // otherwise we're writing to a handle or a file if (currentfile!=-1) unzCloseCurrentFile(uf); currentfile=-1; if (index>=(int)uf->gi.number_entry) return ZR_ARGS; if (index<(int)uf->num_file) unzGoToFirstFile(uf); while ((int)uf->num_file0) {DWORD writ; BOOL bres=WriteFile(h,unzbuf,res,&writ,NULL); if (!bres) {haderr=ZR_WRITE; break;}} if (reached_eof) break; if (res==0) {haderr=ZR_FLATE; break;} } if (!haderr) SetFileTime(h,&ze.ctime,&ze.atime,&ze.mtime); // may fail if it was a pipe if (flags!=ZIP_HANDLE) CloseHandle(h); unzCloseCurrentFile(uf); if (haderr!=0) return haderr; return ZR_OK; } ZRESULT TUnzip::Close() { if (currentfile!=-1) unzCloseCurrentFile(uf); currentfile=-1; if (uf!=0) unzClose(uf); uf=0; return ZR_OK; } ZRESULT lasterrorU=ZR_OK; unsigned int FormatZipMessageU(ZRESULT code, TCHAR *buf,unsigned int len) { if (code==ZR_RECENT) code=lasterrorU; const TCHAR *msg=_T("unknown zip result code"); switch (code) { case ZR_OK: msg=_T("Success"); break; case ZR_NODUPH: msg=_T("Culdn't duplicate handle"); break; case ZR_NOFILE: msg=_T("Couldn't create/open file"); break; case ZR_NOALLOC: msg=_T("Failed to allocate memory"); break; case ZR_WRITE: msg=_T("Error writing to file"); break; case ZR_NOTFOUND: msg=_T("File not found in the zipfile"); break; case ZR_MORE: msg=_T("Still more data to unzip"); break; case ZR_CORRUPT: msg=_T("Zipfile is corrupt or not a zipfile"); break; case ZR_READ: msg=_T("Error reading file"); break; case ZR_PASSWORD: msg=_T("Correct password required"); break; case ZR_ARGS: msg=_T("Caller: faulty arguments"); break; case ZR_PARTIALUNZ: msg=_T("Caller: the file had already been partially unzipped"); break; case ZR_NOTMMAP: msg=_T("Caller: can only get memory of a memory zipfile"); break; case ZR_MEMSIZE: msg=_T("Caller: not enough space allocated for memory zipfile"); break; case ZR_FAILED: msg=_T("Caller: there was a previous error"); break; case ZR_ENDED: msg=_T("Caller: additions to the zip have already been ended"); break; case ZR_ZMODE: msg=_T("Caller: mixing creation and opening of zip"); break; case ZR_NOTINITED: msg=_T("Zip-bug: internal initialisation not completed"); break; case ZR_SEEK: msg=_T("Zip-bug: trying to seek the unseekable"); break; case ZR_MISSIZE: msg=_T("Zip-bug: the anticipated size turned out wrong"); break; case ZR_NOCHANGE: msg=_T("Zip-bug: tried to change mind, but not allowed"); break; case ZR_FLATE: msg=_T("Zip-bug: an internal error during flation"); break; } unsigned int mlen=(unsigned int)_tcslen(msg); if (buf==0 || len==0) return mlen; unsigned int n=mlen; if (n+1>len) n=len-1; _tcsncpy_s(buf,MAX_PATH,msg,n); buf[n]=0; return mlen; } typedef struct { DWORD flag; TUnzip *unz; } TUnzipHandleData; HZIP OpenZipInternal(void *z,unsigned int len,DWORD flags, const char *password) { TUnzip *unz = new TUnzip(password); lasterrorU = unz->Open(z,len,flags); if (lasterrorU!=ZR_OK) {delete unz; return 0;} TUnzipHandleData *han = new TUnzipHandleData; han->flag=1; han->unz=unz; return (HZIP)han; } HZIP OpenZipHandle(HANDLE h, const char *password) {return OpenZipInternal((void*)h,0,ZIP_HANDLE,password);} HZIP OpenZip(const TCHAR *fn, const char *password) {return OpenZipInternal((void*)fn,0,ZIP_FILENAME,password);} HZIP OpenZip(void *z,unsigned int len, const char *password) {return OpenZipInternal(z,len,ZIP_MEMORY,password);} ZRESULT GetZipItem(HZIP hz, int index, ZIPENTRY *ze) { ze->index=0; *ze->name=0; ze->unc_size=0; if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} TUnzipHandleData *han = (TUnzipHandleData*)hz; if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} TUnzip *unz = han->unz; lasterrorU = unz->Get(index,ze); return lasterrorU; } ZRESULT FindZipItem(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) { if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} TUnzipHandleData *han = (TUnzipHandleData*)hz; if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} TUnzip *unz = han->unz; lasterrorU = unz->Find(name,ic,index,ze); return lasterrorU; } ZRESULT UnzipItemInternal(HZIP hz, int index, void *dst, unsigned int len, DWORD flags) { if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} TUnzipHandleData *han = (TUnzipHandleData*)hz; if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} TUnzip *unz = han->unz; lasterrorU = unz->Unzip(index,dst,len,flags); return lasterrorU; } ZRESULT UnzipItemHandle(HZIP hz, int index, HANDLE h) {return UnzipItemInternal(hz,index,(void*)h,0,ZIP_HANDLE);} ZRESULT UnzipItem(HZIP hz, int index, const TCHAR *fn) {return UnzipItemInternal(hz,index,(void*)fn,0,ZIP_FILENAME);} ZRESULT UnzipItem(HZIP hz, int index, void *z,unsigned int len) {return UnzipItemInternal(hz,index,z,len,ZIP_MEMORY);} ZRESULT SetUnzipBaseDir(HZIP hz, const TCHAR *dir) { if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} TUnzipHandleData *han = (TUnzipHandleData*)hz; if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} TUnzip *unz = han->unz; lasterrorU = unz->SetUnzipBaseDir(dir); return lasterrorU; } ZRESULT CloseZipU(HZIP hz) { if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} TUnzipHandleData *han = (TUnzipHandleData*)hz; if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} TUnzip *unz = han->unz; lasterrorU = unz->Close(); delete unz; delete han; return lasterrorU; } bool IsZipHandleU(HZIP hz) { if (hz==0) return false; TUnzipHandleData *han = (TUnzipHandleData*)hz; return (han->flag==1); } ================================================ FILE: src/Setup/unzip.h ================================================ #ifndef _unzip_H #define _unzip_H // UNZIPPING functions -- for unzipping. // This file is a repackaged form of extracts from the zlib code available // at www.gzip.org/zlib, by Jean-Loup Gailly and Mark Adler. The original // copyright notice may be found in unzip.cpp. The repackaging was done // by Lucian Wischik to simplify and extend its use in Windows/C++. Also // encryption and unicode filenames have been added. #ifndef _zip_H DECLARE_HANDLE(HZIP); #endif // An HZIP identifies a zip file that has been opened typedef DWORD ZRESULT; // return codes from any of the zip functions. Listed later. typedef struct { int index; // index of this file within the zip TCHAR name[MAX_PATH]; // filename within the zip DWORD attr; // attributes, as in GetFileAttributes. FILETIME atime,ctime,mtime;// access, create, modify filetimes long comp_size; // sizes of item, compressed and uncompressed. These long unc_size; // may be -1 if not yet known (e.g. being streamed in) } ZIPENTRY; HZIP OpenZip(const TCHAR *fn, const char *password); HZIP OpenZip(void *z,unsigned int len, const char *password); HZIP OpenZipHandle(HANDLE h, const char *password); // OpenZip - opens a zip file and returns a handle with which you can // subsequently examine its contents. You can open a zip file from: // from a pipe: OpenZipHandle(hpipe_read,0); // from a file (by handle): OpenZipHandle(hfile,0); // from a file (by name): OpenZip("c:\\test.zip","password"); // from a memory block: OpenZip(bufstart, buflen,0); // If the file is opened through a pipe, then items may only be // accessed in increasing order, and an item may only be unzipped once, // although GetZipItem can be called immediately before and after unzipping // it. If it's opened in any other way, then full random access is possible. // Note: pipe input is not yet implemented. // Note: zip passwords are ascii, not unicode. // Note: for windows-ce, you cannot close the handle until after CloseZip. // but for real windows, the zip makes its own copy of your handle, so you // can close yours anytime. ZRESULT GetZipItem(HZIP hz, int index, ZIPENTRY *ze); // GetZipItem - call this to get information about an item in the zip. // If index is -1 and the file wasn't opened through a pipe, // then it returns information about the whole zipfile // (and in particular ze.index returns the number of index items). // Note: the item might be a directory (ze.attr & FILE_ATTRIBUTE_DIRECTORY) // See below for notes on what happens when you unzip such an item. // Note: if you are opening the zip through a pipe, then random access // is not possible and GetZipItem(-1) fails and you can't discover the number // of items except by calling GetZipItem on each one of them in turn, // starting at 0, until eventually the call fails. Also, in the event that // you are opening through a pipe and the zip was itself created into a pipe, // then then comp_size and sometimes unc_size as well may not be known until // after the item has been unzipped. ZRESULT FindZipItem(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze); // FindZipItem - finds an item by name. ic means 'insensitive to case'. // It returns the index of the item, and returns information about it. // If nothing was found, then index is set to -1 and the function returns // an error code. ZRESULT UnzipItem(HZIP hz, int index, const TCHAR *fn); ZRESULT UnzipItem(HZIP hz, int index, void *z,unsigned int len); ZRESULT UnzipItemHandle(HZIP hz, int index, HANDLE h); // UnzipItem - given an index to an item, unzips it. You can unzip to: // to a pipe: UnzipItemHandle(hz,i, hpipe_write); // to a file (by handle): UnzipItemHandle(hz,i, hfile); // to a file (by name): UnzipItem(hz,i, ze.name); // to a memory block: UnzipItem(hz,i, buf,buflen); // In the final case, if the buffer isn't large enough to hold it all, // then the return code indicates that more is yet to come. If it was // large enough, and you want to know precisely how big, GetZipItem. // Note: zip files are normally stored with relative pathnames. If you // unzip with ZIP_FILENAME a relative pathname then the item gets created // relative to the current directory - it first ensures that all necessary // subdirectories have been created. Also, the item may itself be a directory. // If you unzip a directory with ZIP_FILENAME, then the directory gets created. // If you unzip it to a handle or a memory block, then nothing gets created // and it emits 0 bytes. ZRESULT SetUnzipBaseDir(HZIP hz, const TCHAR *dir); // if unzipping to a filename, and it's a relative filename, then it will be relative to here. // (defaults to current-directory). ZRESULT CloseZip(HZIP hz); // CloseZip - the zip handle must be closed with this function. unsigned int FormatZipMessage(ZRESULT code, TCHAR *buf,unsigned int len); // FormatZipMessage - given an error code, formats it as a string. // It returns the length of the error message. If buf/len points // to a real buffer, then it also writes as much as possible into there. // These are the result codes: #define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, #define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. // The following come from general system stuff (e.g. files not openable) #define ZR_GENMASK 0x0000FF00 #define ZR_NODUPH 0x00000100 // couldn't duplicate the handle #define ZR_NOFILE 0x00000200 // couldn't create/open the file #define ZR_NOALLOC 0x00000300 // failed to allocate some resource #define ZR_WRITE 0x00000400 // a general error writing to the file #define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip #define ZR_MORE 0x00000600 // there's still more data to be unzipped #define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile #define ZR_READ 0x00000800 // a general error reading the file #define ZR_PASSWORD 0x00001000 // we didn't get the right password to unzip the file // The following come from mistakes on the part of the caller #define ZR_CALLERMASK 0x00FF0000 #define ZR_ARGS 0x00010000 // general mistake with the arguments #define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't #define ZR_MEMSIZE 0x00030000 // the memory size is too small #define ZR_FAILED 0x00040000 // the thing was already failed when you called this function #define ZR_ENDED 0x00050000 // the zip creation has already been closed #define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken #define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped #define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip // The following come from bugs within the zip library itself #define ZR_BUGMASK 0xFF000000 #define ZR_NOTINITED 0x01000000 // initialisation didn't work #define ZR_SEEK 0x02000000 // trying to seek in an unseekable file #define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed #define ZR_FLATE 0x05000000 // an internal error in the de/inflation code // e.g. // // SetCurrentDirectory("c:\\docs\\stuff"); // HZIP hz = OpenZip("c:\\stuff.zip",0); // ZIPENTRY ze; GetZipItem(hz,-1,&ze); int numitems=ze.index; // for (int i=0; i #include CAppModule* _Module; typedef BOOL(WINAPI *SetDefaultDllDirectoriesFunction)(DWORD DirectoryFlags); // Some libraries are still loaded from the current directories. // If we pre-load them with an absolute path then we are good. static void PreloadLibs() { wchar_t sys32Folder[MAX_PATH]; GetSystemDirectory(sys32Folder, MAX_PATH); std::wstring version = (std::wstring(sys32Folder) + L"\\version.dll"); std::wstring logoncli = (std::wstring(sys32Folder) + L"\\logoncli.dll"); std::wstring sspicli = (std::wstring(sys32Folder) + L"\\sspicli.dll"); std::wstring urlmon = (std::wstring(sys32Folder) + L"\\urlmon.dll"); LoadLibrary(version.c_str()); LoadLibrary(logoncli.c_str()); LoadLibrary(sspicli.c_str()); LoadLibrary(urlmon.c_str()); } static void MitigateDllHijacking() { // Set the default DLL lookup directory to System32 for ourselves and kernel32.dll HMODULE hKernel32 = LoadLibrary(L"kernel32.dll"); if (hKernel32) { SetDefaultDllDirectoriesFunction pfn = (SetDefaultDllDirectoriesFunction)GetProcAddress(hKernel32, "SetDefaultDllDirectories"); if (pfn) { (*pfn)(LOAD_LIBRARY_SEARCH_SYSTEM32); } } PreloadLibs(); } int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { MitigateDllHijacking(); int exitCode = -1; CString cmdLine(lpCmdLine); if (cmdLine.Find(L"--checkInstall") >= 0) { // If we're already installed, exit as fast as possible if (!MachineInstaller::ShouldSilentInstall()) { return 0; } // Make sure update.exe gets silent wcscat(lpCmdLine, L" --silent"); } HRESULT hr = ::CoInitialize(NULL); ATLASSERT(SUCCEEDED(hr)); AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES); _Module = new CAppModule(); hr = _Module->Init(NULL, hInstance); bool isQuiet = (cmdLine.Find(L"-s") >= 0); bool weAreUACElevated = CUpdateRunner::AreWeUACElevated() == S_OK; bool attemptingToRerun = (cmdLine.Find(L"--rerunningWithoutUAC") >= 0); if (weAreUACElevated && attemptingToRerun) { CUpdateRunner::DisplayErrorMessage(CString(L"Please re-run this installer as a normal user instead of \"Run as Administrator\"."), NULL); exitCode = E_FAIL; goto out; } if (!CFxHelper::CanInstallDotNet4_5()) { // Explain this as nicely as possible and give up. MessageBox(0L, L"This program cannot run on Windows XP or before; it requires a later version of Windows.", L"Incompatible Operating System", 0); exitCode = E_FAIL; goto out; } NetVersion requiredVersion = CFxHelper::GetRequiredDotNetVersion(); if (!CFxHelper::IsDotNetInstalled(requiredVersion)) { hr = CFxHelper::InstallDotNetFramework(requiredVersion, isQuiet); if (FAILED(hr)) { exitCode = hr; // #yolo CUpdateRunner::DisplayErrorMessage(CString(L"Failed to install the .NET Framework, try installing the latest version manually"), NULL); goto out; } // S_FALSE isn't failure, but we still shouldn't try to install if (hr != S_OK) { exitCode = 0; goto out; } } // If we're UAC-elevated, we shouldn't be because it will give us permissions // problems later. Just silently rerun ourselves. // (Skip this check in Wine, which always reports admin privileges) if (weAreUACElevated && CUpdateRunner::AreWeInWine() != S_OK) { wchar_t buf[4096]; HMODULE hMod = GetModuleHandle(NULL); GetModuleFileNameW(hMod, buf, 4096); wcscat(lpCmdLine, L" --rerunningWithoutUAC"); CUpdateRunner::ShellExecuteFromExplorer(buf, lpCmdLine); exitCode = 0; goto out; } exitCode = CUpdateRunner::ExtractUpdaterAndRun(lpCmdLine, false); out: _Module->Term(); return exitCode; } ================================================ FILE: src/Setup/wtl90/atlapp.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLAPP_H__ #define __ATLAPP_H__ #pragma once #ifndef __cplusplus #error WTL requires C++ compilation (use a .cpp suffix) #endif #ifndef __ATLBASE_H__ #error atlapp.h requires atlbase.h to be included first #endif #ifndef _WIN32_WCE #if (WINVER < 0x0400) #error WTL requires Windows version 4.0 or higher #endif #if (_WIN32_IE < 0x0300) #error WTL requires IE version 3.0 or higher #endif #endif #ifdef _ATL_NO_COMMODULE #error WTL requires that _ATL_NO_COMMODULE is not defined #endif #if (_ATL_VER >= 0x0900) && defined(_ATL_MIN_CRT) #error _ATL_MIN_CRT is not supported with ATL 9.0 and higher #endif #if defined(_WIN32_WCE) && defined(_ATL_MIN_CRT) #pragma message("Warning: WTL for Windows CE doesn't use _ATL_MIN_CRT") #endif #include #if !defined(_ATL_MIN_CRT) && defined(_MT) && !defined(_WIN32_WCE) #include // for _beginthreadex #endif #if (_ATL_VER < 0x0800) && !defined(_DEBUG) #include #endif #include #ifndef _WIN32_WCE #pragma comment(lib, "comctl32.lib") #endif #if defined(_SYSINFOAPI_H_) && defined(NOT_BUILD_WINDOWS_DEPRECATE) && (_WIN32_WINNT >= 0x0501) #include #endif #ifndef _WIN32_WCE #include "atlres.h" #else // CE specific #include "atlresce.h" #endif // _WIN32_WCE // We need to disable this warning because of template class arguments #pragma warning(disable: 4127) #if (_ATL_VER >= 0x0900) && !defined(_SECURE_ATL) #define _SECURE_ATL 1 #endif /////////////////////////////////////////////////////////////////////////////// // WTL version number #define _WTL_VER 0x0900 /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CMessageFilter // CIdleHandler // CMessageLoop // // CAppModule // CServerAppModule // // CRegKeyEx // // Global functions: // AtlGetDefaultGuiFont() // AtlCreateControlFont() // AtlCreateBoldFont() // AtlInitCommonControls() /////////////////////////////////////////////////////////////////////////////// // Global support for Windows CE #ifdef _WIN32_WCE #ifndef SW_SHOWDEFAULT #define SW_SHOWDEFAULT SW_SHOWNORMAL #endif // !SW_SHOWDEFAULT // These get's OR-ed in a constant and will have no effect. // Defining them reduces the number of #ifdefs required for CE. #define LR_DEFAULTSIZE 0 #define LR_LOADFROMFILE 0 #ifndef SM_CXCURSOR #define SM_CXCURSOR 13 #endif #ifndef SM_CYCURSOR #define SM_CYCURSOR 14 #endif inline BOOL IsMenu(HMENU hMenu) { MENUITEMINFO mii = { sizeof(MENUITEMINFO) }; ::SetLastError(0); BOOL bRet = ::GetMenuItemInfo(hMenu, 0, TRUE, &mii); if(!bRet) bRet = (::GetLastError() != ERROR_INVALID_MENU_HANDLE) ? TRUE : FALSE; return bRet; } #if (_WIN32_WCE >= 410) extern "C" void WINAPI ListView_SetItemSpacing(HWND hwndLV, int iHeight); #endif // (_WIN32_WCE >= 410) inline int MulDiv(IN int nNumber, IN int nNumerator, IN int nDenominator) { __int64 multiple = nNumber * nNumerator; return static_cast(multiple / nDenominator); } #if (_ATL_VER >= 0x0800) #ifndef _WTL_KEEP_WS_OVERLAPPEDWINDOW #ifdef WS_OVERLAPPEDWINDOW #undef WS_OVERLAPPEDWINDOW #define WS_OVERLAPPEDWINDOW 0 #endif // WS_OVERLAPPEDWINDOW #endif // !_WTL_KEEP_WS_OVERLAPPEDWINDOW #ifndef RDW_FRAME #define RDW_FRAME 0 #endif // !RDW_FRAME #ifndef WM_WINDOWPOSCHANGING #define WM_WINDOWPOSCHANGING 0 #endif // !WM_WINDOWPOSCHANGING #define FreeResource(x) #define UnlockResource(x) namespace ATL { inline HRESULT CComModule::RegisterClassObjects(DWORD /*dwClsContext*/, DWORD /*dwFlags*/) throw() { return E_NOTIMPL; } inline HRESULT CComModule::RevokeClassObjects() throw() { return E_NOTIMPL; } }; // namespace ATL #ifndef lstrlenW #define lstrlenW (int)ATL::lstrlenW #endif // lstrlenW inline int WINAPI lstrlenA(LPCSTR lpszString) { return ATL::lstrlenA(lpszString); } #ifdef lstrcpyn #undef lstrcpyn #define lstrcpyn ATL::lstrcpynW #endif // lstrcpyn #ifndef SetWindowLongPtrW inline LONG_PTR tmp_SetWindowLongPtrW( HWND hWnd, int nIndex, LONG_PTR dwNewLong ) { return( ::SetWindowLongW( hWnd, nIndex, LONG( dwNewLong ) ) ); } #define SetWindowLongPtrW tmp_SetWindowLongPtrW #endif #ifndef GetWindowLongPtrW inline LONG_PTR tmp_GetWindowLongPtrW( HWND hWnd, int nIndex ) { return( ::GetWindowLongW( hWnd, nIndex ) ); } #define GetWindowLongPtrW tmp_GetWindowLongPtrW #endif #ifndef LongToPtr #define LongToPtr(x) ((void*)x) #endif #ifndef PtrToInt #define PtrToInt( p ) ((INT)(INT_PTR) (p) ) #endif #else // !(_ATL_VER >= 0x0800) #ifdef lstrlenW #undef lstrlenW #define lstrlenW (int)::wcslen #endif // lstrlenW #define lstrlenA (int)strlen #ifndef lstrcpyn inline LPTSTR lstrcpyn(LPTSTR lpstrDest, LPCTSTR lpstrSrc, int nLength) { if(lpstrDest == NULL || lpstrSrc == NULL || nLength <= 0) return NULL; int nLen = __min(lstrlen(lpstrSrc), nLength - 1); LPTSTR lpstrRet = (LPTSTR)memcpy(lpstrDest, lpstrSrc, nLen * sizeof(TCHAR)); lpstrDest[nLen] = 0; return lpstrRet; } #endif // !lstrcpyn #ifndef lstrcpynW inline LPWSTR lstrcpynW(LPWSTR lpstrDest, LPCWSTR lpstrSrc, int nLength) { return lstrcpyn(lpstrDest, lpstrSrc, nLength); // WinCE is Unicode only } #endif // !lstrcpynW #ifndef lstrcpynA inline LPSTR lstrcpynA(LPSTR lpstrDest, LPCSTR lpstrSrc, int nLength) { if(lpstrDest == NULL || lpstrSrc == NULL || nLength <= 0) return NULL; int nLen = __min(lstrlenA(lpstrSrc), nLength - 1); LPSTR lpstrRet = (LPSTR)memcpy(lpstrDest, lpstrSrc, nLen * sizeof(char)); lpstrDest[nLen] = 0; return lpstrRet; } #endif // !lstrcpyn #ifdef TrackPopupMenu #undef TrackPopupMenu #endif // TrackPopupMenu #define DECLARE_WND_CLASS_EX(WndClassName, style, bkgnd) \ static CWndClassInfo& GetWndClassInfo() \ { \ static CWndClassInfo wc = \ { \ { style, StartWindowProc, \ 0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd + 1), NULL, WndClassName }, \ NULL, NULL, IDC_ARROW, TRUE, 0, _T("") \ }; \ return wc; \ } #ifndef _MAX_FNAME #define _MAX_FNAME _MAX_PATH #endif // _MAX_FNAME #if (_WIN32_WCE < 400) #define MAKEINTATOM(i) (LPTSTR)((ULONG_PTR)((WORD)(i))) #endif // (_WIN32_WCE < 400) #if (_WIN32_WCE < 410) #define WHEEL_PAGESCROLL (UINT_MAX) #define WHEEL_DELTA 120 #endif // (_WIN32_WCE < 410) #ifdef DrawIcon #undef DrawIcon #endif #ifndef VARCMP_LT #define VARCMP_LT 0 #endif #ifndef VARCMP_EQ #define VARCMP_EQ 1 #endif #ifndef VARCMP_GT #define VARCMP_GT 2 #endif #ifndef VARCMP_NULL #define VARCMP_NULL 3 #endif #ifndef RDW_ALLCHILDREN #define RDW_ALLCHILDREN 0 #endif #endif // !(_ATL_VER >= 0x0800) #endif // _WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // Global support for using original VC++ 6.0 headers with WTL #if (_MSC_VER < 1300) && !defined(_WIN32_WCE) #ifndef REG_QWORD #define REG_QWORD 11 #endif #ifndef BS_PUSHBOX #define BS_PUSHBOX 0x0000000AL #endif struct __declspec(uuid("000214e6-0000-0000-c000-000000000046")) IShellFolder; struct __declspec(uuid("000214f9-0000-0000-c000-000000000046")) IShellLinkW; struct __declspec(uuid("000214ee-0000-0000-c000-000000000046")) IShellLinkA; #endif // (_MSC_VER < 1300) && !defined(_WIN32_WCE) #ifndef _ATL_NO_OLD_HEADERS_WIN64 #if !defined(_WIN64) && (_ATL_VER < 0x0700) #ifndef PSM_INSERTPAGE #define PSM_INSERTPAGE (WM_USER + 119) #endif // !PSM_INSERTPAGE #ifndef GetClassLongPtr #define GetClassLongPtrA GetClassLongA #define GetClassLongPtrW GetClassLongW #ifdef UNICODE #define GetClassLongPtr GetClassLongPtrW #else #define GetClassLongPtr GetClassLongPtrA #endif // !UNICODE #endif // !GetClassLongPtr #ifndef GCLP_HICONSM #define GCLP_HICONSM (-34) #endif // !GCLP_HICONSM #ifndef GetWindowLongPtr #define GetWindowLongPtrA GetWindowLongA #define GetWindowLongPtrW GetWindowLongW #ifdef UNICODE #define GetWindowLongPtr GetWindowLongPtrW #else #define GetWindowLongPtr GetWindowLongPtrA #endif // !UNICODE #endif // !GetWindowLongPtr #ifndef SetWindowLongPtr #define SetWindowLongPtrA SetWindowLongA #define SetWindowLongPtrW SetWindowLongW #ifdef UNICODE #define SetWindowLongPtr SetWindowLongPtrW #else #define SetWindowLongPtr SetWindowLongPtrA #endif // !UNICODE #endif // !SetWindowLongPtr #ifndef GWLP_WNDPROC #define GWLP_WNDPROC (-4) #endif #ifndef GWLP_HINSTANCE #define GWLP_HINSTANCE (-6) #endif #ifndef GWLP_HWNDPARENT #define GWLP_HWNDPARENT (-8) #endif #ifndef GWLP_USERDATA #define GWLP_USERDATA (-21) #endif #ifndef GWLP_ID #define GWLP_ID (-12) #endif #ifndef DWLP_MSGRESULT #define DWLP_MSGRESULT 0 #endif typedef long LONG_PTR; typedef unsigned long ULONG_PTR; typedef ULONG_PTR DWORD_PTR; #ifndef HandleToUlong #define HandleToUlong( h ) ((ULONG)(ULONG_PTR)(h) ) #endif #ifndef HandleToLong #define HandleToLong( h ) ((LONG)(LONG_PTR) (h) ) #endif #ifndef LongToHandle #define LongToHandle( h) ((HANDLE)(LONG_PTR) (h)) #endif #ifndef PtrToUlong #define PtrToUlong( p ) ((ULONG)(ULONG_PTR) (p) ) #endif #ifndef PtrToLong #define PtrToLong( p ) ((LONG)(LONG_PTR) (p) ) #endif #ifndef PtrToUint #define PtrToUint( p ) ((UINT)(UINT_PTR) (p) ) #endif #ifndef PtrToInt #define PtrToInt( p ) ((INT)(INT_PTR) (p) ) #endif #ifndef PtrToUshort #define PtrToUshort( p ) ((unsigned short)(ULONG_PTR)(p) ) #endif #ifndef PtrToShort #define PtrToShort( p ) ((short)(LONG_PTR)(p) ) #endif #ifndef IntToPtr #define IntToPtr( i ) ((VOID *)(INT_PTR)((int)i)) #endif #ifndef UIntToPtr #define UIntToPtr( ui ) ((VOID *)(UINT_PTR)((unsigned int)ui)) #endif #ifndef LongToPtr #define LongToPtr( l ) ((VOID *)(LONG_PTR)((long)l)) #endif #ifndef ULongToPtr #define ULongToPtr( ul ) ((VOID *)(ULONG_PTR)((unsigned long)ul)) #endif #endif // !defined(_WIN64) && (_ATL_VER < 0x0700) #endif // !_ATL_NO_OLD_HEADERS_WIN64 /////////////////////////////////////////////////////////////////////////////// // Global support for using original VC++ 7.x headers with WTL #if (_MSC_VER >= 1300) && (_MSC_VER < 1400) #ifndef BS_PUSHBOX #define BS_PUSHBOX 0x0000000AL #endif #pragma warning(disable: 4244) // conversion from 'type1' to 'type2', possible loss of data #endif // (_MSC_VER >= 1300) && (_MSC_VER < 1400) /////////////////////////////////////////////////////////////////////////////// // Global support for old SDK headers #ifndef BTNS_BUTTON #define BTNS_BUTTON TBSTYLE_BUTTON #endif #ifndef BTNS_SEP #define BTNS_SEP TBSTYLE_SEP #endif #ifndef BTNS_CHECK #define BTNS_CHECK TBSTYLE_CHECK #endif #ifndef BTNS_GROUP #define BTNS_GROUP TBSTYLE_GROUP #endif #ifndef BTNS_CHECKGROUP #define BTNS_CHECKGROUP TBSTYLE_CHECKGROUP #endif #if (_WIN32_IE >= 0x0300) #ifndef BTNS_DROPDOWN #define BTNS_DROPDOWN TBSTYLE_DROPDOWN #endif #endif #if (_WIN32_IE >= 0x0400) #ifndef BTNS_AUTOSIZE #define BTNS_AUTOSIZE TBSTYLE_AUTOSIZE #endif #ifndef BTNS_NOPREFIX #define BTNS_NOPREFIX TBSTYLE_NOPREFIX #endif #endif /////////////////////////////////////////////////////////////////////////////// // Global support for SecureHelper functions #ifndef _TRUNCATE #define _TRUNCATE ((size_t)-1) #endif #ifndef _ERRCODE_DEFINED #define _ERRCODE_DEFINED typedef int errno_t; #endif #ifndef _SECURECRT_ERRCODE_VALUES_DEFINED #define _SECURECRT_ERRCODE_VALUES_DEFINED #define EINVAL 22 #define STRUNCATE 80 #endif #ifndef _countof #define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0])) #endif /////////////////////////////////////////////////////////////////////////////// // Miscellaneous global support // define useful macros from winuser.h #ifndef IS_INTRESOURCE #define IS_INTRESOURCE(_r) (((ULONG_PTR)(_r) >> 16) == 0) #endif // IS_INTRESOURCE // protect template members from windowsx.h macros #ifdef _INC_WINDOWSX #undef SubclassWindow #endif // _INC_WINDOWSX // define useful macros from windowsx.h #ifndef GET_X_LPARAM #define GET_X_LPARAM(lParam) ((int)(short)LOWORD(lParam)) #endif #ifndef GET_Y_LPARAM #define GET_Y_LPARAM(lParam) ((int)(short)HIWORD(lParam)) #endif // Dummy structs for compiling with /CLR #if (_MSC_VER >= 1300) && defined(_MANAGED) __if_not_exists(_IMAGELIST::_IMAGELIST) { struct _IMAGELIST { }; } __if_not_exists(_TREEITEM::_TREEITEM) { struct _TREEITEM { }; } __if_not_exists(_PSP::_PSP) { struct _PSP { }; } #endif // Define ATLVERIFY macro for ATL3 #if (_ATL_VER < 0x0700) #ifndef ATLVERIFY #ifdef _DEBUG #define ATLVERIFY(expr) ATLASSERT(expr) #else #define ATLVERIFY(expr) (expr) #endif // DEBUG #endif // ATLVERIFY #endif // (_ATL_VER < 0x0700) // Forward declaration for ATL3 and ATL11 fix #if (((_ATL_VER < 0x0700) && defined(_ATL_DLL)) || (_ATL_VER >= 0x0B00)) && !defined(_WIN32_WCE) namespace ATL { HRESULT AtlGetCommCtrlVersion(LPDWORD pdwMajor, LPDWORD pdwMinor); }; #endif #ifndef WM_MOUSEHWHEEL #define WM_MOUSEHWHEEL 0x020E #endif namespace WTL { #if (_ATL_VER >= 0x0700) DECLARE_TRACE_CATEGORY(atlTraceUI); #ifdef _DEBUG __declspec(selectany) ATL::CTraceCategory atlTraceUI(_T("atlTraceUI")); #endif // _DEBUG #else // !(_ATL_VER >= 0x0700) enum wtlTraceFlags { atlTraceUI = 0x10000000 }; #endif // !(_ATL_VER >= 0x0700) // Windows version helper inline bool AtlIsOldWindows() { #ifdef _versionhelpers_H_INCLUDED_ return !::IsWindowsVersionOrGreater(4, 90, 0); #else // !_versionhelpers_H_INCLUDED_ OSVERSIONINFO ovi = { sizeof(OSVERSIONINFO) }; BOOL bRet = ::GetVersionEx(&ovi); return (!bRet || !((ovi.dwMajorVersion >= 5) || (ovi.dwMajorVersion == 4 && ovi.dwMinorVersion >= 90))); #endif // _versionhelpers_H_INCLUDED_ } // Default GUI font helper - "MS Shell Dlg" stock font inline HFONT AtlGetDefaultGuiFont() { #ifndef _WIN32_WCE return (HFONT)::GetStockObject(DEFAULT_GUI_FONT); #else // CE specific return (HFONT)::GetStockObject(SYSTEM_FONT); #endif // _WIN32_WCE } // Control font helper - default font for controls not in a dialog // (NOTE: Caller owns the font, and should destroy it when it's no longer needed) inline HFONT AtlCreateControlFont() { #ifndef _WIN32_WCE LOGFONT lf = { 0 }; ATLVERIFY(::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0) != FALSE); HFONT hFont = ::CreateFontIndirect(&lf); ATLASSERT(hFont != NULL); return hFont; #else // CE specific return (HFONT)::GetStockObject(SYSTEM_FONT); #endif // _WIN32_WCE } // Bold font helper // (NOTE: Caller owns the font, and should destroy it when it's no longer needed) inline HFONT AtlCreateBoldFont(HFONT hFont = NULL) { LOGFONT lf = { 0 }; #ifndef _WIN32_WCE if(hFont == NULL) ATLVERIFY(::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0) != FALSE); else ATLVERIFY(::GetObject(hFont, sizeof(LOGFONT), &lf) == sizeof(LOGFONT)); #else // CE specific if(hFont == NULL) hFont = (HFONT)::GetStockObject(SYSTEM_FONT); ATLVERIFY(::GetObject(hFont, sizeof(LOGFONT), &lf) == sizeof(LOGFONT)); #endif // _WIN32_WCE lf.lfWeight = FW_BOLD; HFONT hFontBold = ::CreateFontIndirect(&lf); ATLASSERT(hFontBold != NULL); return hFontBold; } // Common Controls initialization helper inline BOOL AtlInitCommonControls(DWORD dwFlags) { INITCOMMONCONTROLSEX iccx = { sizeof(INITCOMMONCONTROLSEX), dwFlags }; BOOL bRet = ::InitCommonControlsEx(&iccx); ATLASSERT(bRet); return bRet; } /////////////////////////////////////////////////////////////////////////////// // RunTimeHelper - helper functions for Windows version and structure sizes // Not for Windows CE #if defined(_WIN32_WCE) && !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) #define _WTL_NO_RUNTIME_STRUCT_SIZE #endif #ifndef _WTL_NO_RUNTIME_STRUCT_SIZE #ifndef _SIZEOF_STRUCT #define _SIZEOF_STRUCT(structname, member) (((int)((LPBYTE)(&((structname*)0)->member) - ((LPBYTE)((structname*)0)))) + sizeof(((structname*)0)->member)) #endif #if (_WIN32_WINNT >= 0x0600) && !defined(REBARBANDINFO_V6_SIZE) #define REBARBANDINFO_V6_SIZE _SIZEOF_STRUCT(REBARBANDINFO, cxHeader) #endif // (_WIN32_WINNT >= 0x0600) && !defined(REBARBANDINFO_V6_SIZE) #if (_WIN32_WINNT >= 0x0600) && !defined(LVGROUP_V5_SIZE) #define LVGROUP_V5_SIZE _SIZEOF_STRUCT(LVGROUP, uAlign) #endif // (_WIN32_WINNT >= 0x0600) && !defined(LVGROUP_V5_SIZE) #if (_WIN32_WINNT >= 0x0600) && !defined(LVTILEINFO_V5_SIZE) #define LVTILEINFO_V5_SIZE _SIZEOF_STRUCT(LVTILEINFO, puColumns) #endif // (_WIN32_WINNT >= 0x0600) && !defined(LVTILEINFO_V5_SIZE) #if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) && !defined(MCHITTESTINFO_V1_SIZE) #define MCHITTESTINFO_V1_SIZE _SIZEOF_STRUCT(MCHITTESTINFO, st) #endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) && !defined(MCHITTESTINFO_V1_SIZE) #if !defined(_WIN32_WCE) && (WINVER >= 0x0600) && !defined(NONCLIENTMETRICS_V1_SIZE) #define NONCLIENTMETRICS_V1_SIZE _SIZEOF_STRUCT(NONCLIENTMETRICS, lfMessageFont) #endif // !defined(_WIN32_WCE) && (WINVER >= 0x0600) && !defined(NONCLIENTMETRICS_V1_SIZE) #if !defined(_WIN32_WCE) && (_WIN32_WINNT >= 0x0501) && !defined(TTTOOLINFO_V2_SIZE) #define TTTOOLINFO_V2_SIZE _SIZEOF_STRUCT(TTTOOLINFO, lParam) #endif // !defined(_WIN32_WCE) && (_WIN32_WINNT >= 0x0501) && !defined(TTTOOLINFO_V2_SIZE) #endif // !_WTL_NO_RUNTIME_STRUCT_SIZE namespace RunTimeHelper { #ifndef _WIN32_WCE inline bool IsCommCtrl6() { DWORD dwMajor = 0, dwMinor = 0; HRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor); return (SUCCEEDED(hRet) && (dwMajor >= 6)); } inline bool IsVista() { #ifdef _versionhelpers_H_INCLUDED_ return ::IsWindowsVistaOrGreater(); #else // !_versionhelpers_H_INCLUDED_ OSVERSIONINFO ovi = { sizeof(OSVERSIONINFO) }; BOOL bRet = ::GetVersionEx(&ovi); return ((bRet != FALSE) && (ovi.dwMajorVersion >= 6)); #endif // _versionhelpers_H_INCLUDED_ } inline bool IsThemeAvailable() { bool bRet = false; if(IsCommCtrl6()) { HMODULE hThemeDLL = ::LoadLibrary(_T("uxtheme.dll")); if(hThemeDLL != NULL) { typedef BOOL (STDAPICALLTYPE *PFN_IsThemeActive)(); PFN_IsThemeActive pfnIsThemeActive = (PFN_IsThemeActive)::GetProcAddress(hThemeDLL, "IsThemeActive"); ATLASSERT(pfnIsThemeActive != NULL); bRet = (pfnIsThemeActive != NULL) && (pfnIsThemeActive() != FALSE); if(bRet) { typedef BOOL (STDAPICALLTYPE *PFN_IsAppThemed)(); PFN_IsAppThemed pfnIsAppThemed = (PFN_IsAppThemed)::GetProcAddress(hThemeDLL, "IsAppThemed"); ATLASSERT(pfnIsAppThemed != NULL); bRet = (pfnIsAppThemed != NULL) && (pfnIsAppThemed() != FALSE); } ::FreeLibrary(hThemeDLL); } } return bRet; } inline bool IsWin7() { #ifdef _versionhelpers_H_INCLUDED_ return ::IsWindows7OrGreater(); #else // !_versionhelpers_H_INCLUDED_ OSVERSIONINFO ovi = { sizeof(OSVERSIONINFO) }; BOOL bRet = ::GetVersionEx(&ovi); return ((bRet != FALSE) && (ovi.dwMajorVersion == 6) && (ovi.dwMinorVersion >= 1)); #endif // _versionhelpers_H_INCLUDED_ } inline bool IsRibbonUIAvailable() { static INT iRibbonUI = -1; #if defined(NTDDI_WIN7) && (NTDDI_VERSION >= NTDDI_WIN7) if (iRibbonUI == -1) { HMODULE hRibbonDLL = ::LoadLibrary(_T("propsys.dll")); if (hRibbonDLL != NULL) { const GUID CLSID_UIRibbonFramework = { 0x926749fa, 0x2615, 0x4987, { 0x88, 0x45, 0xc3, 0x3e, 0x65, 0xf2, 0xb9, 0x57 } }; // block - create instance { ATL::CComPtr pIUIFramework; iRibbonUI = SUCCEEDED(pIUIFramework.CoCreateInstance(CLSID_UIRibbonFramework)) ? 1 : 0; } ::FreeLibrary(hRibbonDLL); } else { iRibbonUI = 0; } } #endif // defined(NTDDI_WIN7) && (NTDDI_VERSION >= NTDDI_WIN7) return (iRibbonUI == 1); } #endif // !_WIN32_WCE inline int SizeOf_REBARBANDINFO() { int nSize = sizeof(REBARBANDINFO); #if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600) if(!(IsVista() && IsCommCtrl6())) nSize = REBARBANDINFO_V6_SIZE; #endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600) return nSize; } #if (_WIN32_WINNT >= 0x501) inline int SizeOf_LVGROUP() { int nSize = sizeof(LVGROUP); #if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600) if(!IsVista()) nSize = LVGROUP_V5_SIZE; #endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600) return nSize; } inline int SizeOf_LVTILEINFO() { int nSize = sizeof(LVTILEINFO); #if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600) if(!IsVista()) nSize = LVTILEINFO_V5_SIZE; #endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0600) return nSize; } #endif // (_WIN32_WINNT >= 0x501) inline int SizeOf_MCHITTESTINFO() { int nSize = sizeof(MCHITTESTINFO); #if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) if(!(IsVista() && IsCommCtrl6())) nSize = MCHITTESTINFO_V1_SIZE; #endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) return nSize; } #ifndef _WIN32_WCE inline int SizeOf_NONCLIENTMETRICS() { int nSize = sizeof(NONCLIENTMETRICS); #if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (WINVER >= 0x0600) if(!IsVista()) nSize = NONCLIENTMETRICS_V1_SIZE; #endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (WINVER >= 0x0600) return nSize; } inline int SizeOf_TOOLINFO() { int nSize = sizeof(TOOLINFO); #if !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0501) if(!IsVista()) nSize = TTTOOLINFO_V2_SIZE; #endif // !defined(_WTL_NO_RUNTIME_STRUCT_SIZE) && (_WIN32_WINNT >= 0x0501) return nSize; } #endif // !_WIN32_WCE }; /////////////////////////////////////////////////////////////////////////////// // ModuleHelper - helper functions for ATL3 and ATL7 module classes namespace ModuleHelper { inline HINSTANCE GetModuleInstance() { #if (_ATL_VER >= 0x0700) return ATL::_AtlBaseModule.GetModuleInstance(); #else // !(_ATL_VER >= 0x0700) return ATL::_pModule->GetModuleInstance(); #endif // !(_ATL_VER >= 0x0700) } inline HINSTANCE GetResourceInstance() { #if (_ATL_VER >= 0x0700) return ATL::_AtlBaseModule.GetResourceInstance(); #else // !(_ATL_VER >= 0x0700) return ATL::_pModule->GetResourceInstance(); #endif // !(_ATL_VER >= 0x0700) } inline void AddCreateWndData(ATL::_AtlCreateWndData* pData, void* pObject) { #if (_ATL_VER >= 0x0700) ATL::_AtlWinModule.AddCreateWndData(pData, pObject); #else // !(_ATL_VER >= 0x0700) ATL::_pModule->AddCreateWndData(pData, pObject); #endif // !(_ATL_VER >= 0x0700) } inline void* ExtractCreateWndData() { #if (_ATL_VER >= 0x0700) return ATL::_AtlWinModule.ExtractCreateWndData(); #else // !(_ATL_VER >= 0x0700) return ATL::_pModule->ExtractCreateWndData(); #endif // !(_ATL_VER >= 0x0700) } }; /////////////////////////////////////////////////////////////////////////////// // SecureHelper - helper functions for VS2005 secure CRT namespace SecureHelper { inline void strcpyA_x(char* lpstrDest, size_t cchDest, const char* lpstrSrc) { #if _SECURE_ATL ATL::Checked::strcpy_s(lpstrDest, cchDest, lpstrSrc); #else if(cchDest > (size_t)lstrlenA(lpstrSrc)) ATLVERIFY(lstrcpyA(lpstrDest, lpstrSrc) != NULL); else ATLASSERT(FALSE); #endif } inline void strcpyW_x(wchar_t* lpstrDest, size_t cchDest, const wchar_t* lpstrSrc) { #if _SECURE_ATL ATL::Checked::wcscpy_s(lpstrDest, cchDest, lpstrSrc); #else if(cchDest > (size_t)lstrlenW(lpstrSrc)) ATLVERIFY(lstrcpyW(lpstrDest, lpstrSrc) != NULL); else ATLASSERT(FALSE); #endif } inline void strcpy_x(LPTSTR lpstrDest, size_t cchDest, LPCTSTR lpstrSrc) { #ifdef _UNICODE strcpyW_x(lpstrDest, cchDest, lpstrSrc); #else strcpyA_x(lpstrDest, cchDest, lpstrSrc); #endif } inline errno_t strncpyA_x(char* lpstrDest, size_t cchDest, const char* lpstrSrc, size_t cchCount) { #if _SECURE_ATL return ATL::Checked::strncpy_s(lpstrDest, cchDest, lpstrSrc, cchCount); #else errno_t nRet = 0; if(lpstrDest == NULL || cchDest == 0 || lpstrSrc == NULL) { nRet = EINVAL; } else if(cchCount == _TRUNCATE) { cchCount = __min(cchDest - 1, size_t(lstrlenA(lpstrSrc))); nRet = STRUNCATE; } else if(cchDest <= cchCount) { lpstrDest[0] = 0; nRet = EINVAL; } if(nRet == 0 || nRet == STRUNCATE) nRet = (lstrcpynA(lpstrDest, lpstrSrc, (int)cchCount + 1) != NULL) ? nRet : EINVAL; ATLASSERT(nRet == 0 || nRet == STRUNCATE); return nRet; #endif } inline errno_t strncpyW_x(wchar_t* lpstrDest, size_t cchDest, const wchar_t* lpstrSrc, size_t cchCount) { #if _SECURE_ATL return ATL::Checked::wcsncpy_s(lpstrDest, cchDest, lpstrSrc, cchCount); #else errno_t nRet = 0; if(lpstrDest == NULL || cchDest == 0 || lpstrSrc == NULL) { nRet = EINVAL; } else if(cchCount == _TRUNCATE) { cchCount = __min(cchDest - 1, size_t(lstrlenW(lpstrSrc))); nRet = STRUNCATE; } else if(cchDest <= cchCount) { lpstrDest[0] = 0; nRet = EINVAL; } if(nRet == 0 || nRet == STRUNCATE) nRet = (lstrcpynW(lpstrDest, lpstrSrc, (int)cchCount + 1) != NULL) ? nRet : EINVAL; ATLASSERT(nRet == 0 || nRet == STRUNCATE); return nRet; #endif } inline errno_t strncpy_x(LPTSTR lpstrDest, size_t cchDest, LPCTSTR lpstrSrc, size_t cchCount) { #ifdef _UNICODE return strncpyW_x(lpstrDest, cchDest, lpstrSrc, cchCount); #else return strncpyA_x(lpstrDest, cchDest, lpstrSrc, cchCount); #endif } inline void strcatA_x(char* lpstrDest, size_t cchDest, const char* lpstrSrc) { #if _SECURE_ATL ATL::Checked::strcat_s(lpstrDest, cchDest, lpstrSrc); #else if(cchDest > (size_t)lstrlenA(lpstrSrc)) ATLVERIFY(lstrcatA(lpstrDest, lpstrSrc) != NULL); else ATLASSERT(FALSE); #endif } inline void strcatW_x(wchar_t* lpstrDest, size_t cchDest, const wchar_t* lpstrSrc) { #if _SECURE_ATL ATL::Checked::wcscat_s(lpstrDest, cchDest, lpstrSrc); #else if(cchDest > (size_t)lstrlenW(lpstrSrc)) ATLVERIFY(lstrcatW(lpstrDest, lpstrSrc) != NULL); else ATLASSERT(FALSE); #endif } inline void strcat_x(LPTSTR lpstrDest, size_t cchDest, LPCTSTR lpstrSrc) { #ifdef _UNICODE strcatW_x(lpstrDest, cchDest, lpstrSrc); #else strcatA_x(lpstrDest, cchDest, lpstrSrc); #endif } inline void memcpy_x(void* pDest, size_t cbDest, const void* pSrc, size_t cbSrc) { #if _SECURE_ATL ATL::Checked::memcpy_s(pDest, cbDest, pSrc, cbSrc); #else if(cbDest >= cbSrc) memcpy(pDest, pSrc, cbSrc); else ATLASSERT(FALSE); #endif } inline void memmove_x(void* pDest, size_t cbDest, const void* pSrc, size_t cbSrc) { #if _SECURE_ATL ATL::Checked::memmove_s(pDest, cbDest, pSrc, cbSrc); #else if(cbDest >= cbSrc) memmove(pDest, pSrc, cbSrc); else ATLASSERT(FALSE); #endif } inline int vsprintf_x(LPTSTR lpstrBuff, size_t cchBuff, LPCTSTR lpstrFormat, va_list args) { #if _SECURE_ATL && !defined(_ATL_MIN_CRT) && !defined(_WIN32_WCE) return _vstprintf_s(lpstrBuff, cchBuff, lpstrFormat, args); #else cchBuff; // Avoid unused argument warning #pragma warning(push) #pragma warning(disable: 4996) return _vstprintf(lpstrBuff, lpstrFormat, args); #pragma warning(pop) #endif } inline int wvsprintf_x(LPTSTR lpstrBuff, size_t cchBuff, LPCTSTR lpstrFormat, va_list args) { #if _SECURE_ATL && !defined(_ATL_MIN_CRT) && !defined(_WIN32_WCE) return _vstprintf_s(lpstrBuff, cchBuff, lpstrFormat, args); #else cchBuff; // Avoid unused argument warning return ::wvsprintf(lpstrBuff, lpstrFormat, args); #endif } inline int sprintf_x(LPTSTR lpstrBuff, size_t cchBuff, LPCTSTR lpstrFormat, ...) { va_list args; va_start(args, lpstrFormat); int nRes = vsprintf_x(lpstrBuff, cchBuff, lpstrFormat, args); va_end(args); return nRes; } inline int wsprintf_x(LPTSTR lpstrBuff, size_t cchBuff, LPCTSTR lpstrFormat, ...) { va_list args; va_start(args, lpstrFormat); int nRes = wvsprintf_x(lpstrBuff, cchBuff, lpstrFormat, args); va_end(args); return nRes; } }; // namespace SecureHelper /////////////////////////////////////////////////////////////////////////////// // MinCrtHelper - helper functions for using _ATL_MIN_CRT namespace MinCrtHelper { inline int _isspace(TCHAR ch) { #ifndef _ATL_MIN_CRT return _istspace(ch); #else // _ATL_MIN_CRT WORD type = 0; ::GetStringTypeEx(::GetThreadLocale(), CT_CTYPE1, &ch, 1, &type); return (type & C1_SPACE) == C1_SPACE; #endif // _ATL_MIN_CRT } inline int _isdigit(TCHAR ch) { #ifndef _ATL_MIN_CRT return _istdigit(ch); #else // _ATL_MIN_CRT WORD type = 0; ::GetStringTypeEx(::GetThreadLocale(), CT_CTYPE1, &ch, 1, &type); return (type & C1_DIGIT) == C1_DIGIT; #endif // _ATL_MIN_CRT } inline int _atoi(LPCTSTR str) { #ifndef _ATL_MIN_CRT return _ttoi(str); #else // _ATL_MIN_CRT while(_isspace(*str) != 0) ++str; TCHAR ch = *str++; TCHAR sign = ch; // save sign indication if(ch == _T('-') || ch == _T('+')) ch = *str++; // skip sign int total = 0; while(_isdigit(ch) != 0) { total = 10 * total + (ch - '0'); // accumulate digit ch = *str++; // get next char } return (sign == '-') ? -total : total; // return result, negated if necessary #endif // _ATL_MIN_CRT } inline LPCTSTR _strrchr(LPCTSTR str, TCHAR ch) { #ifndef _ATL_MIN_CRT return _tcsrchr(str, ch); #else // _ATL_MIN_CRT LPCTSTR lpsz = NULL; while(*str != 0) { if(*str == ch) lpsz = str; str = ::CharNext(str); } return lpsz; #endif // _ATL_MIN_CRT } inline LPTSTR _strrchr(LPTSTR str, TCHAR ch) { #ifndef _ATL_MIN_CRT return _tcsrchr(str, ch); #else // _ATL_MIN_CRT LPTSTR lpsz = NULL; while(*str != 0) { if(*str == ch) lpsz = str; str = ::CharNext(str); } return lpsz; #endif // _ATL_MIN_CRT } }; // namespace MinCrtHelper /////////////////////////////////////////////////////////////////////////////// // GenericWndClass - generic window class usable for subclassing // Use in dialog templates to specify a placeholder to be subclassed // Specify as a custom control with class name WTL_GenericWindow // Call Rregister() before creating dialog (for example, in WinMain) namespace GenericWndClass { inline LPCTSTR GetName() { return _T("WTL_GenericWindow"); } inline ATOM Register() { WNDCLASSEX wc = { sizeof(WNDCLASSEX) }; wc.lpfnWndProc = ::DefWindowProc; wc.hInstance = ModuleHelper::GetModuleInstance(); wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszClassName = GetName(); ATOM atom = ::RegisterClassEx(&wc); ATLASSERT(atom != 0); return atom; } inline BOOL Unregister() // only needed for DLLs or tmp use { return ::UnregisterClass(GetName(), ModuleHelper::GetModuleInstance()); } }; // namespace GenericWndClass /////////////////////////////////////////////////////////////////////////////// // CMessageFilter - Interface for message filter support class CMessageFilter { public: virtual BOOL PreTranslateMessage(MSG* pMsg) = 0; }; /////////////////////////////////////////////////////////////////////////////// // CIdleHandler - Interface for idle processing class CIdleHandler { public: virtual BOOL OnIdle() = 0; }; #ifndef _ATL_NO_OLD_NAMES // for compatilibility with old names only typedef CIdleHandler CUpdateUIObject; #define DoUpdate OnIdle #endif // !_ATL_NO_OLD_NAMES /////////////////////////////////////////////////////////////////////////////// // CMessageLoop - message loop implementation class CMessageLoop { public: ATL::CSimpleArray m_aMsgFilter; ATL::CSimpleArray m_aIdleHandler; MSG m_msg; // Message filter operations BOOL AddMessageFilter(CMessageFilter* pMessageFilter) { return m_aMsgFilter.Add(pMessageFilter); } BOOL RemoveMessageFilter(CMessageFilter* pMessageFilter) { return m_aMsgFilter.Remove(pMessageFilter); } // Idle handler operations BOOL AddIdleHandler(CIdleHandler* pIdleHandler) { return m_aIdleHandler.Add(pIdleHandler); } BOOL RemoveIdleHandler(CIdleHandler* pIdleHandler) { return m_aIdleHandler.Remove(pIdleHandler); } #ifndef _ATL_NO_OLD_NAMES // for compatilibility with old names only BOOL AddUpdateUI(CIdleHandler* pIdleHandler) { ATLTRACE2(atlTraceUI, 0, _T("CUpdateUIObject and AddUpdateUI are deprecated. Please change your code to use CIdleHandler and OnIdle\n")); return AddIdleHandler(pIdleHandler); } BOOL RemoveUpdateUI(CIdleHandler* pIdleHandler) { ATLTRACE2(atlTraceUI, 0, _T("CUpdateUIObject and RemoveUpdateUI are deprecated. Please change your code to use CIdleHandler and OnIdle\n")); return RemoveIdleHandler(pIdleHandler); } #endif // !_ATL_NO_OLD_NAMES // message loop int Run() { BOOL bDoIdle = TRUE; int nIdleCount = 0; BOOL bRet; for(;;) { while(bDoIdle && !::PeekMessage(&m_msg, NULL, 0, 0, PM_NOREMOVE)) { if(!OnIdle(nIdleCount++)) bDoIdle = FALSE; } bRet = ::GetMessage(&m_msg, NULL, 0, 0); if(bRet == -1) { ATLTRACE2(atlTraceUI, 0, _T("::GetMessage returned -1 (error)\n")); continue; // error, don't process } else if(!bRet) { ATLTRACE2(atlTraceUI, 0, _T("CMessageLoop::Run - exiting\n")); break; // WM_QUIT, exit message loop } if(!PreTranslateMessage(&m_msg)) { ::TranslateMessage(&m_msg); ::DispatchMessage(&m_msg); } if(IsIdleMessage(&m_msg)) { bDoIdle = TRUE; nIdleCount = 0; } } return (int)m_msg.wParam; } static BOOL IsIdleMessage(MSG* pMsg) { // These messages should NOT cause idle processing switch(pMsg->message) { case WM_MOUSEMOVE: #ifndef _WIN32_WCE case WM_NCMOUSEMOVE: #endif // !_WIN32_WCE case WM_PAINT: case 0x0118: // WM_SYSTIMER (caret blink) return FALSE; } return TRUE; } // Overrideables // Override to change message filtering virtual BOOL PreTranslateMessage(MSG* pMsg) { // loop backwards for(int i = m_aMsgFilter.GetSize() - 1; i >= 0; i--) { CMessageFilter* pMessageFilter = m_aMsgFilter[i]; if(pMessageFilter != NULL && pMessageFilter->PreTranslateMessage(pMsg)) return TRUE; } return FALSE; // not translated } // override to change idle processing virtual BOOL OnIdle(int /*nIdleCount*/) { for(int i = 0; i < m_aIdleHandler.GetSize(); i++) { CIdleHandler* pIdleHandler = m_aIdleHandler[i]; if(pIdleHandler != NULL) pIdleHandler->OnIdle(); } return FALSE; // don't continue } }; /////////////////////////////////////////////////////////////////////////////// // CStaticDataInitCriticalSectionLock and CWindowCreateCriticalSectionLock // internal classes to manage critical sections for both ATL3 and ATL7 class CStaticDataInitCriticalSectionLock { public: #if (_ATL_VER >= 0x0700) ATL::CComCritSecLock m_cslock; CStaticDataInitCriticalSectionLock() : m_cslock(ATL::_pAtlModule->m_csStaticDataInitAndTypeInfo, false) { } #endif // (_ATL_VER >= 0x0700) HRESULT Lock() { #if (_ATL_VER >= 0x0700) return m_cslock.Lock(); #else // !(_ATL_VER >= 0x0700) ::EnterCriticalSection(&ATL::_pModule->m_csStaticDataInit); return S_OK; #endif // !(_ATL_VER >= 0x0700) } void Unlock() { #if (_ATL_VER >= 0x0700) m_cslock.Unlock(); #else // !(_ATL_VER >= 0x0700) ::LeaveCriticalSection(&ATL::_pModule->m_csStaticDataInit); #endif // !(_ATL_VER >= 0x0700) } }; class CWindowCreateCriticalSectionLock { public: #if (_ATL_VER >= 0x0700) ATL::CComCritSecLock m_cslock; CWindowCreateCriticalSectionLock() : m_cslock(ATL::_AtlWinModule.m_csWindowCreate, false) { } #endif // (_ATL_VER >= 0x0700) HRESULT Lock() { #if (_ATL_VER >= 0x0700) return m_cslock.Lock(); #else // !(_ATL_VER >= 0x0700) ::EnterCriticalSection(&ATL::_pModule->m_csWindowCreate); return S_OK; #endif // !(_ATL_VER >= 0x0700) } void Unlock() { #if (_ATL_VER >= 0x0700) m_cslock.Unlock(); #else // !(_ATL_VER >= 0x0700) ::LeaveCriticalSection(&ATL::_pModule->m_csWindowCreate); #endif // !(_ATL_VER >= 0x0700) } }; /////////////////////////////////////////////////////////////////////////////// // CTempBuffer - helper class for stack allocations for ATL3 #ifndef _WTL_STACK_ALLOC_THRESHOLD #define _WTL_STACK_ALLOC_THRESHOLD 512 #endif #if (_ATL_VER >= 0x0700) using ATL::CTempBuffer; #else // !(_ATL_VER >= 0x0700) #ifndef SIZE_MAX #ifdef _WIN64 #define SIZE_MAX _UI64_MAX #else #define SIZE_MAX UINT_MAX #endif #endif #pragma warning(push) #pragma warning(disable: 4284) // warning for operator -> template class CTempBuffer { public: CTempBuffer() : m_p(NULL) { } CTempBuffer(size_t nElements) : m_p(NULL) { Allocate(nElements); } ~CTempBuffer() { if(m_p != reinterpret_cast(m_abFixedBuffer)) free(m_p); } operator T*() const { return m_p; } T* operator ->() const { ATLASSERT(m_p != NULL); return m_p; } T* Allocate(size_t nElements) { ATLASSERT(nElements <= (SIZE_MAX / sizeof(T))); return AllocateBytes(nElements * sizeof(T)); } T* AllocateBytes(size_t nBytes) { ATLASSERT(m_p == NULL); if(nBytes > t_nFixedBytes) m_p = static_cast(malloc(nBytes)); else m_p = reinterpret_cast(m_abFixedBuffer); return m_p; } private: T* m_p; BYTE m_abFixedBuffer[t_nFixedBytes]; }; #pragma warning(pop) #endif // !(_ATL_VER >= 0x0700) /////////////////////////////////////////////////////////////////////////////// // CAppModule - module class for an application class CAppModule : public ATL::CComModule { public: DWORD m_dwMainThreadID; ATL::CSimpleMap* m_pMsgLoopMap; ATL::CSimpleArray* m_pSettingChangeNotify; // Overrides of CComModule::Init and Term HRESULT Init(ATL::_ATL_OBJMAP_ENTRY* pObjMap, HINSTANCE hInstance, const GUID* pLibID = NULL) { HRESULT hRet = CComModule::Init(pObjMap, hInstance, pLibID); if(FAILED(hRet)) return hRet; m_dwMainThreadID = ::GetCurrentThreadId(); typedef ATL::CSimpleMap _mapClass; m_pMsgLoopMap = NULL; ATLTRY(m_pMsgLoopMap = new _mapClass); if(m_pMsgLoopMap == NULL) return E_OUTOFMEMORY; m_pSettingChangeNotify = NULL; return hRet; } void Term() { TermSettingChangeNotify(); delete m_pMsgLoopMap; CComModule::Term(); } // Message loop map methods BOOL AddMessageLoop(CMessageLoop* pMsgLoop) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::AddMessageLoop.\n")); ATLASSERT(FALSE); return FALSE; } ATLASSERT(pMsgLoop != NULL); ATLASSERT(m_pMsgLoopMap->Lookup(::GetCurrentThreadId()) == NULL); // not in map yet BOOL bRet = m_pMsgLoopMap->Add(::GetCurrentThreadId(), pMsgLoop); lock.Unlock(); return bRet; } BOOL RemoveMessageLoop() { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::RemoveMessageLoop.\n")); ATLASSERT(FALSE); return FALSE; } BOOL bRet = m_pMsgLoopMap->Remove(::GetCurrentThreadId()); lock.Unlock(); return bRet; } CMessageLoop* GetMessageLoop(DWORD dwThreadID = ::GetCurrentThreadId()) const { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::GetMessageLoop.\n")); ATLASSERT(FALSE); return NULL; } CMessageLoop* pLoop = m_pMsgLoopMap->Lookup(dwThreadID); lock.Unlock(); return pLoop; } // Setting change notify methods // Note: Call this from the main thread for MSDI apps BOOL InitSettingChangeNotify(DLGPROC pfnDlgProc = _SettingChangeDlgProc) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::InitSettingChangeNotify.\n")); ATLASSERT(FALSE); return FALSE; } if(m_pSettingChangeNotify == NULL) { typedef ATL::CSimpleArray _notifyClass; ATLTRY(m_pSettingChangeNotify = new _notifyClass); ATLASSERT(m_pSettingChangeNotify != NULL); } BOOL bRet = (m_pSettingChangeNotify != NULL); if(bRet && m_pSettingChangeNotify->GetSize() == 0) { // init everything _ATL_EMPTY_DLGTEMPLATE templ; HWND hNtfWnd = ::CreateDialogIndirect(GetModuleInstance(), &templ, NULL, pfnDlgProc); ATLASSERT(::IsWindow(hNtfWnd)); if(::IsWindow(hNtfWnd)) { // need conditional code because types don't match in winuser.h #ifdef _WIN64 ::SetWindowLongPtr(hNtfWnd, GWLP_USERDATA, (LONG_PTR)this); #else ::SetWindowLongPtr(hNtfWnd, GWLP_USERDATA, PtrToLong(this)); #endif bRet = m_pSettingChangeNotify->Add(hNtfWnd); } else { bRet = FALSE; } } lock.Unlock(); return bRet; } void TermSettingChangeNotify() { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::TermSettingChangeNotify.\n")); ATLASSERT(FALSE); return; } if(m_pSettingChangeNotify != NULL && m_pSettingChangeNotify->GetSize() > 0) ::DestroyWindow((*m_pSettingChangeNotify)[0]); delete m_pSettingChangeNotify; m_pSettingChangeNotify = NULL; lock.Unlock(); } BOOL AddSettingChangeNotify(HWND hWnd) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::AddSettingChangeNotify.\n")); ATLASSERT(FALSE); return FALSE; } ATLASSERT(::IsWindow(hWnd)); BOOL bRet = FALSE; if(InitSettingChangeNotify() != FALSE) bRet = m_pSettingChangeNotify->Add(hWnd); lock.Unlock(); return bRet; } BOOL RemoveSettingChangeNotify(HWND hWnd) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CAppModule::RemoveSettingChangeNotify.\n")); ATLASSERT(FALSE); return FALSE; } BOOL bRet = FALSE; if(m_pSettingChangeNotify != NULL) bRet = m_pSettingChangeNotify->Remove(hWnd); lock.Unlock(); return bRet; } // Implementation - setting change notify dialog template and dialog procedure struct _ATL_EMPTY_DLGTEMPLATE : DLGTEMPLATE { _ATL_EMPTY_DLGTEMPLATE() { memset(this, 0, sizeof(_ATL_EMPTY_DLGTEMPLATE)); style = WS_POPUP; } WORD wMenu, wClass, wTitle; }; #ifdef _WIN64 static INT_PTR CALLBACK _SettingChangeDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) #else static BOOL CALLBACK _SettingChangeDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) #endif { if(uMsg == WM_SETTINGCHANGE) { // need conditional code because types don't match in winuser.h #ifdef _WIN64 CAppModule* pModule = (CAppModule*)::GetWindowLongPtr(hWnd, GWLP_USERDATA); #else CAppModule* pModule = (CAppModule*)LongToPtr(::GetWindowLongPtr(hWnd, GWLP_USERDATA)); #endif ATLASSERT(pModule != NULL); ATLASSERT(pModule->m_pSettingChangeNotify != NULL); const UINT uTimeout = 1500; // ms for(int i = 1; i < pModule->m_pSettingChangeNotify->GetSize(); i++) { #if !defined(_WIN32_WCE) ::SendMessageTimeout((*pModule->m_pSettingChangeNotify)[i], uMsg, wParam, lParam, SMTO_ABORTIFHUNG, uTimeout, NULL); #elif(_WIN32_WCE >= 400) // CE specific ::SendMessageTimeout((*pModule->m_pSettingChangeNotify)[i], uMsg, wParam, lParam, SMTO_NORMAL, uTimeout, NULL); #else // _WIN32_WCE < 400 specific uTimeout; ::SendMessage((*pModule->m_pSettingChangeNotify)[i], uMsg, wParam, lParam); #endif } return TRUE; } return FALSE; } }; /////////////////////////////////////////////////////////////////////////////// // CServerAppModule - module class for a COM server application class CServerAppModule : public CAppModule { public: HANDLE m_hEventShutdown; bool m_bActivity; DWORD m_dwTimeOut; DWORD m_dwPause; // Override of CAppModule::Init HRESULT Init(ATL::_ATL_OBJMAP_ENTRY* pObjMap, HINSTANCE hInstance, const GUID* pLibID = NULL) { m_dwTimeOut = 5000; m_dwPause = 1000; return CAppModule::Init(pObjMap, hInstance, pLibID); } void Term() { if(m_hEventShutdown != NULL && ::CloseHandle(m_hEventShutdown)) m_hEventShutdown = NULL; CAppModule::Term(); } // COM Server methods LONG Unlock() { LONG lRet = CComModule::Unlock(); if(lRet == 0) { m_bActivity = true; ::SetEvent(m_hEventShutdown); // tell monitor that we transitioned to zero } return lRet; } void MonitorShutdown() { for(;;) { ::WaitForSingleObject(m_hEventShutdown, INFINITE); DWORD dwWait = 0; do { m_bActivity = false; dwWait = ::WaitForSingleObject(m_hEventShutdown, m_dwTimeOut); } while(dwWait == WAIT_OBJECT_0); // timed out if(!m_bActivity && m_nLockCnt == 0) // if no activity let's really bail { #if ((_WIN32_WINNT >= 0x0400 ) || defined(_WIN32_DCOM)) && defined(_ATL_FREE_THREADED) && !defined(_WIN32_WCE) ::CoSuspendClassObjects(); if(!m_bActivity && m_nLockCnt == 0) #endif break; } } // This handle should be valid now. If it isn't, // check if _Module.Term was called first (it shouldn't) if(::CloseHandle(m_hEventShutdown)) m_hEventShutdown = NULL; ::PostThreadMessage(m_dwMainThreadID, WM_QUIT, 0, 0); } bool StartMonitor() { m_hEventShutdown = ::CreateEvent(NULL, false, false, NULL); if(m_hEventShutdown == NULL) return false; DWORD dwThreadID = 0; #if !defined(_ATL_MIN_CRT) && defined(_MT) && !defined(_WIN32_WCE) HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, (UINT (WINAPI*)(void*))MonitorProc, this, 0, (UINT*)&dwThreadID); #else HANDLE hThread = ::CreateThread(NULL, 0, MonitorProc, this, 0, &dwThreadID); #endif bool bRet = (hThread != NULL); if(bRet) ::CloseHandle(hThread); return bRet; } static DWORD WINAPI MonitorProc(void* pv) { CServerAppModule* p = (CServerAppModule*)pv; p->MonitorShutdown(); return 0; } #if (_ATL_VER < 0x0700) // search for an occurence of string p2 in string p1 static LPCTSTR FindOneOf(LPCTSTR p1, LPCTSTR p2) { while(p1 != NULL && *p1 != NULL) { LPCTSTR p = p2; while(p != NULL && *p != NULL) { if(*p1 == *p) return ::CharNext(p1); p = ::CharNext(p); } p1 = ::CharNext(p1); } return NULL; } #endif // (_ATL_VER < 0x0700) }; /////////////////////////////////////////////////////////////////////////////// // CRegKeyEx - adds type-specific methods to ATL3 CRegKey #if (_ATL_VER < 0x0700) class CRegKeyEx : public ATL::CRegKey { public: // Constructors and operators CRegKeyEx(HKEY hKey = NULL) { m_hKey = hKey; } CRegKeyEx(CRegKeyEx& key) { Attach(key.Detach()); } CRegKeyEx& operator =(CRegKeyEx& key) { Close(); Attach(key.Detach()); return *this; } // Methods LONG SetValue(LPCTSTR pszValueName, DWORD dwType, const void* pValue, ULONG nBytes) { ATLASSERT(m_hKey != NULL); return ::RegSetValueEx(m_hKey, pszValueName, NULL, dwType, static_cast(pValue), nBytes); } LONG SetGUIDValue(LPCTSTR pszValueName, REFGUID guidValue) { ATLASSERT(m_hKey != NULL); OLECHAR szGUID[64] = { 0 }; ::StringFromGUID2(guidValue, szGUID, 64); USES_CONVERSION; LPCTSTR lpstr = OLE2CT(szGUID); #ifndef _UNICODE if(lpstr == NULL) return E_OUTOFMEMORY; #endif return SetStringValue(pszValueName, lpstr); } LONG SetBinaryValue(LPCTSTR pszValueName, const void* pValue, ULONG nBytes) { ATLASSERT(m_hKey != NULL); return ::RegSetValueEx(m_hKey, pszValueName, NULL, REG_BINARY, reinterpret_cast(pValue), nBytes); } LONG SetDWORDValue(LPCTSTR pszValueName, DWORD dwValue) { ATLASSERT(m_hKey != NULL); return ::RegSetValueEx(m_hKey, pszValueName, NULL, REG_DWORD, reinterpret_cast(&dwValue), sizeof(DWORD)); } #ifndef _WIN32_WCE LONG SetQWORDValue(LPCTSTR pszValueName, ULONGLONG qwValue) { ATLASSERT(m_hKey != NULL); return ::RegSetValueEx(m_hKey, pszValueName, NULL, REG_QWORD, reinterpret_cast(&qwValue), sizeof(ULONGLONG)); } #endif LONG SetStringValue(LPCTSTR pszValueName, LPCTSTR pszValue, DWORD dwType = REG_SZ) { ATLASSERT(m_hKey != NULL); if(pszValue == NULL) { ATLASSERT(FALSE); return ERROR_INVALID_DATA; } ATLASSERT((dwType == REG_SZ) || (dwType == REG_EXPAND_SZ)); return ::RegSetValueEx(m_hKey, pszValueName, NULL, dwType, reinterpret_cast(pszValue), (lstrlen(pszValue) + 1) * sizeof(TCHAR)); } LONG SetMultiStringValue(LPCTSTR pszValueName, LPCTSTR pszValue) { ATLASSERT(m_hKey != NULL); if(pszValue == NULL) { ATLASSERT(FALSE); return ERROR_INVALID_DATA; } ULONG nBytes = 0; ULONG nLength = 0; LPCTSTR pszTemp = pszValue; do { nLength = lstrlen(pszTemp) + 1; pszTemp += nLength; nBytes += nLength * sizeof(TCHAR); } while (nLength != 1); return ::RegSetValueEx(m_hKey, pszValueName, NULL, REG_MULTI_SZ, reinterpret_cast(pszValue), nBytes); } LONG QueryValue(LPCTSTR pszValueName, DWORD* pdwType, void* pData, ULONG* pnBytes) { ATLASSERT(m_hKey != NULL); return ::RegQueryValueEx(m_hKey, pszValueName, NULL, pdwType, static_cast(pData), pnBytes); } LONG QueryGUIDValue(LPCTSTR pszValueName, GUID& guidValue) { ATLASSERT(m_hKey != NULL); guidValue = GUID_NULL; TCHAR szGUID[64] = { 0 }; ULONG nCount = 64; LONG lRes = QueryStringValue(pszValueName, szGUID, &nCount); if (lRes != ERROR_SUCCESS) return lRes; if(szGUID[0] != _T('{')) return ERROR_INVALID_DATA; USES_CONVERSION; LPOLESTR lpstr = T2OLE(szGUID); #ifndef _UNICODE if(lpstr == NULL) return E_OUTOFMEMORY; #endif HRESULT hr = ::CLSIDFromString(lpstr, &guidValue); if (FAILED(hr)) return ERROR_INVALID_DATA; return ERROR_SUCCESS; } LONG QueryBinaryValue(LPCTSTR pszValueName, void* pValue, ULONG* pnBytes) { ATLASSERT(pnBytes != NULL); ATLASSERT(m_hKey != NULL); DWORD dwType = 0; LONG lRes = ::RegQueryValueEx(m_hKey, pszValueName, NULL, &dwType, reinterpret_cast(pValue), pnBytes); if (lRes != ERROR_SUCCESS) return lRes; if (dwType != REG_BINARY) return ERROR_INVALID_DATA; return ERROR_SUCCESS; } LONG QueryDWORDValue(LPCTSTR pszValueName, DWORD& dwValue) { ATLASSERT(m_hKey != NULL); ULONG nBytes = sizeof(DWORD); DWORD dwType = 0; LONG lRes = ::RegQueryValueEx(m_hKey, pszValueName, NULL, &dwType, reinterpret_cast(&dwValue), &nBytes); if (lRes != ERROR_SUCCESS) return lRes; if (dwType != REG_DWORD) return ERROR_INVALID_DATA; return ERROR_SUCCESS; } #ifndef _WIN32_WCE LONG QueryQWORDValue(LPCTSTR pszValueName, ULONGLONG& qwValue) { ATLASSERT(m_hKey != NULL); ULONG nBytes = sizeof(ULONGLONG); DWORD dwType = 0; LONG lRes = ::RegQueryValueEx(m_hKey, pszValueName, NULL, &dwType, reinterpret_cast(&qwValue), &nBytes); if (lRes != ERROR_SUCCESS) return lRes; if (dwType != REG_QWORD) return ERROR_INVALID_DATA; return ERROR_SUCCESS; } #endif LONG QueryStringValue(LPCTSTR pszValueName, LPTSTR pszValue, ULONG* pnChars) { ATLASSERT(m_hKey != NULL); ATLASSERT(pnChars != NULL); ULONG nBytes = (*pnChars) * sizeof(TCHAR); DWORD dwType = 0; *pnChars = 0; LONG lRes = ::RegQueryValueEx(m_hKey, pszValueName, NULL, &dwType, reinterpret_cast(pszValue), &nBytes); if (lRes != ERROR_SUCCESS) { return lRes; } if(dwType != REG_SZ && dwType != REG_EXPAND_SZ) { return ERROR_INVALID_DATA; } if (pszValue != NULL) { if(nBytes != 0) { if ((nBytes % sizeof(TCHAR) != 0) || (pszValue[nBytes / sizeof(TCHAR) -1] != 0)) return ERROR_INVALID_DATA; } else { pszValue[0] = _T('\0'); } } *pnChars = nBytes / sizeof(TCHAR); return ERROR_SUCCESS; } LONG QueryMultiStringValue(LPCTSTR pszValueName, LPTSTR pszValue, ULONG* pnChars) { ATLASSERT(m_hKey != NULL); ATLASSERT(pnChars != NULL); if (pszValue != NULL && *pnChars < 2) return ERROR_INSUFFICIENT_BUFFER; ULONG nBytes = (*pnChars) * sizeof(TCHAR); DWORD dwType = 0; *pnChars = 0; LONG lRes = ::RegQueryValueEx(m_hKey, pszValueName, NULL, &dwType, reinterpret_cast(pszValue), &nBytes); if (lRes != ERROR_SUCCESS) return lRes; if (dwType != REG_MULTI_SZ) return ERROR_INVALID_DATA; if (pszValue != NULL && (nBytes % sizeof(TCHAR) != 0 || nBytes / sizeof(TCHAR) < 1 || pszValue[nBytes / sizeof(TCHAR) - 1] != 0 || ((nBytes / sizeof(TCHAR)) > 1 && pszValue[nBytes / sizeof(TCHAR) - 2] != 0))) return ERROR_INVALID_DATA; *pnChars = nBytes / sizeof(TCHAR); return ERROR_SUCCESS; } }; #else // !(_ATL_VER < 0x0700) typedef ATL::CRegKey CRegKeyEx; #endif // !(_ATL_VER < 0x0700) /////////////////////////////////////////////////////////////////////////////// // CString forward reference (enables CString use in atluser.h and atlgdi.h) #if defined(_WTL_FORWARD_DECLARE_CSTRING) && !defined(_WTL_USE_CSTRING) #define _WTL_USE_CSTRING #endif // defined(_WTL_FORWARD_DECLARE_CSTRING) && !defined(_WTL_USE_CSTRING) #ifdef _WTL_USE_CSTRING class CString; // forward declaration (include atlmisc.h for the whole class) #endif // _WTL_USE_CSTRING // CString namespace #ifndef _CSTRING_NS #ifdef __ATLSTR_H__ #define _CSTRING_NS ATL #else #define _CSTRING_NS WTL #endif #endif // _CSTRING_NS // Type classes namespace #ifndef _WTYPES_NS #ifdef __ATLTYPES_H__ #define _WTYPES_NS #else #define _WTYPES_NS WTL #endif #endif // _WTYPES_NS }; // namespace WTL /////////////////////////////////////////////////////////////////////////////// // General DLL version helpers // (ATL3: excluded from atlbase.h if _ATL_DLL is defined; ATL11: removed) #if (((_ATL_VER < 0x0700) && defined(_ATL_DLL)) || (_ATL_VER >= 0x0B00)) && !defined(_WIN32_WCE) namespace ATL { inline HRESULT AtlGetDllVersion(HINSTANCE hInstDLL, DLLVERSIONINFO* pDllVersionInfo) { ATLASSERT(pDllVersionInfo != NULL); if(pDllVersionInfo == NULL) return E_INVALIDARG; // We must get this function explicitly because some DLLs don't implement it. DLLGETVERSIONPROC pfnDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(hInstDLL, "DllGetVersion"); if(pfnDllGetVersion == NULL) return E_NOTIMPL; return (*pfnDllGetVersion)(pDllVersionInfo); } inline HRESULT AtlGetDllVersion(LPCTSTR lpstrDllName, DLLVERSIONINFO* pDllVersionInfo) { HINSTANCE hInstDLL = ::LoadLibrary(lpstrDllName); if(hInstDLL == NULL) return E_FAIL; HRESULT hRet = AtlGetDllVersion(hInstDLL, pDllVersionInfo); ::FreeLibrary(hInstDLL); return hRet; } // Common Control Versions: // Win95/WinNT 4.0 maj=4 min=00 // IE 3.x maj=4 min=70 // IE 4.0 maj=4 min=71 inline HRESULT AtlGetCommCtrlVersion(LPDWORD pdwMajor, LPDWORD pdwMinor) { ATLASSERT(pdwMajor != NULL && pdwMinor != NULL); if(pdwMajor == NULL || pdwMinor == NULL) return E_INVALIDARG; DLLVERSIONINFO dvi; ::ZeroMemory(&dvi, sizeof(dvi)); dvi.cbSize = sizeof(dvi); HRESULT hRet = AtlGetDllVersion(_T("comctl32.dll"), &dvi); if(SUCCEEDED(hRet)) { *pdwMajor = dvi.dwMajorVersion; *pdwMinor = dvi.dwMinorVersion; } else if(hRet == E_NOTIMPL) { // If DllGetVersion is not there, then the DLL is a version // previous to the one shipped with IE 3.x *pdwMajor = 4; *pdwMinor = 0; hRet = S_OK; } return hRet; } // Shell Versions: // Win95/WinNT 4.0 maj=4 min=00 // IE 3.x, IE 4.0 without Web Integrated Desktop maj=4 min=00 // IE 4.0 with Web Integrated Desktop maj=4 min=71 // IE 4.01 with Web Integrated Desktop maj=4 min=72 inline HRESULT AtlGetShellVersion(LPDWORD pdwMajor, LPDWORD pdwMinor) { ATLASSERT(pdwMajor != NULL && pdwMinor != NULL); if(pdwMajor == NULL || pdwMinor == NULL) return E_INVALIDARG; DLLVERSIONINFO dvi; ::ZeroMemory(&dvi, sizeof(dvi)); dvi.cbSize = sizeof(dvi); HRESULT hRet = AtlGetDllVersion(_T("shell32.dll"), &dvi); if(SUCCEEDED(hRet)) { *pdwMajor = dvi.dwMajorVersion; *pdwMinor = dvi.dwMinorVersion; } else if(hRet == E_NOTIMPL) { // If DllGetVersion is not there, then the DLL is a version // previous to the one shipped with IE 4.x *pdwMajor = 4; *pdwMinor = 0; hRet = S_OK; } return hRet; } }; // namespace ATL #endif // (_ATL_VER < 0x0700) && defined(_ATL_DLL) && !defined(_WIN32_WCE) // These are always included #include "atlwinx.h" #include "atluser.h" #include "atlgdi.h" #ifndef _WTL_NO_AUTOMATIC_NAMESPACE using namespace WTL; #endif // !_WTL_NO_AUTOMATIC_NAMESPACE #endif // __ATLAPP_H__ ================================================ FILE: src/Setup/wtl90/atlcrack.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLCRACK_H__ #define __ATLCRACK_H__ #pragma once #ifndef __ATLAPP_H__ #error atlcrack.h requires atlapp.h to be included first #endif /////////////////////////////////////////////////////////////////////////////// // Message map macro for cracked handlers // Note about message maps with cracked handlers: // For ATL 3.0, a message map using cracked handlers MUST use BEGIN_MSG_MAP_EX. // For ATL 7.0 or higher, you can use BEGIN_MSG_MAP for CWindowImpl/CDialogImpl derived classes, // but must use BEGIN_MSG_MAP_EX for classes that don't derive from CWindowImpl/CDialogImpl. #define BEGIN_MSG_MAP_EX(theClass) \ public: \ BOOL m_bMsgHandled; \ /* "handled" management for cracked handlers */ \ BOOL IsMsgHandled() const \ { \ return m_bMsgHandled; \ } \ void SetMsgHandled(BOOL bHandled) \ { \ m_bMsgHandled = bHandled; \ } \ BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) \ { \ BOOL bOldMsgHandled = m_bMsgHandled; \ BOOL bRet = _ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID); \ m_bMsgHandled = bOldMsgHandled; \ return bRet; \ } \ BOOL _ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID) \ { \ BOOL bHandled = TRUE; \ hWnd; \ uMsg; \ wParam; \ lParam; \ lResult; \ bHandled; \ switch(dwMsgMapID) \ { \ case 0: /////////////////////////////////////////////////////////////////////////////// // Standard Windows message macros // int OnCreate(LPCREATESTRUCT lpCreateStruct) #define MSG_WM_CREATE(func) \ if (uMsg == WM_CREATE) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((LPCREATESTRUCT)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam) #define MSG_WM_INITDIALOG(func) \ if (uMsg == WM_INITDIALOG) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HWND)wParam, lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // BOOL OnCopyData(CWindow wnd, PCOPYDATASTRUCT pCopyDataStruct) #define MSG_WM_COPYDATA(func) \ if (uMsg == WM_COPYDATA) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HWND)wParam, (PCOPYDATASTRUCT)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnDestroy() #define MSG_WM_DESTROY(func) \ if (uMsg == WM_DESTROY) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnMove(CPoint ptPos) #define MSG_WM_MOVE(func) \ if (uMsg == WM_MOVE) \ { \ SetMsgHandled(TRUE); \ func(_WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSize(UINT nType, CSize size) #define MSG_WM_SIZE(func) \ if (uMsg == WM_SIZE) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnActivate(UINT nState, BOOL bMinimized, CWindow wndOther) #define MSG_WM_ACTIVATE(func) \ if (uMsg == WM_ACTIVATE) \ { \ SetMsgHandled(TRUE); \ func((UINT)LOWORD(wParam), (BOOL)HIWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSetFocus(CWindow wndOld) #define MSG_WM_SETFOCUS(func) \ if (uMsg == WM_SETFOCUS) \ { \ SetMsgHandled(TRUE); \ func((HWND)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnKillFocus(CWindow wndFocus) #define MSG_WM_KILLFOCUS(func) \ if (uMsg == WM_KILLFOCUS) \ { \ SetMsgHandled(TRUE); \ func((HWND)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnEnable(BOOL bEnable) #define MSG_WM_ENABLE(func) \ if (uMsg == WM_ENABLE) \ { \ SetMsgHandled(TRUE); \ func((BOOL)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnPaint(CDCHandle dc) #define MSG_WM_PAINT(func) \ if (uMsg == WM_PAINT) \ { \ SetMsgHandled(TRUE); \ func((HDC)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnClose() #define MSG_WM_CLOSE(func) \ if (uMsg == WM_CLOSE) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // BOOL OnQueryEndSession(UINT nSource, UINT uLogOff) #define MSG_WM_QUERYENDSESSION(func) \ if (uMsg == WM_QUERYENDSESSION) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((UINT)wParam, (UINT)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // BOOL OnQueryOpen() #define MSG_WM_QUERYOPEN(func) \ if (uMsg == WM_QUERYOPEN) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func(); \ if(IsMsgHandled()) \ return TRUE; \ } // BOOL OnEraseBkgnd(CDCHandle dc) #define MSG_WM_ERASEBKGND(func) \ if (uMsg == WM_ERASEBKGND) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HDC)wParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSysColorChange() #define MSG_WM_SYSCOLORCHANGE(func) \ if (uMsg == WM_SYSCOLORCHANGE) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnEndSession(BOOL bEnding, UINT uLogOff) #define MSG_WM_ENDSESSION(func) \ if (uMsg == WM_ENDSESSION) \ { \ SetMsgHandled(TRUE); \ func((BOOL)wParam, (UINT)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnShowWindow(BOOL bShow, UINT nStatus) #define MSG_WM_SHOWWINDOW(func) \ if (uMsg == WM_SHOWWINDOW) \ { \ SetMsgHandled(TRUE); \ func((BOOL)wParam, (int)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // HBRUSH OnCtlColorEdit(CDCHandle dc, CEdit edit) #define MSG_WM_CTLCOLOREDIT(func) \ if (uMsg == WM_CTLCOLOREDIT) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // HBRUSH OnCtlColorListBox(CDCHandle dc, CListBox listBox) #define MSG_WM_CTLCOLORLISTBOX(func) \ if (uMsg == WM_CTLCOLORLISTBOX) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // HBRUSH OnCtlColorBtn(CDCHandle dc, CButton button) #define MSG_WM_CTLCOLORBTN(func) \ if (uMsg == WM_CTLCOLORBTN) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // HBRUSH OnCtlColorDlg(CDCHandle dc, CWindow wnd) #define MSG_WM_CTLCOLORDLG(func) \ if (uMsg == WM_CTLCOLORDLG) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // HBRUSH OnCtlColorScrollBar(CDCHandle dc, CScrollBar scrollBar) #define MSG_WM_CTLCOLORSCROLLBAR(func) \ if (uMsg == WM_CTLCOLORSCROLLBAR) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // HBRUSH OnCtlColorStatic(CDCHandle dc, CStatic wndStatic) #define MSG_WM_CTLCOLORSTATIC(func) \ if (uMsg == WM_CTLCOLORSTATIC) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSettingChange(UINT uFlags, LPCTSTR lpszSection) #define MSG_WM_SETTINGCHANGE(func) \ if (uMsg == WM_SETTINGCHANGE) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (LPCTSTR)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnDevModeChange(LPCTSTR lpDeviceName) #define MSG_WM_DEVMODECHANGE(func) \ if (uMsg == WM_DEVMODECHANGE) \ { \ SetMsgHandled(TRUE); \ func((LPCTSTR)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnActivateApp(BOOL bActive, DWORD dwThreadID) #define MSG_WM_ACTIVATEAPP(func) \ if (uMsg == WM_ACTIVATEAPP) \ { \ SetMsgHandled(TRUE); \ func((BOOL)wParam, (DWORD)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnFontChange() #define MSG_WM_FONTCHANGE(func) \ if (uMsg == WM_FONTCHANGE) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnTimeChange() #define MSG_WM_TIMECHANGE(func) \ if (uMsg == WM_TIMECHANGE) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnCancelMode() #define MSG_WM_CANCELMODE(func) \ if (uMsg == WM_CANCELMODE) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // BOOL OnSetCursor(CWindow wnd, UINT nHitTest, UINT message) #define MSG_WM_SETCURSOR(func) \ if (uMsg == WM_SETCURSOR) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ if(IsMsgHandled()) \ return TRUE; \ } // int OnMouseActivate(CWindow wndTopLevel, UINT nHitTest, UINT message) #define MSG_WM_MOUSEACTIVATE(func) \ if (uMsg == WM_MOUSEACTIVATE) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnChildActivate() #define MSG_WM_CHILDACTIVATE(func) \ if (uMsg == WM_CHILDACTIVATE) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnGetMinMaxInfo(LPMINMAXINFO lpMMI) #define MSG_WM_GETMINMAXINFO(func) \ if (uMsg == WM_GETMINMAXINFO) \ { \ SetMsgHandled(TRUE); \ func((LPMINMAXINFO)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnIconEraseBkgnd(CDCHandle dc) #define MSG_WM_ICONERASEBKGND(func) \ if (uMsg == WM_ICONERASEBKGND) \ { \ SetMsgHandled(TRUE); \ func((HDC)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSpoolerStatus(UINT nStatus, UINT nJobs) #define MSG_WM_SPOOLERSTATUS(func) \ if (uMsg == WM_SPOOLERSTATUS) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (UINT)LOWORD(lParam)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) #define MSG_WM_DRAWITEM(func) \ if (uMsg == WM_DRAWITEM) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (LPDRAWITEMSTRUCT)lParam); \ lResult = TRUE; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) #define MSG_WM_MEASUREITEM(func) \ if (uMsg == WM_MEASUREITEM) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (LPMEASUREITEMSTRUCT)lParam); \ lResult = TRUE; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnDeleteItem(int nIDCtl, LPDELETEITEMSTRUCT lpDeleteItemStruct) #define MSG_WM_DELETEITEM(func) \ if (uMsg == WM_DELETEITEM) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (LPDELETEITEMSTRUCT)lParam); \ lResult = TRUE; \ if(IsMsgHandled()) \ return TRUE; \ } //int OnCharToItem(UINT nChar, UINT nIndex, CListBox listBox) #define MSG_WM_CHARTOITEM(func) \ if (uMsg == WM_CHARTOITEM) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // int OnVKeyToItem(UINT nKey, UINT nIndex, CListBox listBox) #define MSG_WM_VKEYTOITEM(func) \ if (uMsg == WM_VKEYTOITEM) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // HCURSOR OnQueryDragIcon() #define MSG_WM_QUERYDRAGICON(func) \ if (uMsg == WM_QUERYDRAGICON) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func(); \ if(IsMsgHandled()) \ return TRUE; \ } // int OnCompareItem(int nIDCtl, LPCOMPAREITEMSTRUCT lpCompareItemStruct) #define MSG_WM_COMPAREITEM(func) \ if (uMsg == WM_COMPAREITEM) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((UINT)wParam, (LPCOMPAREITEMSTRUCT)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnCompacting(UINT nCpuTime) #define MSG_WM_COMPACTING(func) \ if (uMsg == WM_COMPACTING) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // BOOL OnNcCreate(LPCREATESTRUCT lpCreateStruct) #define MSG_WM_NCCREATE(func) \ if (uMsg == WM_NCCREATE) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((LPCREATESTRUCT)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNcDestroy() #define MSG_WM_NCDESTROY(func) \ if (uMsg == WM_NCDESTROY) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnNcCalcSize(BOOL bCalcValidRects, LPARAM lParam) #define MSG_WM_NCCALCSIZE(func) \ if (uMsg == WM_NCCALCSIZE) \ { \ SetMsgHandled(TRUE); \ lResult = func((BOOL)wParam, lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // UINT OnNcHitTest(CPoint point) #define MSG_WM_NCHITTEST(func) \ if (uMsg == WM_NCHITTEST) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func(_WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNcPaint(CRgnHandle rgn) #define MSG_WM_NCPAINT(func) \ if (uMsg == WM_NCPAINT) \ { \ SetMsgHandled(TRUE); \ func((HRGN)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // BOOL OnNcActivate(BOOL bActive) #define MSG_WM_NCACTIVATE(func) \ if (uMsg == WM_NCACTIVATE) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((BOOL)wParam); \ if(IsMsgHandled()) \ return TRUE; \ } // UINT OnGetDlgCode(LPMSG lpMsg) #define MSG_WM_GETDLGCODE(func) \ if (uMsg == WM_GETDLGCODE) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((LPMSG)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNcMouseMove(UINT nHitTest, CPoint point) #define MSG_WM_NCMOUSEMOVE(func) \ if (uMsg == WM_NCMOUSEMOVE) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNcLButtonDown(UINT nHitTest, CPoint point) #define MSG_WM_NCLBUTTONDOWN(func) \ if (uMsg == WM_NCLBUTTONDOWN) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNcLButtonUp(UINT nHitTest, CPoint point) #define MSG_WM_NCLBUTTONUP(func) \ if (uMsg == WM_NCLBUTTONUP) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNcLButtonDblClk(UINT nHitTest, CPoint point) #define MSG_WM_NCLBUTTONDBLCLK(func) \ if (uMsg == WM_NCLBUTTONDBLCLK) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNcRButtonDown(UINT nHitTest, CPoint point) #define MSG_WM_NCRBUTTONDOWN(func) \ if (uMsg == WM_NCRBUTTONDOWN) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNcRButtonUp(UINT nHitTest, CPoint point) #define MSG_WM_NCRBUTTONUP(func) \ if (uMsg == WM_NCRBUTTONUP) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNcRButtonDblClk(UINT nHitTest, CPoint point) #define MSG_WM_NCRBUTTONDBLCLK(func) \ if (uMsg == WM_NCRBUTTONDBLCLK) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNcMButtonDown(UINT nHitTest, CPoint point) #define MSG_WM_NCMBUTTONDOWN(func) \ if (uMsg == WM_NCMBUTTONDOWN) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNcMButtonUp(UINT nHitTest, CPoint point) #define MSG_WM_NCMBUTTONUP(func) \ if (uMsg == WM_NCMBUTTONUP) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNcMButtonDblClk(UINT nHitTest, CPoint point) #define MSG_WM_NCMBUTTONDBLCLK(func) \ if (uMsg == WM_NCMBUTTONDBLCLK) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) #define MSG_WM_KEYDOWN(func) \ if (uMsg == WM_KEYDOWN) \ { \ SetMsgHandled(TRUE); \ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) #define MSG_WM_KEYUP(func) \ if (uMsg == WM_KEYUP) \ { \ SetMsgHandled(TRUE); \ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) #define MSG_WM_CHAR(func) \ if (uMsg == WM_CHAR) \ { \ SetMsgHandled(TRUE); \ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnDeadChar(UINT nChar, UINT nRepCnt, UINT nFlags) #define MSG_WM_DEADCHAR(func) \ if (uMsg == WM_DEADCHAR) \ { \ SetMsgHandled(TRUE); \ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) #define MSG_WM_SYSKEYDOWN(func) \ if (uMsg == WM_SYSKEYDOWN) \ { \ SetMsgHandled(TRUE); \ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) #define MSG_WM_SYSKEYUP(func) \ if (uMsg == WM_SYSKEYUP) \ { \ SetMsgHandled(TRUE); \ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSysChar(UINT nChar, UINT nRepCnt, UINT nFlags) #define MSG_WM_SYSCHAR(func) \ if (uMsg == WM_SYSCHAR) \ { \ SetMsgHandled(TRUE); \ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSysDeadChar(UINT nChar, UINT nRepCnt, UINT nFlags) #define MSG_WM_SYSDEADCHAR(func) \ if (uMsg == WM_SYSDEADCHAR) \ { \ SetMsgHandled(TRUE); \ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSysCommand(UINT nID, CPoint point) #define MSG_WM_SYSCOMMAND(func) \ if (uMsg == WM_SYSCOMMAND) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnTCard(UINT idAction, DWORD dwActionData) #define MSG_WM_TCARD(func) \ if (uMsg == WM_TCARD) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (DWORD)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnTimer(UINT_PTR nIDEvent) #define MSG_WM_TIMER(func) \ if (uMsg == WM_TIMER) \ { \ SetMsgHandled(TRUE); \ func((UINT_PTR)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar) #define MSG_WM_HSCROLL(func) \ if (uMsg == WM_HSCROLL) \ { \ SetMsgHandled(TRUE); \ func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar) #define MSG_WM_VSCROLL(func) \ if (uMsg == WM_VSCROLL) \ { \ SetMsgHandled(TRUE); \ func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnInitMenu(CMenuHandle menu) #define MSG_WM_INITMENU(func) \ if (uMsg == WM_INITMENU) \ { \ SetMsgHandled(TRUE); \ func((HMENU)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnInitMenuPopup(CMenuHandle menuPopup, UINT nIndex, BOOL bSysMenu) #define MSG_WM_INITMENUPOPUP(func) \ if (uMsg == WM_INITMENUPOPUP) \ { \ SetMsgHandled(TRUE); \ func((HMENU)wParam, (UINT)LOWORD(lParam), (BOOL)HIWORD(lParam)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnMenuSelect(UINT nItemID, UINT nFlags, CMenuHandle menu) #define MSG_WM_MENUSELECT(func) \ if (uMsg == WM_MENUSELECT) \ { \ SetMsgHandled(TRUE); \ func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HMENU)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnMenuChar(UINT nChar, UINT nFlags, CMenuHandle menu) #define MSG_WM_MENUCHAR(func) \ if (uMsg == WM_MENUCHAR) \ { \ SetMsgHandled(TRUE); \ lResult = func((TCHAR)LOWORD(wParam), (UINT)HIWORD(wParam), (HMENU)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnNotify(int idCtrl, LPNMHDR pnmh) #define MSG_WM_NOTIFY(func) \ if (uMsg == WM_NOTIFY) \ { \ SetMsgHandled(TRUE); \ lResult = func((int)wParam, (LPNMHDR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnEnterIdle(UINT nWhy, CWindow wndWho) #define MSG_WM_ENTERIDLE(func) \ if (uMsg == WM_ENTERIDLE) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnMouseMove(UINT nFlags, CPoint point) #define MSG_WM_MOUSEMOVE(func) \ if (uMsg == WM_MOUSEMOVE) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) #define MSG_WM_MOUSEWHEEL(func) \ if (uMsg == WM_MOUSEWHEEL) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((UINT)LOWORD(wParam), (short)HIWORD(wParam), _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnLButtonDown(UINT nFlags, CPoint point) #define MSG_WM_LBUTTONDOWN(func) \ if (uMsg == WM_LBUTTONDOWN) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnLButtonUp(UINT nFlags, CPoint point) #define MSG_WM_LBUTTONUP(func) \ if (uMsg == WM_LBUTTONUP) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnLButtonDblClk(UINT nFlags, CPoint point) #define MSG_WM_LBUTTONDBLCLK(func) \ if (uMsg == WM_LBUTTONDBLCLK) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnRButtonDown(UINT nFlags, CPoint point) #define MSG_WM_RBUTTONDOWN(func) \ if (uMsg == WM_RBUTTONDOWN) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnRButtonUp(UINT nFlags, CPoint point) #define MSG_WM_RBUTTONUP(func) \ if (uMsg == WM_RBUTTONUP) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnRButtonDblClk(UINT nFlags, CPoint point) #define MSG_WM_RBUTTONDBLCLK(func) \ if (uMsg == WM_RBUTTONDBLCLK) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnMButtonDown(UINT nFlags, CPoint point) #define MSG_WM_MBUTTONDOWN(func) \ if (uMsg == WM_MBUTTONDOWN) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnMButtonUp(UINT nFlags, CPoint point) #define MSG_WM_MBUTTONUP(func) \ if (uMsg == WM_MBUTTONUP) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnMButtonDblClk(UINT nFlags, CPoint point) #define MSG_WM_MBUTTONDBLCLK(func) \ if (uMsg == WM_MBUTTONDBLCLK) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnParentNotify(UINT message, UINT nChildID, LPARAM lParam) #define MSG_WM_PARENTNOTIFY(func) \ if (uMsg == WM_PARENTNOTIFY) \ { \ SetMsgHandled(TRUE); \ func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnMDIActivate(CWindow wndActivate, CWindow wndDeactivate) #define MSG_WM_MDIACTIVATE(func) \ if (uMsg == WM_MDIACTIVATE) \ { \ SetMsgHandled(TRUE); \ func((HWND)wParam, (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnRenderFormat(UINT nFormat) #define MSG_WM_RENDERFORMAT(func) \ if (uMsg == WM_RENDERFORMAT) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnRenderAllFormats() #define MSG_WM_RENDERALLFORMATS(func) \ if (uMsg == WM_RENDERALLFORMATS) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnDestroyClipboard() #define MSG_WM_DESTROYCLIPBOARD(func) \ if (uMsg == WM_DESTROYCLIPBOARD) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnDrawClipboard() #define MSG_WM_DRAWCLIPBOARD(func) \ if (uMsg == WM_DRAWCLIPBOARD) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnPaintClipboard(CWindow wndViewer, const LPPAINTSTRUCT lpPaintStruct) #define MSG_WM_PAINTCLIPBOARD(func) \ if (uMsg == WM_PAINTCLIPBOARD) \ { \ SetMsgHandled(TRUE); \ func((HWND)wParam, (const LPPAINTSTRUCT)::GlobalLock((HGLOBAL)lParam)); \ ::GlobalUnlock((HGLOBAL)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnVScrollClipboard(CWindow wndViewer, UINT nSBCode, UINT nPos) #define MSG_WM_VSCROLLCLIPBOARD(func) \ if (uMsg == WM_VSCROLLCLIPBOARD) \ { \ SetMsgHandled(TRUE); \ func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnContextMenu(CWindow wnd, CPoint point) #define MSG_WM_CONTEXTMENU(func) \ if (uMsg == WM_CONTEXTMENU) \ { \ SetMsgHandled(TRUE); \ func((HWND)wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSizeClipboard(CWindow wndViewer, const LPRECT lpRect) #define MSG_WM_SIZECLIPBOARD(func) \ if (uMsg == WM_SIZECLIPBOARD) \ { \ SetMsgHandled(TRUE); \ func((HWND)wParam, (const LPRECT)::GlobalLock((HGLOBAL)lParam)); \ ::GlobalUnlock((HGLOBAL)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnAskCbFormatName(UINT nMaxCount, LPTSTR lpszString) #define MSG_WM_ASKCBFORMATNAME(func) \ if (uMsg == WM_ASKCBFORMATNAME) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (LPTSTR)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnChangeCbChain(CWindow wndRemove, CWindow wndAfter) #define MSG_WM_CHANGECBCHAIN(func) \ if (uMsg == WM_CHANGECBCHAIN) \ { \ SetMsgHandled(TRUE); \ func((HWND)wParam, (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnHScrollClipboard(CWindow wndViewer, UINT nSBCode, UINT nPos) #define MSG_WM_HSCROLLCLIPBOARD(func) \ if (uMsg == WM_HSCROLLCLIPBOARD) \ { \ SetMsgHandled(TRUE); \ func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // BOOL OnQueryNewPalette() #define MSG_WM_QUERYNEWPALETTE(func) \ if (uMsg == WM_QUERYNEWPALETTE) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func(); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnPaletteChanged(CWindow wndFocus) #define MSG_WM_PALETTECHANGED(func) \ if (uMsg == WM_PALETTECHANGED) \ { \ SetMsgHandled(TRUE); \ func((HWND)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnPaletteIsChanging(CWindow wndPalChg) #define MSG_WM_PALETTEISCHANGING(func) \ if (uMsg == WM_PALETTEISCHANGING) \ { \ SetMsgHandled(TRUE); \ func((HWND)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnDropFiles(HDROP hDropInfo) #define MSG_WM_DROPFILES(func) \ if (uMsg == WM_DROPFILES) \ { \ SetMsgHandled(TRUE); \ func((HDROP)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnWindowPosChanging(LPWINDOWPOS lpWndPos) #define MSG_WM_WINDOWPOSCHANGING(func) \ if (uMsg == WM_WINDOWPOSCHANGING) \ { \ SetMsgHandled(TRUE); \ func((LPWINDOWPOS)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnWindowPosChanged(LPWINDOWPOS lpWndPos) #define MSG_WM_WINDOWPOSCHANGED(func) \ if (uMsg == WM_WINDOWPOSCHANGED) \ { \ SetMsgHandled(TRUE); \ func((LPWINDOWPOS)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnExitMenuLoop(BOOL fIsTrackPopupMenu) #define MSG_WM_EXITMENULOOP(func) \ if (uMsg == WM_EXITMENULOOP) \ { \ SetMsgHandled(TRUE); \ func((BOOL)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnEnterMenuLoop(BOOL fIsTrackPopupMenu) #define MSG_WM_ENTERMENULOOP(func) \ if (uMsg == WM_ENTERMENULOOP) \ { \ SetMsgHandled(TRUE); \ func((BOOL)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnStyleChanged(int nStyleType, LPSTYLESTRUCT lpStyleStruct) #define MSG_WM_STYLECHANGED(func) \ if (uMsg == WM_STYLECHANGED) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (LPSTYLESTRUCT)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnStyleChanging(int nStyleType, LPSTYLESTRUCT lpStyleStruct) #define MSG_WM_STYLECHANGING(func) \ if (uMsg == WM_STYLECHANGING) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (LPSTYLESTRUCT)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSizing(UINT fwSide, LPRECT pRect) #define MSG_WM_SIZING(func) \ if (uMsg == WM_SIZING) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (LPRECT)lParam); \ lResult = TRUE; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnMoving(UINT fwSide, LPRECT pRect) #define MSG_WM_MOVING(func) \ if (uMsg == WM_MOVING) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (LPRECT)lParam); \ lResult = TRUE; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnCaptureChanged(CWindow wnd) #define MSG_WM_CAPTURECHANGED(func) \ if (uMsg == WM_CAPTURECHANGED) \ { \ SetMsgHandled(TRUE); \ func((HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // BOOL OnDeviceChange(UINT nEventType, DWORD_PTR dwData) #define MSG_WM_DEVICECHANGE(func) \ if (uMsg == WM_DEVICECHANGE) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((UINT)wParam, (DWORD_PTR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnCommand(UINT uNotifyCode, int nID, CWindow wndCtl) #define MSG_WM_COMMAND(func) \ if (uMsg == WM_COMMAND) \ { \ SetMsgHandled(TRUE); \ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnDisplayChange(UINT uBitsPerPixel, CSize sizeScreen) #define MSG_WM_DISPLAYCHANGE(func) \ if (uMsg == WM_DISPLAYCHANGE) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, _WTYPES_NS::CSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnEnterSizeMove() #define MSG_WM_ENTERSIZEMOVE(func) \ if (uMsg == WM_ENTERSIZEMOVE) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnExitSizeMove() #define MSG_WM_EXITSIZEMOVE(func) \ if (uMsg == WM_EXITSIZEMOVE) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // HFONT OnGetFont() #define MSG_WM_GETFONT(func) \ if (uMsg == WM_GETFONT) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func(); \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnGetHotKey() #define MSG_WM_GETHOTKEY(func) \ if (uMsg == WM_GETHOTKEY) \ { \ SetMsgHandled(TRUE); \ lResult = func(); \ if(IsMsgHandled()) \ return TRUE; \ } // HICON OnGetIcon() #define MSG_WM_GETICON(func) \ if (uMsg == WM_GETICON) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((UINT)wParam); \ if(IsMsgHandled()) \ return TRUE; \ } // int OnGetText(int cchTextMax, LPTSTR lpszText) #define MSG_WM_GETTEXT(func) \ if (uMsg == WM_GETTEXT) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((int)wParam, (LPTSTR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // int OnGetTextLength() #define MSG_WM_GETTEXTLENGTH(func) \ if (uMsg == WM_GETTEXTLENGTH) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func(); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnHelp(LPHELPINFO lpHelpInfo) #define MSG_WM_HELP(func) \ if (uMsg == WM_HELP) \ { \ SetMsgHandled(TRUE); \ func((LPHELPINFO)lParam); \ lResult = TRUE; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnHotKey(int nHotKeyID, UINT uModifiers, UINT uVirtKey) #define MSG_WM_HOTKEY(func) \ if (uMsg == WM_HOTKEY) \ { \ SetMsgHandled(TRUE); \ func((int)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnInputLangChange(DWORD dwCharSet, HKL hKbdLayout) #define MSG_WM_INPUTLANGCHANGE(func) \ if (uMsg == WM_INPUTLANGCHANGE) \ { \ SetMsgHandled(TRUE); \ func((DWORD)wParam, (HKL)lParam); \ lResult = TRUE; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnInputLangChangeRequest(BOOL bSysCharSet, HKL hKbdLayout) #define MSG_WM_INPUTLANGCHANGEREQUEST(func) \ if (uMsg == WM_INPUTLANGCHANGEREQUEST) \ { \ SetMsgHandled(TRUE); \ func((BOOL)wParam, (HKL)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNextDlgCtl(BOOL bHandle, WPARAM wCtlFocus) #define MSG_WM_NEXTDLGCTL(func) \ if (uMsg == WM_NEXTDLGCTL) \ { \ SetMsgHandled(TRUE); \ func((BOOL)LOWORD(lParam), wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNextMenu(int nVirtKey, LPMDINEXTMENU lpMdiNextMenu) #define MSG_WM_NEXTMENU(func) \ if (uMsg == WM_NEXTMENU) \ { \ SetMsgHandled(TRUE); \ func((int)wParam, (LPMDINEXTMENU)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // int OnNotifyFormat(CWindow wndFrom, int nCommand) #define MSG_WM_NOTIFYFORMAT(func) \ if (uMsg == WM_NOTIFYFORMAT) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HWND)wParam, (int)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // BOOL OnPowerBroadcast(DWORD dwPowerEvent, DWORD_PTR dwData) #define MSG_WM_POWERBROADCAST(func) \ if (uMsg == WM_POWERBROADCAST) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((DWORD)wParam, (DWORD_PTR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnPrint(CDCHandle dc, UINT uFlags) #define MSG_WM_PRINT(func) \ if (uMsg == WM_PRINT) \ { \ SetMsgHandled(TRUE); \ func((HDC)wParam, (UINT)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnPrintClient(CDCHandle dc, UINT uFlags) #define MSG_WM_PRINTCLIENT(func) \ if (uMsg == WM_PRINTCLIENT) \ { \ SetMsgHandled(TRUE); \ func((HDC)wParam, (UINT)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnRasDialEvent(RASCONNSTATE rasconnstate, DWORD dwError) #define MSG_WM_RASDIALEVENT(func) \ if (uMsg == WM_RASDIALEVENT) \ { \ SetMsgHandled(TRUE); \ func((RASCONNSTATE)wParam, (DWORD)lParam); \ lResult = TRUE; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSetFont(CFontHandle font, BOOL bRedraw) #define MSG_WM_SETFONT(func) \ if (uMsg == WM_SETFONT) \ { \ SetMsgHandled(TRUE); \ func((HFONT)wParam, (BOOL)LOWORD(lParam)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // int OnSetHotKey(int nVirtKey, UINT uFlags) #define MSG_WM_SETHOTKEY(func) \ if (uMsg == WM_SETHOTKEY) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((int)LOBYTE(LOWORD(wParam)), (UINT)HIBYTE(LOWORD(wParam))); \ if(IsMsgHandled()) \ return TRUE; \ } // HICON OnSetIcon(UINT uType, HICON hIcon) #define MSG_WM_SETICON(func) \ if (uMsg == WM_SETICON) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((UINT)wParam, (HICON)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnSetRedraw(BOOL bRedraw) #define MSG_WM_SETREDRAW(func) \ if (uMsg == WM_SETREDRAW) \ { \ SetMsgHandled(TRUE); \ func((BOOL)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // int OnSetText(LPCTSTR lpstrText) #define MSG_WM_SETTEXT(func) \ if (uMsg == WM_SETTEXT) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((LPCTSTR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnUserChanged() #define MSG_WM_USERCHANGED(func) \ if (uMsg == WM_USERCHANGED) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } /////////////////////////////////////////////////////////////////////////////// // New NT4 & NT5 messages #if (_WIN32_WINNT >= 0x0400) // void OnMouseHover(WPARAM wParam, CPoint ptPos) #define MSG_WM_MOUSEHOVER(func) \ if (uMsg == WM_MOUSEHOVER) \ { \ SetMsgHandled(TRUE); \ func(wParam, _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnMouseLeave() #define MSG_WM_MOUSELEAVE(func) \ if (uMsg == WM_MOUSELEAVE) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } #endif // _WIN32_WINNT >= 0x0400 #if (WINVER >= 0x0500) // void OnMenuRButtonUp(WPARAM wParam, CMenuHandle menu) #define MSG_WM_MENURBUTTONUP(func) \ if (uMsg == WM_MENURBUTTONUP) \ { \ SetMsgHandled(TRUE); \ func(wParam, (HMENU)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnMenuDrag(WPARAM wParam, CMenuHandle menu) #define MSG_WM_MENUDRAG(func) \ if (uMsg == WM_MENUDRAG) \ { \ SetMsgHandled(TRUE); \ lResult = func(wParam, (HMENU)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnMenuGetObject(PMENUGETOBJECTINFO info) #define MSG_WM_MENUGETOBJECT(func) \ if (uMsg == WM_MENUGETOBJECT) \ { \ SetMsgHandled(TRUE); \ lResult = func((PMENUGETOBJECTINFO)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnUnInitMenuPopup(UINT nID, CMenuHandle menu) #define MSG_WM_UNINITMENUPOPUP(func) \ if (uMsg == WM_UNINITMENUPOPUP) \ { \ SetMsgHandled(TRUE); \ func((UINT)HIWORD(lParam), (HMENU)wParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnMenuCommand(WPARAM nIndex, CMenuHandle menu) #define MSG_WM_MENUCOMMAND(func) \ if (uMsg == WM_MENUCOMMAND) \ { \ SetMsgHandled(TRUE); \ func(wParam, (HMENU)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } #endif // WINVER >= 0x0500 #if (_WIN32_WINNT >= 0x0500) // BOOL OnAppCommand(CWindow wndFocus, short cmd, WORD uDevice, int dwKeys) #define MSG_WM_APPCOMMAND(func) \ if (uMsg == WM_APPCOMMAND) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HWND)wParam, GET_APPCOMMAND_LPARAM(lParam), GET_DEVICE_LPARAM(lParam), GET_KEYSTATE_LPARAM(lParam)); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNCXButtonDown(int fwButton, short nHittest, CPoint ptPos) #define MSG_WM_NCXBUTTONDOWN(func) \ if (uMsg == WM_NCXBUTTONDOWN) \ { \ SetMsgHandled(TRUE); \ func(GET_XBUTTON_WPARAM(wParam), GET_NCHITTEST_WPARAM(wParam), _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNCXButtonUp(int fwButton, short nHittest, CPoint ptPos) #define MSG_WM_NCXBUTTONUP(func) \ if (uMsg == WM_NCXBUTTONUP) \ { \ SetMsgHandled(TRUE); \ func(GET_XBUTTON_WPARAM(wParam), GET_NCHITTEST_WPARAM(wParam), _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnNCXButtonDblClk(int fwButton, short nHittest, CPoint ptPos) #define MSG_WM_NCXBUTTONDBLCLK(func) \ if (uMsg == WM_NCXBUTTONDBLCLK) \ { \ SetMsgHandled(TRUE); \ func(GET_XBUTTON_WPARAM(wParam), GET_NCHITTEST_WPARAM(wParam), _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnXButtonDown(int fwButton, int dwKeys, CPoint ptPos) #define MSG_WM_XBUTTONDOWN(func) \ if (uMsg == WM_XBUTTONDOWN) \ { \ SetMsgHandled(TRUE); \ func(GET_XBUTTON_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnXButtonUp(int fwButton, int dwKeys, CPoint ptPos) #define MSG_WM_XBUTTONUP(func) \ if (uMsg == WM_XBUTTONUP) \ { \ SetMsgHandled(TRUE); \ func(GET_XBUTTON_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnXButtonDblClk(int fwButton, int dwKeys, CPoint ptPos) #define MSG_WM_XBUTTONDBLCLK(func) \ if (uMsg == WM_XBUTTONDBLCLK) \ { \ SetMsgHandled(TRUE); \ func(GET_XBUTTON_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnChangeUIState(WORD nAction, WORD nState) #define MSG_WM_CHANGEUISTATE(func) \ if (uMsg == WM_CHANGEUISTATE) \ { \ SetMsgHandled(TRUE); \ func(LOWORD(wParam), HIWORD(wParam)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnUpdateUIState(WORD nAction, WORD nState) #define MSG_WM_UPDATEUISTATE(func) \ if (uMsg == WM_UPDATEUISTATE) \ { \ SetMsgHandled(TRUE); \ func(LOWORD(wParam), HIWORD(wParam)); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnQueryUIState() #define MSG_WM_QUERYUISTATE(func) \ if (uMsg == WM_QUERYUISTATE) \ { \ SetMsgHandled(TRUE); \ lResult = func(); \ if(IsMsgHandled()) \ return TRUE; \ } #endif // (_WIN32_WINNT >= 0x0500) #if(_WIN32_WINNT >= 0x0501) // void OnInput(WPARAM RawInputCode, HRAWINPUT hRawInput) #define MSG_WM_INPUT(func) \ if (uMsg == WM_INPUT) \ { \ SetMsgHandled(TRUE); \ func(GET_RAWINPUT_CODE_WPARAM(wParam), (HRAWINPUT)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnUniChar(TCHAR nChar, UINT nRepCnt, UINT nFlags) #define MSG_WM_UNICHAR(func) \ if (uMsg == WM_UNICHAR) \ { \ SetMsgHandled(TRUE); \ func((TCHAR)wParam, (UINT)lParam & 0xFFFF, (UINT)((lParam & 0xFFFF0000) >> 16)); \ if(IsMsgHandled()) \ { \ lResult = (wParam == UNICODE_NOCHAR) ? TRUE : FALSE; \ return TRUE; \ } \ } // void OnWTSSessionChange(WPARAM nStatusCode, PWTSSESSION_NOTIFICATION nSessionID) #define MSG_WM_WTSSESSION_CHANGE(func) \ if (uMsg == WM_WTSSESSION_CHANGE) \ { \ SetMsgHandled(TRUE); \ func(wParam, (PWTSSESSION_NOTIFICATION)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnThemeChanged() #define MSG_WM_THEMECHANGED(func) \ if (uMsg == WM_THEMECHANGED) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } #endif // _WIN32_WINNT >= 0x0501 #if (_WIN32_WINNT >= 0x0600) // BOOL OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt) #define MSG_WM_MOUSEHWHEEL(func) \ if (uMsg == WM_MOUSEHWHEEL) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((UINT)LOWORD(wParam), (short)HIWORD(wParam), _WTYPES_NS::CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); \ if(IsMsgHandled()) \ return TRUE; \ } #endif // (_WIN32_WINNT >= 0x0600) /////////////////////////////////////////////////////////////////////////////// // ATL defined messages // BOOL OnForwardMsg(LPMSG Msg, DWORD nUserData) #define MSG_WM_FORWARDMSG(func) \ if (uMsg == WM_FORWARDMSG) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((LPMSG)lParam, (DWORD)wParam); \ if(IsMsgHandled()) \ return TRUE; \ } /////////////////////////////////////////////////////////////////////////////// // Dialog specific messages // LRESULT OnDMGetDefID() #define MSG_DM_GETDEFID(func) \ if (uMsg == DM_GETDEFID) \ { \ SetMsgHandled(TRUE); \ lResult = func(); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnDMSetDefID(UINT DefID) #define MSG_DM_SETDEFID(func) \ if (uMsg == DM_SETDEFID) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam); \ lResult = TRUE; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnDMReposition() #define MSG_DM_REPOSITION(func) \ if (uMsg == DM_REPOSITION) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } /////////////////////////////////////////////////////////////////////////////// // Reflected messages // void OnReflectedCommand(UINT uNotifyCode, int nID, CWindow wndCtl) #define MSG_OCM_COMMAND(func) \ if (uMsg == OCM_COMMAND) \ { \ SetMsgHandled(TRUE); \ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnReflectedNotify(int idCtrl, LPNMHDR pnmh) #define MSG_OCM_NOTIFY(func) \ if (uMsg == OCM_NOTIFY) \ { \ SetMsgHandled(TRUE); \ lResult = func((int)wParam, (LPNMHDR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnReflectedParentNotify(UINT message, UINT nChildID, LPARAM lParam) #define MSG_OCM_PARENTNOTIFY(func) \ if (uMsg == OCM_PARENTNOTIFY) \ { \ SetMsgHandled(TRUE); \ func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnReflectedDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) #define MSG_OCM_DRAWITEM(func) \ if (uMsg == OCM_DRAWITEM) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (LPDRAWITEMSTRUCT)lParam); \ lResult = TRUE; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnReflectedMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) #define MSG_OCM_MEASUREITEM(func) \ if (uMsg == OCM_MEASUREITEM) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (LPMEASUREITEMSTRUCT)lParam); \ lResult = TRUE; \ if(IsMsgHandled()) \ return TRUE; \ } // int OnReflectedCompareItem(int nIDCtl, LPCOMPAREITEMSTRUCT lpCompareItemStruct) #define MSG_OCM_COMPAREITEM(func) \ if (uMsg == OCM_COMPAREITEM) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((UINT)wParam, (LPCOMPAREITEMSTRUCT)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnReflectedDeleteItem(int nIDCtl, LPDELETEITEMSTRUCT lpDeleteItemStruct) #define MSG_OCM_DELETEITEM(func) \ if (uMsg == OCM_DELETEITEM) \ { \ SetMsgHandled(TRUE); \ func((UINT)wParam, (LPDELETEITEMSTRUCT)lParam); \ lResult = TRUE; \ if(IsMsgHandled()) \ return TRUE; \ } // int OnReflectedVKeyToItem(UINT nKey, UINT nIndex, CListBox listBox) #define MSG_OCM_VKEYTOITEM(func) \ if (uMsg == OCM_VKEYTOITEM) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } //int OnReflectedCharToItem(UINT nChar, UINT nIndex, CListBox listBox) #define MSG_OCM_CHARTOITEM(func) \ if (uMsg == OCM_CHARTOITEM) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((UINT)LOWORD(wParam), (UINT)HIWORD(wParam), (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnReflectedHScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar) #define MSG_OCM_HSCROLL(func) \ if (uMsg == OCM_HSCROLL) \ { \ SetMsgHandled(TRUE); \ func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnReflectedVScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar) #define MSG_OCM_VSCROLL(func) \ if (uMsg == OCM_VSCROLL) \ { \ SetMsgHandled(TRUE); \ func((int)LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // HBRUSH OnReflectedCtlColorEdit(CDCHandle dc, CEdit edit) #define MSG_OCM_CTLCOLOREDIT(func) \ if (uMsg == OCM_CTLCOLOREDIT) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // HBRUSH OnReflectedCtlColorListBox(CDCHandle dc, CListBox listBox) #define MSG_OCM_CTLCOLORLISTBOX(func) \ if (uMsg == OCM_CTLCOLORLISTBOX) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // HBRUSH OnReflectedCtlColorBtn(CDCHandle dc, CButton button) #define MSG_OCM_CTLCOLORBTN(func) \ if (uMsg == OCM_CTLCOLORBTN) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // HBRUSH OnReflectedCtlColorDlg(CDCHandle dc, CWindow wnd) #define MSG_OCM_CTLCOLORDLG(func) \ if (uMsg == OCM_CTLCOLORDLG) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // HBRUSH OnReflectedCtlColorScrollBar(CDCHandle dc, CScrollBar scrollBar) #define MSG_OCM_CTLCOLORSCROLLBAR(func) \ if (uMsg == OCM_CTLCOLORSCROLLBAR) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // HBRUSH OnReflectedCtlColorStatic(CDCHandle dc, CStatic wndStatic) #define MSG_OCM_CTLCOLORSTATIC(func) \ if (uMsg == OCM_CTLCOLORSTATIC) \ { \ SetMsgHandled(TRUE); \ lResult = (LRESULT)func((HDC)wParam, (HWND)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } /////////////////////////////////////////////////////////////////////////////// // Edit specific messages // void OnClear() #define MSG_WM_CLEAR(func) \ if (uMsg == WM_CLEAR) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnCopy() #define MSG_WM_COPY(func) \ if (uMsg == WM_COPY) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnCut() #define MSG_WM_CUT(func) \ if (uMsg == WM_CUT) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnPaste() #define MSG_WM_PASTE(func) \ if (uMsg == WM_PASTE) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnUndo() #define MSG_WM_UNDO(func) \ if (uMsg == WM_UNDO) \ { \ SetMsgHandled(TRUE); \ func(); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } /////////////////////////////////////////////////////////////////////////////// // Generic message handlers // LRESULT OnMessageHandlerEX(UINT uMsg, WPARAM wParam, LPARAM lParam) #define MESSAGE_HANDLER_EX(msg, func) \ if(uMsg == msg) \ { \ SetMsgHandled(TRUE); \ lResult = func(uMsg, wParam, lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnMessageRangeHandlerEX(UINT uMsg, WPARAM wParam, LPARAM lParam) #define MESSAGE_RANGE_HANDLER_EX(msgFirst, msgLast, func) \ if(uMsg >= msgFirst && uMsg <= msgLast) \ { \ SetMsgHandled(TRUE); \ lResult = func(uMsg, wParam, lParam); \ if(IsMsgHandled()) \ return TRUE; \ } /////////////////////////////////////////////////////////////////////////////// // Commands and notifications // void OnCommandHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) #define COMMAND_HANDLER_EX(id, code, func) \ if (uMsg == WM_COMMAND && code == HIWORD(wParam) && id == LOWORD(wParam)) \ { \ SetMsgHandled(TRUE); \ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) #define COMMAND_ID_HANDLER_EX(id, func) \ if (uMsg == WM_COMMAND && id == LOWORD(wParam)) \ { \ SetMsgHandled(TRUE); \ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnCommandCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) #define COMMAND_CODE_HANDLER_EX(code, func) \ if (uMsg == WM_COMMAND && code == HIWORD(wParam)) \ { \ SetMsgHandled(TRUE); \ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnNotifyHandlerEX(LPNMHDR pnmh) #define NOTIFY_HANDLER_EX(id, cd, func) \ if (uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code && id == ((LPNMHDR)lParam)->idFrom) \ { \ SetMsgHandled(TRUE); \ lResult = func((LPNMHDR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnNotifyIDHandlerEX(LPNMHDR pnmh) #define NOTIFY_ID_HANDLER_EX(id, func) \ if (uMsg == WM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom) \ { \ SetMsgHandled(TRUE); \ lResult = func((LPNMHDR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnNotifyCodeHandlerEX(LPNMHDR pnmh) #define NOTIFY_CODE_HANDLER_EX(cd, func) \ if (uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code) \ { \ SetMsgHandled(TRUE); \ lResult = func((LPNMHDR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnCommandRangeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) #define COMMAND_RANGE_HANDLER_EX(idFirst, idLast, func) \ if(uMsg == WM_COMMAND && LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) \ { \ SetMsgHandled(TRUE); \ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnCommandRangeCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) #define COMMAND_RANGE_CODE_HANDLER_EX(idFirst, idLast, code, func) \ if(uMsg == WM_COMMAND && code == HIWORD(wParam) && LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) \ { \ SetMsgHandled(TRUE); \ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnNotifyRangeHandlerEX(LPNMHDR pnmh) #define NOTIFY_RANGE_HANDLER_EX(idFirst, idLast, func) \ if(uMsg == WM_NOTIFY && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) \ { \ SetMsgHandled(TRUE); \ lResult = func((LPNMHDR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnNotifyRangeCodeHandlerEX(LPNMHDR pnmh) #define NOTIFY_RANGE_CODE_HANDLER_EX(idFirst, idLast, cd, func) \ if(uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) \ { \ SetMsgHandled(TRUE); \ lResult = func((LPNMHDR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnReflectedCommandHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) #define REFLECTED_COMMAND_HANDLER_EX(id, code, func) \ if (uMsg == OCM_COMMAND && code == HIWORD(wParam) && id == LOWORD(wParam)) \ { \ SetMsgHandled(TRUE); \ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnReflectedCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) #define REFLECTED_COMMAND_ID_HANDLER_EX(id, func) \ if (uMsg == OCM_COMMAND && id == LOWORD(wParam)) \ { \ SetMsgHandled(TRUE); \ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnReflectedCommandCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) #define REFLECTED_COMMAND_CODE_HANDLER_EX(code, func) \ if (uMsg == OCM_COMMAND && code == HIWORD(wParam)) \ { \ SetMsgHandled(TRUE); \ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnReflectedNotifyHandlerEX(LPNMHDR pnmh) #define REFLECTED_NOTIFY_HANDLER_EX(id, cd, func) \ if (uMsg == OCM_NOTIFY && cd == ((LPNMHDR)lParam)->code && id == ((LPNMHDR)lParam)->idFrom) \ { \ SetMsgHandled(TRUE); \ lResult = func((LPNMHDR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnReflectedNotifyIDHandlerEX(LPNMHDR pnmh) #define REFLECTED_NOTIFY_ID_HANDLER_EX(id, func) \ if (uMsg == OCM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom) \ { \ SetMsgHandled(TRUE); \ lResult = func((LPNMHDR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnReflectedNotifyCodeHandlerEX(LPNMHDR pnmh) #define REFLECTED_NOTIFY_CODE_HANDLER_EX(cd, func) \ if (uMsg == OCM_NOTIFY && cd == ((LPNMHDR)lParam)->code) \ { \ SetMsgHandled(TRUE); \ lResult = func((LPNMHDR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // void OnReflectedCommandRangeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) #define REFLECTED_COMMAND_RANGE_HANDLER_EX(idFirst, idLast, func) \ if(uMsg == OCM_COMMAND && LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) \ { \ SetMsgHandled(TRUE); \ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // void OnReflectedCommandRangeCodeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl) #define REFLECTED_COMMAND_RANGE_CODE_HANDLER_EX(idFirst, idLast, code, func) \ if(uMsg == OCM_COMMAND && code == HIWORD(wParam) && LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) \ { \ SetMsgHandled(TRUE); \ func((UINT)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam); \ lResult = 0; \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnReflectedNotifyRangeHandlerEX(LPNMHDR pnmh) #define REFLECTED_NOTIFY_RANGE_HANDLER_EX(idFirst, idLast, func) \ if(uMsg == OCM_NOTIFY && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) \ { \ SetMsgHandled(TRUE); \ lResult = func((LPNMHDR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } // LRESULT OnReflectedNotifyRangeCodeHandlerEX(LPNMHDR pnmh) #define REFLECTED_NOTIFY_RANGE_CODE_HANDLER_EX(idFirst, idLast, cd, func) \ if(uMsg == OCM_NOTIFY && cd == ((LPNMHDR)lParam)->code && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) \ { \ SetMsgHandled(TRUE); \ lResult = func((LPNMHDR)lParam); \ if(IsMsgHandled()) \ return TRUE; \ } #endif // __ATLCRACK_H__ ================================================ FILE: src/Setup/wtl90/atlctrls.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLCTRLS_H__ #define __ATLCTRLS_H__ #pragma once #ifndef __ATLAPP_H__ #error atlctrls.h requires atlapp.h to be included first #endif #ifndef __ATLWIN_H__ #error atlctrls.h requires atlwin.h to be included first #endif #ifndef _WIN32_WCE #include #include #elif defined(WIN32_PLATFORM_WFSP) && !defined(_WINUSERM_H_) #include #endif // !_WIN32_WCE // protect template members from windowsx.h macros #ifdef _INC_WINDOWSX #undef GetNextSibling #undef GetPrevSibling #endif // _INC_WINDOWSX /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CStaticT - CStatic // CButtonT - CButton // CListBoxT - CListBox // CComboBoxT - CComboBox // CEditT - CEdit // CEditCommands // CScrollBarT - CScrollBar // // CImageListT - CImageList, CImageListManaged // CListViewCtrlT - CListViewCtrl // CTreeViewCtrlT - CTreeViewCtrl // CTreeItemT - CTreeItem // CTreeViewCtrlExT - CTreeViewCtrlEx // CHeaderCtrlT - CHeaderCtrl // CToolBarCtrlT - CToolBarCtrl // CStatusBarCtrlT - CStatusBarCtrl // CTabCtrlT - CTabCtrl // CToolInfo // CToolTipCtrlT - CToolTipCtrl // CTrackBarCtrlT - CTrackBarCtrl // CUpDownCtrlT - CUpDownCtrl // CProgressBarCtrlT - CProgressBarCtrl // CHotKeyCtrlT - CHotKeyCtrl // CAnimateCtrlT - CAnimateCtrl // CRichEditCtrlT - CRichEditCtrl // CRichEditCommands // CDragListBoxT - CDragListBox // CDragListNotifyImpl // CReBarCtrlT - CReBarCtrl // CComboBoxExT - CComboBoxEx // CDateTimePickerCtrlT - CDateTimePickerCtrl // CMonthCalendarCtrlT - CMonthCalendarCtrl // CFlatScrollBarImpl // CFlatScrollBarT - CFlatScrollBar // CIPAddressCtrlT - CIPAddressCtrl // CPagerCtrlT - CPagerCtrl // CLinkCtrlT - CLinkCtrl // // CCustomDraw // // CCECommandBarCtrlT - CCECommandBarCtrl // CCECommandBandsCtrlT - CCECommandBandsCtrl namespace WTL { // These are wrapper classes for Windows standard and common controls. // To implement a window based on a control, use following: // Example: Implementing a window based on a list box // // class CMyListBox : CWindowImpl // { // public: // BEGIN_MSG_MAP(CMyListBox) // // put your message handler entries here // END_MSG_MAP() // }; // --- Standard Windows controls --- /////////////////////////////////////////////////////////////////////////////// // CStatic - client side for a Windows STATIC control template class CStaticT : public TBase { public: // Constructors CStaticT(HWND hWnd = NULL) : TBase(hWnd) { } CStaticT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return _T("STATIC"); } #ifndef _WIN32_WCE HICON GetIcon() const { ATLASSERT(::IsWindow(m_hWnd)); return (HICON)::SendMessage(m_hWnd, STM_GETICON, 0, 0L); } HICON SetIcon(HICON hIcon) { ATLASSERT(::IsWindow(m_hWnd)); return (HICON)::SendMessage(m_hWnd, STM_SETICON, (WPARAM)hIcon, 0L); } HENHMETAFILE GetEnhMetaFile() const { ATLASSERT(::IsWindow(m_hWnd)); return (HENHMETAFILE)::SendMessage(m_hWnd, STM_GETIMAGE, IMAGE_ENHMETAFILE, 0L); } HENHMETAFILE SetEnhMetaFile(HENHMETAFILE hMetaFile) { ATLASSERT(::IsWindow(m_hWnd)); return (HENHMETAFILE)::SendMessage(m_hWnd, STM_SETIMAGE, IMAGE_ENHMETAFILE, (LPARAM)hMetaFile); } #else // CE specific HICON GetIcon() const { ATLASSERT(::IsWindow(m_hWnd)); return (HICON)::SendMessage(m_hWnd, STM_GETIMAGE, IMAGE_ICON, 0L); } HICON SetIcon(HICON hIcon) { ATLASSERT(::IsWindow(m_hWnd)); return (HICON)::SendMessage(m_hWnd, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); } #endif // _WIN32_WCE CBitmapHandle GetBitmap() const { ATLASSERT(::IsWindow(m_hWnd)); return CBitmapHandle((HBITMAP)::SendMessage(m_hWnd, STM_GETIMAGE, IMAGE_BITMAP, 0L)); } CBitmapHandle SetBitmap(HBITMAP hBitmap) { ATLASSERT(::IsWindow(m_hWnd)); return CBitmapHandle((HBITMAP)::SendMessage(m_hWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap)); } HCURSOR GetCursor() const { ATLASSERT(::IsWindow(m_hWnd)); return (HCURSOR)::SendMessage(m_hWnd, STM_GETIMAGE, IMAGE_CURSOR, 0L); } HCURSOR SetCursor(HCURSOR hCursor) { ATLASSERT(::IsWindow(m_hWnd)); return (HCURSOR)::SendMessage(m_hWnd, STM_SETIMAGE, IMAGE_CURSOR, (LPARAM)hCursor); } }; typedef CStaticT CStatic; /////////////////////////////////////////////////////////////////////////////// // CButton - client side for a Windows BUTTON control template class CButtonT : public TBase { public: // Constructors CButtonT(HWND hWnd = NULL) : TBase(hWnd) { } CButtonT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return _T("BUTTON"); } UINT GetState() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, BM_GETSTATE, 0, 0L); } void SetState(BOOL bHighlight) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, BM_SETSTATE, bHighlight, 0L); } int GetCheck() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, BM_GETCHECK, 0, 0L); } void SetCheck(int nCheck) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, BM_SETCHECK, nCheck, 0L); } UINT GetButtonStyle() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::GetWindowLong(m_hWnd, GWL_STYLE) & 0xFFFF; } void SetButtonStyle(UINT nStyle, BOOL bRedraw = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, BM_SETSTYLE, nStyle, (LPARAM)bRedraw); } #ifndef _WIN32_WCE HICON GetIcon() const { ATLASSERT(::IsWindow(m_hWnd)); return (HICON)::SendMessage(m_hWnd, BM_GETIMAGE, IMAGE_ICON, 0L); } HICON SetIcon(HICON hIcon) { ATLASSERT(::IsWindow(m_hWnd)); return (HICON)::SendMessage(m_hWnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); } CBitmapHandle GetBitmap() const { ATLASSERT(::IsWindow(m_hWnd)); return CBitmapHandle((HBITMAP)::SendMessage(m_hWnd, BM_GETIMAGE, IMAGE_BITMAP, 0L)); } CBitmapHandle SetBitmap(HBITMAP hBitmap) { ATLASSERT(::IsWindow(m_hWnd)); return CBitmapHandle((HBITMAP)::SendMessage(m_hWnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap)); } #endif // !_WIN32_WCE #if (_WIN32_WINNT >= 0x0501) BOOL GetIdealSize(LPSIZE lpSize) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, BCM_GETIDEALSIZE, 0, (LPARAM)lpSize); } BOOL GetImageList(PBUTTON_IMAGELIST pButtonImagelist) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, BCM_GETIMAGELIST, 0, (LPARAM)pButtonImagelist); } BOOL SetImageList(PBUTTON_IMAGELIST pButtonImagelist) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, BCM_SETIMAGELIST, 0, (LPARAM)pButtonImagelist); } BOOL GetTextMargin(LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, BCM_GETTEXTMARGIN, 0, (LPARAM)lpRect); } BOOL SetTextMargin(LPRECT lpRect) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, BCM_SETTEXTMARGIN, 0, (LPARAM)lpRect); } #endif // (_WIN32_WINNT >= 0x0501) #if (WINVER >= 0x0600) void SetDontClick(BOOL bDontClick) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, BM_SETDONTCLICK, (WPARAM)bDontClick, 0L); } #endif // (WINVER >= 0x0600) #if (_WIN32_WINNT >= 0x0600) BOOL SetDropDownState(BOOL bDropDown) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (BS_SPLITBUTTON | BS_DEFSPLITBUTTON)) != 0); return (BOOL)::SendMessage(m_hWnd, BCM_SETDROPDOWNSTATE, (WPARAM)bDropDown, 0L); } BOOL GetSplitInfo(PBUTTON_SPLITINFO pSplitInfo) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (BS_SPLITBUTTON | BS_DEFSPLITBUTTON)) != 0); return (BOOL)::SendMessage(m_hWnd, BCM_GETSPLITINFO, 0, (LPARAM)pSplitInfo); } BOOL SetSplitInfo(PBUTTON_SPLITINFO pSplitInfo) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (BS_SPLITBUTTON | BS_DEFSPLITBUTTON)) != 0); return (BOOL)::SendMessage(m_hWnd, BCM_SETSPLITINFO, 0, (LPARAM)pSplitInfo); } int GetNoteLength() const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (BS_COMMANDLINK | BS_DEFCOMMANDLINK)) != 0); return (int)::SendMessage(m_hWnd, BCM_GETNOTELENGTH, 0, 0L); } BOOL GetNote(LPWSTR lpstrNoteText, int cchNoteText) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (BS_COMMANDLINK | BS_DEFCOMMANDLINK)) != 0); return (BOOL)::SendMessage(m_hWnd, BCM_GETNOTE, cchNoteText, (LPARAM)lpstrNoteText); } BOOL SetNote(LPCWSTR lpstrNoteText) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (BS_COMMANDLINK | BS_DEFCOMMANDLINK)) != 0); return (BOOL)::SendMessage(m_hWnd, BCM_SETNOTE, 0, (LPARAM)lpstrNoteText); } LRESULT SetElevationRequiredState(BOOL bSet) { ATLASSERT(::IsWindow(m_hWnd)); return ::SendMessage(m_hWnd, BCM_SETSHIELD, 0, (LPARAM)bSet); } #endif // (_WIN32_WINNT >= 0x0600) // Operations void Click() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, BM_CLICK, 0, 0L); } }; typedef CButtonT CButton; /////////////////////////////////////////////////////////////////////////////// // CListBox - client side for a Windows LISTBOX control template class CListBoxT : public TBase { public: // Constructors CListBoxT(HWND hWnd = NULL) : TBase(hWnd) { } CListBoxT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return _T("LISTBOX"); } // for entire listbox int GetCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_GETCOUNT, 0, 0L); } #ifndef _WIN32_WCE int SetCount(int cItems) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(((GetStyle() & LBS_NODATA) != 0) && ((GetStyle() & LBS_HASSTRINGS) == 0)); return (int)::SendMessage(m_hWnd, LB_SETCOUNT, cItems, 0L); } #endif // !_WIN32_WCE int GetHorizontalExtent() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_GETHORIZONTALEXTENT, 0, 0L); } void SetHorizontalExtent(int cxExtent) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LB_SETHORIZONTALEXTENT, cxExtent, 0L); } int GetTopIndex() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_GETTOPINDEX, 0, 0L); } int SetTopIndex(int nIndex) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_SETTOPINDEX, nIndex, 0L); } LCID GetLocale() const { ATLASSERT(::IsWindow(m_hWnd)); return (LCID)::SendMessage(m_hWnd, LB_GETLOCALE, 0, 0L); } LCID SetLocale(LCID nNewLocale) { ATLASSERT(::IsWindow(m_hWnd)); return (LCID)::SendMessage(m_hWnd, LB_SETLOCALE, (WPARAM)nNewLocale, 0L); } #if (WINVER >= 0x0500) && !defined(_WIN32_WCE) DWORD GetListBoxInfo() const { ATLASSERT(::IsWindow(m_hWnd)); #if (_WIN32_WINNT >= 0x0501) return (DWORD)::SendMessage(m_hWnd, LB_GETLISTBOXINFO, 0, 0L); #else // !(_WIN32_WINNT >= 0x0501) return ::GetListBoxInfo(m_hWnd); #endif // !(_WIN32_WINNT >= 0x0501) } #endif // (WINVER >= 0x0500) && !defined(_WIN32_WCE) // for single-selection listboxes int GetCurSel() const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) == 0); return (int)::SendMessage(m_hWnd, LB_GETCURSEL, 0, 0L); } int SetCurSel(int nSelect) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) == 0); return (int)::SendMessage(m_hWnd, LB_SETCURSEL, nSelect, 0L); } // for multiple-selection listboxes int GetSel(int nIndex) const // also works for single-selection { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_GETSEL, nIndex, 0L); } int SetSel(int nIndex, BOOL bSelect = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0); return (int)::SendMessage(m_hWnd, LB_SETSEL, bSelect, nIndex); } int GetSelCount() const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0); return (int)::SendMessage(m_hWnd, LB_GETSELCOUNT, 0, 0L); } int GetSelItems(int nMaxItems, LPINT rgIndex) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0); return (int)::SendMessage(m_hWnd, LB_GETSELITEMS, nMaxItems, (LPARAM)rgIndex); } int GetAnchorIndex() const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0); return (int)::SendMessage(m_hWnd, LB_GETANCHORINDEX, 0, 0L); } void SetAnchorIndex(int nIndex) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0); ::SendMessage(m_hWnd, LB_SETANCHORINDEX, nIndex, 0L); } int GetCaretIndex() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_GETCARETINDEX, 0, 0); } int SetCaretIndex(int nIndex, BOOL bScroll = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_SETCARETINDEX, nIndex, MAKELONG(bScroll, 0)); } // for listbox items DWORD_PTR GetItemData(int nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD_PTR)::SendMessage(m_hWnd, LB_GETITEMDATA, nIndex, 0L); } int SetItemData(int nIndex, DWORD_PTR dwItemData) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_SETITEMDATA, nIndex, (LPARAM)dwItemData); } void* GetItemDataPtr(int nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (void*)::SendMessage(m_hWnd, LB_GETITEMDATA, nIndex, 0L); } int SetItemDataPtr(int nIndex, void* pData) { ATLASSERT(::IsWindow(m_hWnd)); return SetItemData(nIndex, (DWORD_PTR)pData); } int GetItemRect(int nIndex, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_GETITEMRECT, nIndex, (LPARAM)lpRect); } int GetText(int nIndex, LPTSTR lpszBuffer) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_GETTEXT, nIndex, (LPARAM)lpszBuffer); } #ifndef _ATL_NO_COM #ifdef _OLEAUTO_H_ BOOL GetTextBSTR(int nIndex, BSTR& bstrText) const { USES_CONVERSION; ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(bstrText == NULL); int nLen = GetTextLen(nIndex); if(nLen == LB_ERR) return FALSE; CTempBuffer buff; LPTSTR lpstrText = buff.Allocate(nLen + 1); if(lpstrText == NULL) return FALSE; if(GetText(nIndex, lpstrText) == LB_ERR) return FALSE; bstrText = ::SysAllocString(T2OLE(lpstrText)); return (bstrText != NULL) ? TRUE : FALSE; } #endif // _OLEAUTO_H_ #endif // !_ATL_NO_COM #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) int GetText(int nIndex, _CSTRING_NS::CString& strText) const { ATLASSERT(::IsWindow(m_hWnd)); int cchLen = GetTextLen(nIndex); if(cchLen == LB_ERR) return LB_ERR; int nRet = LB_ERR; LPTSTR lpstr = strText.GetBufferSetLength(cchLen); if(lpstr != NULL) { nRet = GetText(nIndex, lpstr); strText.ReleaseBuffer(); } return nRet; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) int GetTextLen(int nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_GETTEXTLEN, nIndex, 0L); } int GetItemHeight(int nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_GETITEMHEIGHT, nIndex, 0L); } int SetItemHeight(int nIndex, UINT cyItemHeight) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_SETITEMHEIGHT, nIndex, MAKELONG(cyItemHeight, 0)); } // Settable only attributes void SetColumnWidth(int cxWidth) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LB_SETCOLUMNWIDTH, cxWidth, 0L); } BOOL SetTabStops(int nTabStops, LPINT rgTabStops) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & LBS_USETABSTOPS) != 0); return (BOOL)::SendMessage(m_hWnd, LB_SETTABSTOPS, nTabStops, (LPARAM)rgTabStops); } BOOL SetTabStops() { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & LBS_USETABSTOPS) != 0); return (BOOL)::SendMessage(m_hWnd, LB_SETTABSTOPS, 0, 0L); } BOOL SetTabStops(const int& cxEachStop) // takes an 'int' { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & LBS_USETABSTOPS) != 0); return (BOOL)::SendMessage(m_hWnd, LB_SETTABSTOPS, 1, (LPARAM)(LPINT)&cxEachStop); } // Operations int InitStorage(int nItems, UINT nBytes) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_INITSTORAGE, (WPARAM)nItems, nBytes); } void ResetContent() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LB_RESETCONTENT, 0, 0L); } UINT ItemFromPoint(POINT pt, BOOL& bOutside) const { ATLASSERT(::IsWindow(m_hWnd)); DWORD dw = (DWORD)::SendMessage(m_hWnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y)); bOutside = (BOOL)HIWORD(dw); return (UINT)LOWORD(dw); } // manipulating listbox items int AddString(LPCTSTR lpszItem) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_ADDSTRING, 0, (LPARAM)lpszItem); } int DeleteString(UINT nIndex) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_DELETESTRING, nIndex, 0L); } int InsertString(int nIndex, LPCTSTR lpszItem) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_INSERTSTRING, nIndex, (LPARAM)lpszItem); } #ifndef _WIN32_WCE int Dir(UINT attr, LPCTSTR lpszWildCard) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_DIR, attr, (LPARAM)lpszWildCard); } int AddFile(LPCTSTR lpstrFileName) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_ADDFILE, 0, (LPARAM)lpstrFileName); } #endif // !_WIN32_WCE // selection helpers int FindString(int nStartAfter, LPCTSTR lpszItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_FINDSTRING, nStartAfter, (LPARAM)lpszItem); } int FindStringExact(int nIndexStart, LPCTSTR lpszFind) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_FINDSTRINGEXACT, nIndexStart, (LPARAM)lpszFind); } int SelectString(int nStartAfter, LPCTSTR lpszItem) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LB_SELECTSTRING, nStartAfter, (LPARAM)lpszItem); } int SelItemRange(BOOL bSelect, int nFirstItem, int nLastItem) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != 0); ATLASSERT(nFirstItem <= nLastItem); return bSelect ? (int)::SendMessage(m_hWnd, LB_SELITEMRANGEEX, nFirstItem, nLastItem) : (int)::SendMessage(m_hWnd, LB_SELITEMRANGEEX, nLastItem, nFirstItem); } #ifdef WIN32_PLATFORM_WFSP // SmartPhone only messages DWORD GetInputMode(BOOL bCurrentMode = TRUE) { return SendMessage(LB_GETINPUTMODE, 0, (LPARAM)bCurrentMode); } BOOL SetInputMode(DWORD dwMode) { return SendMessage(LB_SETINPUTMODE, 0, (LPARAM)dwMode); } #endif // WIN32_PLATFORM_WFSP }; typedef CListBoxT CListBox; /////////////////////////////////////////////////////////////////////////////// // CComboBox - client side for a Windows COMBOBOX control #ifndef WIN32_PLATFORM_WFSP // No COMBOBOX on SmartPhones template class CComboBoxT : public TBase { public: // Constructors CComboBoxT(HWND hWnd = NULL) : TBase(hWnd) { } CComboBoxT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return _T("COMBOBOX"); } // for entire combo box int GetCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_GETCOUNT, 0, 0L); } int GetCurSel() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_GETCURSEL, 0, 0L); } int SetCurSel(int nSelect) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_SETCURSEL, nSelect, 0L); } LCID GetLocale() const { ATLASSERT(::IsWindow(m_hWnd)); return (LCID)::SendMessage(m_hWnd, CB_GETLOCALE, 0, 0L); } LCID SetLocale(LCID nNewLocale) { ATLASSERT(::IsWindow(m_hWnd)); return (LCID)::SendMessage(m_hWnd, CB_SETLOCALE, (WPARAM)nNewLocale, 0L); } int GetTopIndex() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_GETTOPINDEX, 0, 0L); } int SetTopIndex(int nIndex) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_SETTOPINDEX, nIndex, 0L); } UINT GetHorizontalExtent() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, CB_GETHORIZONTALEXTENT, 0, 0L); } void SetHorizontalExtent(UINT nExtent) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, CB_SETHORIZONTALEXTENT, nExtent, 0L); } int GetDroppedWidth() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_GETDROPPEDWIDTH, 0, 0L); } int SetDroppedWidth(UINT nWidth) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_SETDROPPEDWIDTH, nWidth, 0L); } #if ((WINVER >= 0x0500) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 420)) BOOL GetComboBoxInfo(PCOMBOBOXINFO pComboBoxInfo) const { ATLASSERT(::IsWindow(m_hWnd)); #if ((_WIN32_WINNT >= 0x0501) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 420)) return (BOOL)::SendMessage(m_hWnd, CB_GETCOMBOBOXINFO, 0, (LPARAM)pComboBoxInfo); #else // !((_WIN32_WINNT >= 0x0501) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 420)) return ::GetComboBoxInfo(m_hWnd, pComboBoxInfo); #endif // !((_WIN32_WINNT >= 0x0501) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 420)) } #endif // ((WINVER >= 0x0500) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 420)) // for edit control DWORD GetEditSel() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, CB_GETEDITSEL, 0, 0L); } BOOL SetEditSel(int nStartChar, int nEndChar) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, CB_SETEDITSEL, 0, MAKELONG(nStartChar, nEndChar)); } // for combobox item DWORD_PTR GetItemData(int nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD_PTR)::SendMessage(m_hWnd, CB_GETITEMDATA, nIndex, 0L); } int SetItemData(int nIndex, DWORD_PTR dwItemData) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_SETITEMDATA, nIndex, (LPARAM)dwItemData); } void* GetItemDataPtr(int nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (void*)GetItemData(nIndex); } int SetItemDataPtr(int nIndex, void* pData) { ATLASSERT(::IsWindow(m_hWnd)); return SetItemData(nIndex, (DWORD_PTR)pData); } int GetLBText(int nIndex, LPTSTR lpszText) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_GETLBTEXT, nIndex, (LPARAM)lpszText); } #ifndef _ATL_NO_COM BOOL GetLBTextBSTR(int nIndex, BSTR& bstrText) const { USES_CONVERSION; ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(bstrText == NULL); int nLen = GetLBTextLen(nIndex); if(nLen == CB_ERR) return FALSE; CTempBuffer buff; LPTSTR lpstrText = buff.Allocate(nLen + 1); if(lpstrText == NULL) return FALSE; if(GetLBText(nIndex, lpstrText) == CB_ERR) return FALSE; bstrText = ::SysAllocString(T2OLE(lpstrText)); return (bstrText != NULL) ? TRUE : FALSE; } #endif // !_ATL_NO_COM #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) int GetLBText(int nIndex, _CSTRING_NS::CString& strText) const { ATLASSERT(::IsWindow(m_hWnd)); int cchLen = GetLBTextLen(nIndex); if(cchLen == CB_ERR) return CB_ERR; int nRet = CB_ERR; LPTSTR lpstr = strText.GetBufferSetLength(cchLen); if(lpstr != NULL) { nRet = GetLBText(nIndex, lpstr); strText.ReleaseBuffer(); } return nRet; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) int GetLBTextLen(int nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_GETLBTEXTLEN, nIndex, 0L); } int GetItemHeight(int nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_GETITEMHEIGHT, nIndex, 0L); } int SetItemHeight(int nIndex, UINT cyItemHeight) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_SETITEMHEIGHT, nIndex, MAKELONG(cyItemHeight, 0)); } BOOL GetExtendedUI() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, CB_GETEXTENDEDUI, 0, 0L); } int SetExtendedUI(BOOL bExtended = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_SETEXTENDEDUI, bExtended, 0L); } void GetDroppedControlRect(LPRECT lprect) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)lprect); } BOOL GetDroppedState() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, CB_GETDROPPEDSTATE, 0, 0L); } #if (_WIN32_WINNT >= 0x0501) int GetMinVisible() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_GETMINVISIBLE, 0, 0L); } BOOL SetMinVisible(int nMinVisible) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, CB_SETMINVISIBLE, nMinVisible, 0L); } // Vista only BOOL GetCueBannerText(LPWSTR lpwText, int cchText) const { #ifndef CB_GETCUEBANNER const UINT CB_GETCUEBANNER = (CBM_FIRST + 4); #endif ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, CB_GETCUEBANNER, (WPARAM)lpwText, cchText); } // Vista only BOOL SetCueBannerText(LPCWSTR lpcwText) { #ifndef CB_SETCUEBANNER const UINT CB_SETCUEBANNER = (CBM_FIRST + 3); #endif ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, CB_SETCUEBANNER, 0, (LPARAM)lpcwText); } #endif // (_WIN32_WINNT >= 0x0501) // Operations int InitStorage(int nItems, UINT nBytes) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_INITSTORAGE, (WPARAM)nItems, nBytes); } void ResetContent() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, CB_RESETCONTENT, 0, 0L); } // for edit control BOOL LimitText(int nMaxChars) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, CB_LIMITTEXT, nMaxChars, 0L); } // for drop-down combo boxes void ShowDropDown(BOOL bShowIt = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, CB_SHOWDROPDOWN, bShowIt, 0L); } // manipulating listbox items int AddString(LPCTSTR lpszString) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_ADDSTRING, 0, (LPARAM)lpszString); } int DeleteString(UINT nIndex) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_DELETESTRING, nIndex, 0L); } int InsertString(int nIndex, LPCTSTR lpszString) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_INSERTSTRING, nIndex, (LPARAM)lpszString); } #ifndef _WIN32_WCE int Dir(UINT attr, LPCTSTR lpszWildCard) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_DIR, attr, (LPARAM)lpszWildCard); } #endif // !_WIN32_WCE // selection helpers int FindString(int nStartAfter, LPCTSTR lpszString) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_FINDSTRING, nStartAfter, (LPARAM)lpszString); } int FindStringExact(int nIndexStart, LPCTSTR lpszFind) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_FINDSTRINGEXACT, nIndexStart, (LPARAM)lpszFind); } int SelectString(int nStartAfter, LPCTSTR lpszString) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CB_SELECTSTRING, nStartAfter, (LPARAM)lpszString); } // Clipboard operations void Clear() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_CLEAR, 0, 0L); } void Copy() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_COPY, 0, 0L); } void Cut() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_CUT, 0, 0L); } void Paste() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_PASTE, 0, 0L); } }; typedef CComboBoxT CComboBox; #endif // !WIN32_PLATFORM_WFSP /////////////////////////////////////////////////////////////////////////////// // CEdit - client side for a Windows EDIT control template class CEditT : public TBase { public: // Constructors CEditT(HWND hWnd = NULL) : TBase(hWnd) { } CEditT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return _T("EDIT"); } BOOL CanUndo() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_CANUNDO, 0, 0L); } int GetLineCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_GETLINECOUNT, 0, 0L); } BOOL GetModify() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_GETMODIFY, 0, 0L); } void SetModify(BOOL bModified = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETMODIFY, bModified, 0L); } void GetRect(LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_GETRECT, 0, (LPARAM)lpRect); } DWORD GetSel() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, EM_GETSEL, 0, 0L); } void GetSel(int& nStartChar, int& nEndChar) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar); } #ifndef _WIN32_WCE HLOCAL GetHandle() const { ATLASSERT(::IsWindow(m_hWnd)); return (HLOCAL)::SendMessage(m_hWnd, EM_GETHANDLE, 0, 0L); } void SetHandle(HLOCAL hBuffer) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETHANDLE, (WPARAM)hBuffer, 0L); } #endif // !_WIN32_WCE DWORD GetMargins() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, EM_GETMARGINS, 0, 0L); } void GetMargins(UINT& nLeft, UINT& nRight) const { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, EM_GETMARGINS, 0, 0L); nLeft = LOWORD(dwRet); nRight = HIWORD(dwRet); } void SetMargins(UINT nLeft, UINT nRight, WORD wFlags = EC_LEFTMARGIN | EC_RIGHTMARGIN) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETMARGINS, wFlags, MAKELONG(nLeft, nRight)); } UINT GetLimitText() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, EM_GETLIMITTEXT, 0, 0L); } void SetLimitText(UINT nMax) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETLIMITTEXT, nMax, 0L); } POINT PosFromChar(UINT nChar) const { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, EM_POSFROMCHAR, nChar, 0); POINT point = { GET_X_LPARAM(dwRet), GET_Y_LPARAM(dwRet) }; return point; } int CharFromPos(POINT pt, int* pLine = NULL) const { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, EM_CHARFROMPOS, 0, MAKELPARAM(pt.x, pt.y)); if(pLine != NULL) *pLine = (int)(short)HIWORD(dwRet); return (int)(short)LOWORD(dwRet); } // NOTE: first word in lpszBuffer must contain the size of the buffer! int GetLine(int nIndex, LPTSTR lpszBuffer) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer); } int GetLine(int nIndex, LPTSTR lpszBuffer, int nMaxLength) const { ATLASSERT(::IsWindow(m_hWnd)); *(LPWORD)lpszBuffer = (WORD)nMaxLength; return (int)::SendMessage(m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer); } TCHAR GetPasswordChar() const { ATLASSERT(::IsWindow(m_hWnd)); return (TCHAR)::SendMessage(m_hWnd, EM_GETPASSWORDCHAR, 0, 0L); } void SetPasswordChar(TCHAR ch) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETPASSWORDCHAR, ch, 0L); } #ifndef _WIN32_WCE EDITWORDBREAKPROC GetWordBreakProc() const { ATLASSERT(::IsWindow(m_hWnd)); return (EDITWORDBREAKPROC)::SendMessage(m_hWnd, EM_GETWORDBREAKPROC, 0, 0L); } void SetWordBreakProc(EDITWORDBREAKPROC ewbprc) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETWORDBREAKPROC, 0, (LPARAM)ewbprc); } #endif // !_WIN32_WCE int GetFirstVisibleLine() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_GETFIRSTVISIBLELINE, 0, 0L); } #ifndef _WIN32_WCE int GetThumb() const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & ES_MULTILINE) != 0); return (int)::SendMessage(m_hWnd, EM_GETTHUMB, 0, 0L); } #endif // !_WIN32_WCE BOOL SetReadOnly(BOOL bReadOnly = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETREADONLY, bReadOnly, 0L); } #if (WINVER >= 0x0500) && !defined(_WIN32_WCE) UINT GetImeStatus(UINT uStatus) const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, EM_GETIMESTATUS, uStatus, 0L); } UINT SetImeStatus(UINT uStatus, UINT uData) { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, EM_SETIMESTATUS, uStatus, uData); } #endif // (WINVER >= 0x0500) && !defined(_WIN32_WCE) #if (_WIN32_WINNT >= 0x0501) BOOL GetCueBannerText(LPCWSTR lpstrText, int cchText) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_GETCUEBANNER, (WPARAM)lpstrText, cchText); } // bKeepWithFocus - Vista only BOOL SetCueBannerText(LPCWSTR lpstrText, BOOL bKeepWithFocus = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETCUEBANNER, (WPARAM)bKeepWithFocus, (LPARAM)(lpstrText)); } #endif // (_WIN32_WINNT >= 0x0501) // Operations void EmptyUndoBuffer() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_EMPTYUNDOBUFFER, 0, 0L); } BOOL FmtLines(BOOL bAddEOL) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_FMTLINES, bAddEOL, 0L); } void LimitText(int nChars = 0) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_LIMITTEXT, nChars, 0L); } int LineFromChar(int nIndex = -1) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_LINEFROMCHAR, nIndex, 0L); } int LineIndex(int nLine = -1) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_LINEINDEX, nLine, 0L); } int LineLength(int nLine = -1) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_LINELENGTH, nLine, 0L); } void LineScroll(int nLines, int nChars = 0) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_LINESCROLL, nChars, nLines); } void ReplaceSel(LPCTSTR lpszNewText, BOOL bCanUndo = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_REPLACESEL, (WPARAM) bCanUndo, (LPARAM)lpszNewText); } void SetRect(LPCRECT lpRect) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETRECT, 0, (LPARAM)lpRect); } void SetRectNP(LPCRECT lpRect) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETRECTNP, 0, (LPARAM)lpRect); } void SetSel(DWORD dwSelection, BOOL bNoScroll = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETSEL, LOWORD(dwSelection), HIWORD(dwSelection)); if(!bNoScroll) ::SendMessage(m_hWnd, EM_SCROLLCARET, 0, 0L); } void SetSel(int nStartChar, int nEndChar, BOOL bNoScroll = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETSEL, nStartChar, nEndChar); if(!bNoScroll) ::SendMessage(m_hWnd, EM_SCROLLCARET, 0, 0L); } void SetSelAll(BOOL bNoScroll = FALSE) { SetSel(0, -1, bNoScroll); } void SetSelNone(BOOL bNoScroll = FALSE) { SetSel(-1, 0, bNoScroll); } BOOL SetTabStops(int nTabStops, LPINT rgTabStops) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETTABSTOPS, nTabStops, (LPARAM)rgTabStops); } BOOL SetTabStops() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETTABSTOPS, 0, 0L); } BOOL SetTabStops(const int& cxEachStop) // takes an 'int' { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETTABSTOPS, 1, (LPARAM)(LPINT)&cxEachStop); } void ScrollCaret() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SCROLLCARET, 0, 0L); } int Scroll(int nScrollAction) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & ES_MULTILINE) != 0); LRESULT lRet = ::SendMessage(m_hWnd, EM_SCROLL, nScrollAction, 0L); if(!(BOOL)HIWORD(lRet)) return -1; // failed return (int)(short)LOWORD(lRet); } void InsertText(int nInsertAfterChar, LPCTSTR lpstrText, BOOL bNoScroll = FALSE, BOOL bCanUndo = FALSE) { SetSel(nInsertAfterChar, nInsertAfterChar, bNoScroll); ReplaceSel(lpstrText, bCanUndo); } void AppendText(LPCTSTR lpstrText, BOOL bNoScroll = FALSE, BOOL bCanUndo = FALSE) { InsertText(GetWindowTextLength(), lpstrText, bNoScroll, bCanUndo); } #if (_WIN32_WINNT >= 0x0501) BOOL ShowBalloonTip(PEDITBALLOONTIP pEditBaloonTip) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SHOWBALLOONTIP, 0, (LPARAM)pEditBaloonTip); } BOOL HideBalloonTip() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_HIDEBALLOONTIP, 0, 0L); } #endif // (_WIN32_WINNT >= 0x0501) #if (_WIN32_WINNT >= 0x0600) DWORD GetHilite() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, EM_GETHILITE, 0, 0L); } void GetHilite(int& nStartChar, int& nEndChar) const { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, EM_GETHILITE, 0, 0L); nStartChar = (int)(short)LOWORD(dwRet); nEndChar = (int)(short)HIWORD(dwRet); } void SetHilite(int nStartChar, int nEndChar) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETHILITE, nStartChar, nEndChar); } #endif // (_WIN32_WINNT >= 0x0600) // Clipboard operations BOOL Undo() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_UNDO, 0, 0L); } void Clear() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_CLEAR, 0, 0L); } void Copy() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_COPY, 0, 0L); } void Cut() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_CUT, 0, 0L); } void Paste() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_PASTE, 0, 0L); } #ifdef WIN32_PLATFORM_WFSP // SmartPhone only messages DWORD GetExtendedStyle() { return SendMessage(EM_GETEXTENDEDSTYLE); } DWORD SetExtendedStyle(DWORD dwMask, DWORD dwExStyle) { return SendMessage(EM_SETEXTENDEDSTYLE, (WPARAM)dwMask, (LPARAM)dwExStyle); } DWORD GetInputMode(BOOL bCurrentMode = TRUE) { return SendMessage(EM_GETINPUTMODE, 0, (LPARAM)bCurrentMode); } BOOL SetInputMode(DWORD dwMode) { return SendMessage(EM_SETINPUTMODE, 0, (LPARAM)dwMode); } BOOL SetSymbols(LPCTSTR szSymbols) { return SendMessage(EM_SETSYMBOLS, 0, (LPARAM)szSymbols); } BOOL ResetSymbols() { return SendMessage(EM_SETSYMBOLS); } #endif // WIN32_PLATFORM_WFSP }; typedef CEditT CEdit; /////////////////////////////////////////////////////////////////////////////// // CEditCommands - message handlers for standard EDIT commands // Chain to CEditCommands message map. Your class must also derive from CEdit. // Example: // class CMyEdit : public CWindowImpl, // public CEditCommands // { // public: // BEGIN_MSG_MAP(CMyEdit) // // your handlers... // CHAIN_MSG_MAP_ALT(CEditCommands, 1) // END_MSG_MAP() // // other stuff... // }; template class CEditCommands { public: BEGIN_MSG_MAP(CEditCommands< T >) ALT_MSG_MAP(1) COMMAND_ID_HANDLER(ID_EDIT_CLEAR, OnEditClear) COMMAND_ID_HANDLER(ID_EDIT_CLEAR_ALL, OnEditClearAll) COMMAND_ID_HANDLER(ID_EDIT_COPY, OnEditCopy) COMMAND_ID_HANDLER(ID_EDIT_CUT, OnEditCut) COMMAND_ID_HANDLER(ID_EDIT_PASTE, OnEditPaste) COMMAND_ID_HANDLER(ID_EDIT_SELECT_ALL, OnEditSelectAll) COMMAND_ID_HANDLER(ID_EDIT_UNDO, OnEditUndo) END_MSG_MAP() LRESULT OnEditClear(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->Clear(); return 0; } LRESULT OnEditClearAll(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->SetSel(0, -1); pT->Clear(); return 0; } LRESULT OnEditCopy(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->Copy(); return 0; } LRESULT OnEditCut(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->Cut(); return 0; } LRESULT OnEditPaste(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->Paste(); return 0; } LRESULT OnEditSelectAll(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->SetSel(0, -1); return 0; } LRESULT OnEditUndo(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->Undo(); return 0; } // State (update UI) helpers BOOL CanCut() const { return HasSelection(); } BOOL CanCopy() const { return HasSelection(); } BOOL CanClear() const { return HasSelection(); } BOOL CanSelectAll() const { return HasText(); } BOOL CanFind() const { return HasText(); } BOOL CanRepeat() const { return HasText(); } BOOL CanReplace() const { return HasText(); } BOOL CanClearAll() const { return HasText(); } // Implementation BOOL HasSelection() const { const T* pT = static_cast(this); int nMin, nMax; ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nMin, (LPARAM)&nMax); return (nMin != nMax); } BOOL HasText() const { const T* pT = static_cast(this); return (pT->GetWindowTextLength() > 0); } }; /////////////////////////////////////////////////////////////////////////////// // CScrollBar - client side for a Windows SCROLLBAR control template class CScrollBarT : public TBase { public: // Constructors CScrollBarT(HWND hWnd = NULL) : TBase(hWnd) { } CScrollBarT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return _T("SCROLLBAR"); } #ifndef _WIN32_WCE int GetScrollPos() const { ATLASSERT(::IsWindow(m_hWnd)); return ::GetScrollPos(m_hWnd, SB_CTL); } #endif // !_WIN32_WCE int SetScrollPos(int nPos, BOOL bRedraw = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return ::SetScrollPos(m_hWnd, SB_CTL, nPos, bRedraw); } #ifndef _WIN32_WCE void GetScrollRange(LPINT lpMinPos, LPINT lpMaxPos) const { ATLASSERT(::IsWindow(m_hWnd)); ::GetScrollRange(m_hWnd, SB_CTL, lpMinPos, lpMaxPos); } #endif // !_WIN32_WCE void SetScrollRange(int nMinPos, int nMaxPos, BOOL bRedraw = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SetScrollRange(m_hWnd, SB_CTL, nMinPos, nMaxPos, bRedraw); } BOOL GetScrollInfo(LPSCROLLINFO lpScrollInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return ::GetScrollInfo(m_hWnd, SB_CTL, lpScrollInfo); } int SetScrollInfo(LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return ::SetScrollInfo(m_hWnd, SB_CTL, lpScrollInfo, bRedraw); } #ifndef _WIN32_WCE int GetScrollLimit() const { int nMin = 0, nMax = 0; ::GetScrollRange(m_hWnd, SB_CTL, &nMin, &nMax); SCROLLINFO info = { sizeof(SCROLLINFO), SIF_PAGE }; if(::GetScrollInfo(m_hWnd, SB_CTL, &info)) nMax -= ((info.nPage - 1) > 0) ? (info.nPage - 1) : 0; return nMax; } #if (WINVER >= 0x0500) BOOL GetScrollBarInfo(PSCROLLBARINFO pScrollBarInfo) const { ATLASSERT(::IsWindow(m_hWnd)); #if (_WIN32_WINNT >= 0x0501) return (BOOL)::SendMessage(m_hWnd, SBM_GETSCROLLBARINFO, 0, (LPARAM)pScrollBarInfo); #else // !(_WIN32_WINNT >= 0x0501) return ::GetScrollBarInfo(m_hWnd, OBJID_CLIENT, pScrollBarInfo); #endif // !(_WIN32_WINNT >= 0x0501) } #endif // (WINVER >= 0x0500) // Operations void ShowScrollBar(BOOL bShow = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::ShowScrollBar(m_hWnd, SB_CTL, bShow); } BOOL EnableScrollBar(UINT nArrowFlags = ESB_ENABLE_BOTH) { ATLASSERT(::IsWindow(m_hWnd)); return ::EnableScrollBar(m_hWnd, SB_CTL, nArrowFlags); } #endif // !_WIN32_WCE }; typedef CScrollBarT CScrollBar; // --- Windows Common Controls --- /////////////////////////////////////////////////////////////////////////////// // CImageList // forward declarations template class CImageListT; typedef CImageListT CImageList; typedef CImageListT CImageListManaged; template class CImageListT { public: // Data members HIMAGELIST m_hImageList; // Constructor/destructor/operators CImageListT(HIMAGELIST hImageList = NULL) : m_hImageList(hImageList) { } ~CImageListT() { if(t_bManaged && (m_hImageList != NULL)) Destroy(); } CImageListT& operator =(HIMAGELIST hImageList) { Attach(hImageList); return *this; } void Attach(HIMAGELIST hImageList) { ATLASSERT(m_hImageList == NULL); ATLASSERT(hImageList != NULL); if(t_bManaged && (m_hImageList != NULL) && (m_hImageList != hImageList)) ImageList_Destroy(m_hImageList); m_hImageList = hImageList; } HIMAGELIST Detach() { HIMAGELIST hImageList = m_hImageList; m_hImageList = NULL; return hImageList; } operator HIMAGELIST() const { return m_hImageList; } bool IsNull() const { return (m_hImageList == NULL); } // Attributes int GetImageCount() const { ATLASSERT(m_hImageList != NULL); return ImageList_GetImageCount(m_hImageList); } COLORREF GetBkColor() const { ATLASSERT(m_hImageList != NULL); return ImageList_GetBkColor(m_hImageList); } COLORREF SetBkColor(COLORREF cr) { ATLASSERT(m_hImageList != NULL); return ImageList_SetBkColor(m_hImageList, cr); } BOOL GetImageInfo(int nImage, IMAGEINFO* pImageInfo) const { ATLASSERT(m_hImageList != NULL); return ImageList_GetImageInfo(m_hImageList, nImage, pImageInfo); } HICON GetIcon(int nIndex, UINT uFlags = ILD_NORMAL) const { ATLASSERT(m_hImageList != NULL); return ImageList_GetIcon(m_hImageList, nIndex, uFlags); } BOOL GetIconSize(int& cx, int& cy) const { ATLASSERT(m_hImageList != NULL); return ImageList_GetIconSize(m_hImageList, &cx, &cy); } BOOL GetIconSize(SIZE& size) const { ATLASSERT(m_hImageList != NULL); return ImageList_GetIconSize(m_hImageList, (int*)&size.cx, (int*)&size.cy); } BOOL SetIconSize(int cx, int cy) { ATLASSERT(m_hImageList != NULL); return ImageList_SetIconSize(m_hImageList, cx, cy); } BOOL SetIconSize(SIZE size) { ATLASSERT(m_hImageList != NULL); return ImageList_SetIconSize(m_hImageList, size.cx, size.cy); } BOOL SetImageCount(UINT uNewCount) { ATLASSERT(m_hImageList != NULL); return ImageList_SetImageCount(m_hImageList, uNewCount); } BOOL SetOverlayImage(int nImage, int nOverlay) { ATLASSERT(m_hImageList != NULL); return ImageList_SetOverlayImage(m_hImageList, nImage, nOverlay); } // Operations BOOL Create(int cx, int cy, UINT nFlags, int nInitial, int nGrow) { ATLASSERT(m_hImageList == NULL); m_hImageList = ImageList_Create(cx, cy, nFlags, nInitial, nGrow); return (m_hImageList != NULL) ? TRUE : FALSE; } BOOL Create(ATL::_U_STRINGorID bitmap, int cx, int nGrow, COLORREF crMask) { ATLASSERT(m_hImageList == NULL); m_hImageList = ImageList_LoadBitmap(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr, cx, nGrow, crMask); return (m_hImageList != NULL) ? TRUE : FALSE; } BOOL CreateFromImage(ATL::_U_STRINGorID image, int cx, int nGrow, COLORREF crMask, UINT uType, UINT uFlags = LR_DEFAULTCOLOR | LR_DEFAULTSIZE) { ATLASSERT(m_hImageList == NULL); m_hImageList = ImageList_LoadImage(ModuleHelper::GetResourceInstance(), image.m_lpstr, cx, nGrow, crMask, uType, uFlags); return (m_hImageList != NULL) ? TRUE : FALSE; } BOOL Merge(HIMAGELIST hImageList1, int nImage1, HIMAGELIST hImageList2, int nImage2, int dx, int dy) { ATLASSERT(m_hImageList == NULL); m_hImageList = ImageList_Merge(hImageList1, nImage1, hImageList2, nImage2, dx, dy); return (m_hImageList != NULL) ? TRUE : FALSE; } #ifndef _WIN32_WCE #ifdef __IStream_INTERFACE_DEFINED__ BOOL CreateFromStream(LPSTREAM lpStream) { ATLASSERT(m_hImageList == NULL); m_hImageList = ImageList_Read(lpStream); return (m_hImageList != NULL) ? TRUE : FALSE; } #endif // __IStream_INTERFACE_DEFINED__ #endif // !_WIN32_WCE BOOL Destroy() { if (m_hImageList == NULL) return FALSE; BOOL bRet = ImageList_Destroy(m_hImageList); if(bRet) m_hImageList = NULL; return bRet; } int Add(HBITMAP hBitmap, HBITMAP hBitmapMask = NULL) { ATLASSERT(m_hImageList != NULL); return ImageList_Add(m_hImageList, hBitmap, hBitmapMask); } int Add(HBITMAP hBitmap, COLORREF crMask) { ATLASSERT(m_hImageList != NULL); return ImageList_AddMasked(m_hImageList, hBitmap, crMask); } BOOL Remove(int nImage) { ATLASSERT(m_hImageList != NULL); return ImageList_Remove(m_hImageList, nImage); } BOOL RemoveAll() { ATLASSERT(m_hImageList != NULL); return ImageList_RemoveAll(m_hImageList); } BOOL Replace(int nImage, HBITMAP hBitmap, HBITMAP hBitmapMask) { ATLASSERT(m_hImageList != NULL); return ImageList_Replace(m_hImageList, nImage, hBitmap, hBitmapMask); } int AddIcon(HICON hIcon) { ATLASSERT(m_hImageList != NULL); return ImageList_AddIcon(m_hImageList, hIcon); } int ReplaceIcon(int nImage, HICON hIcon) { ATLASSERT(m_hImageList != NULL); return ImageList_ReplaceIcon(m_hImageList, nImage, hIcon); } HICON ExtractIcon(int nImage) { ATLASSERT(m_hImageList != NULL); return ImageList_ExtractIcon(NULL, m_hImageList, nImage); } BOOL Draw(HDC hDC, int nImage, int x, int y, UINT nStyle) { ATLASSERT(m_hImageList != NULL); ATLASSERT(hDC != NULL); return ImageList_Draw(m_hImageList, nImage, hDC, x, y, nStyle); } BOOL Draw(HDC hDC, int nImage, POINT pt, UINT nStyle) { ATLASSERT(m_hImageList != NULL); ATLASSERT(hDC != NULL); return ImageList_Draw(m_hImageList, nImage, hDC, pt.x, pt.y, nStyle); } BOOL DrawEx(int nImage, HDC hDC, int x, int y, int dx, int dy, COLORREF rgbBk, COLORREF rgbFg, UINT fStyle) { ATLASSERT(m_hImageList != NULL); ATLASSERT(hDC != NULL); return ImageList_DrawEx(m_hImageList, nImage, hDC, x, y, dx, dy, rgbBk, rgbFg, fStyle); } BOOL DrawEx(int nImage, HDC hDC, RECT& rect, COLORREF rgbBk, COLORREF rgbFg, UINT fStyle) { ATLASSERT(m_hImageList != NULL); ATLASSERT(hDC != NULL); return ImageList_DrawEx(m_hImageList, nImage, hDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, rgbBk, rgbFg, fStyle); } static BOOL DrawIndirect(IMAGELISTDRAWPARAMS* pimldp) { return ImageList_DrawIndirect(pimldp); } BOOL Copy(int nSrc, int nDst, UINT uFlags = ILCF_MOVE) { ATLASSERT(m_hImageList != NULL); return ImageList_Copy(m_hImageList, nDst, m_hImageList, nSrc, uFlags); } #ifdef __IStream_INTERFACE_DEFINED__ #ifndef _WIN32_WCE static HIMAGELIST Read(LPSTREAM lpStream) { return ImageList_Read(lpStream); } BOOL Write(LPSTREAM lpStream) { ATLASSERT(m_hImageList != NULL); return ImageList_Write(m_hImageList, lpStream); } #endif // !_WIN32_WCE #if (_WIN32_WINNT >= 0x0501) static HRESULT ReadEx(DWORD dwFlags, LPSTREAM lpStream, REFIID riid, PVOID* ppv) { return ImageList_ReadEx(dwFlags, lpStream, riid, ppv); } HRESULT WriteEx(DWORD dwFlags, LPSTREAM lpStream) { ATLASSERT(m_hImageList != NULL); return ImageList_WriteEx(m_hImageList, dwFlags, lpStream); } #endif // (_WIN32_WINNT >= 0x0501) #endif // __IStream_INTERFACE_DEFINED__ // Drag operations BOOL BeginDrag(int nImage, POINT ptHotSpot) { ATLASSERT(m_hImageList != NULL); return ImageList_BeginDrag(m_hImageList, nImage, ptHotSpot.x, ptHotSpot.y); } BOOL BeginDrag(int nImage, int xHotSpot, int yHotSpot) { ATLASSERT(m_hImageList != NULL); return ImageList_BeginDrag(m_hImageList, nImage, xHotSpot, yHotSpot); } static void EndDrag() { ImageList_EndDrag(); } static BOOL DragMove(POINT pt) { return ImageList_DragMove(pt.x, pt.y); } static BOOL DragMove(int x, int y) { return ImageList_DragMove(x, y); } BOOL SetDragCursorImage(int nDrag, POINT ptHotSpot) { ATLASSERT(m_hImageList != NULL); return ImageList_SetDragCursorImage(m_hImageList, nDrag, ptHotSpot.x, ptHotSpot.y); } BOOL SetDragCursorImage(int nDrag, int xHotSpot, int yHotSpot) { ATLASSERT(m_hImageList != NULL); return ImageList_SetDragCursorImage(m_hImageList, nDrag, xHotSpot, yHotSpot); } static BOOL DragShowNolock(BOOL bShow = TRUE) { return ImageList_DragShowNolock(bShow); } static CImageList GetDragImage(LPPOINT lpPoint, LPPOINT lpPointHotSpot) { return CImageList(ImageList_GetDragImage(lpPoint, lpPointHotSpot)); } static BOOL DragEnter(HWND hWnd, POINT point) { return ImageList_DragEnter(hWnd, point.x, point.y); } static BOOL DragEnter(HWND hWnd, int x, int y) { return ImageList_DragEnter(hWnd, x, y); } static BOOL DragLeave(HWND hWnd) { return ImageList_DragLeave(hWnd); } #if (_WIN32_IE >= 0x0400) CImageList Duplicate() const { ATLASSERT(m_hImageList != NULL); return CImageList(ImageList_Duplicate(m_hImageList)); } static CImageList Duplicate(HIMAGELIST hImageList) { ATLASSERT(hImageList != NULL); return CImageList(ImageList_Duplicate(hImageList)); } #endif // (_WIN32_IE >= 0x0400) }; /////////////////////////////////////////////////////////////////////////////// // CToolTipCtrl #ifndef _WIN32_WCE class CToolInfo : public TOOLINFO { public: CToolInfo(UINT nFlags, HWND hWnd, UINT nIDTool = 0, LPRECT lpRect = NULL, LPTSTR lpstrText = LPSTR_TEXTCALLBACK, LPARAM lUserParam = NULL) { Init(nFlags, hWnd, nIDTool, lpRect, lpstrText, lUserParam); } operator LPTOOLINFO() { return this; } operator LPARAM() { return (LPARAM)this; } void Init(UINT nFlags, HWND hWnd, UINT nIDTool = 0, LPRECT lpRect = NULL, LPTSTR lpstrText = LPSTR_TEXTCALLBACK, LPARAM lUserParam = NULL) { ATLASSERT(::IsWindow(hWnd)); memset(this, 0, sizeof(TOOLINFO)); cbSize = RunTimeHelper::SizeOf_TOOLINFO(); uFlags = nFlags; if(nIDTool == 0) { hwnd = ::GetParent(hWnd); uFlags |= TTF_IDISHWND; uId = (UINT_PTR)hWnd; } else { hwnd = hWnd; uId = nIDTool; } if(lpRect != NULL) rect = *lpRect; hinst = ModuleHelper::GetResourceInstance(); lpszText = lpstrText; lParam = lUserParam; } }; template class CToolTipCtrlT : public TBase { public: // Constructors CToolTipCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CToolTipCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return TOOLTIPS_CLASS; } void GetText(LPTOOLINFO lpToolInfo) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_GETTEXT, 0, (LPARAM)&lpToolInfo); } void GetText(LPTSTR lpstrText, HWND hWnd, UINT nIDTool = 0) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hWnd != NULL); CToolInfo ti(0, hWnd, nIDTool, NULL, lpstrText); ::SendMessage(m_hWnd, TTM_GETTEXT, 0, ti); } BOOL GetToolInfo(LPTOOLINFO lpToolInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TTM_GETTOOLINFO, 0, (LPARAM)lpToolInfo); } BOOL GetToolInfo(HWND hWnd, UINT nIDTool, UINT* puFlags, LPRECT lpRect, LPTSTR lpstrText) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hWnd != NULL); ATLASSERT(puFlags != NULL); ATLASSERT(lpRect != NULL); CToolInfo ti(0, hWnd, nIDTool, NULL, lpstrText); BOOL bRet = (BOOL)::SendMessage(m_hWnd, TTM_GETTOOLINFO, 0, ti); if(bRet != FALSE) { *puFlags = ti.uFlags; *lpRect = ti.rect; } return bRet; } void SetToolInfo(LPTOOLINFO lpToolInfo) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_SETTOOLINFO, 0, (LPARAM)lpToolInfo); } void SetToolRect(LPTOOLINFO lpToolInfo) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_NEWTOOLRECT, 0, (LPARAM)lpToolInfo); } void SetToolRect(HWND hWnd, UINT nIDTool, LPCRECT lpRect) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hWnd != NULL); ATLASSERT(nIDTool != 0); CToolInfo ti(0, hWnd, nIDTool, (LPRECT)lpRect, NULL); ::SendMessage(m_hWnd, TTM_NEWTOOLRECT, 0, ti); } int GetToolCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TTM_GETTOOLCOUNT, 0, 0L); } int GetDelayTime(DWORD dwType) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TTM_GETDELAYTIME, dwType, 0L); } void SetDelayTime(DWORD dwType, int nTime) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_SETDELAYTIME, dwType, MAKELPARAM(nTime, 0)); } void GetMargin(LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_GETMARGIN, 0, (LPARAM)lpRect); } void SetMargin(LPRECT lpRect) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_SETMARGIN, 0, (LPARAM)lpRect); } int GetMaxTipWidth() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TTM_GETMAXTIPWIDTH, 0, 0L); } int SetMaxTipWidth(int nWidth) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TTM_SETMAXTIPWIDTH, 0, nWidth); } COLORREF GetTipBkColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, TTM_GETTIPBKCOLOR, 0, 0L); } void SetTipBkColor(COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_SETTIPBKCOLOR, (WPARAM)clr, 0L); } COLORREF GetTipTextColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, TTM_GETTIPTEXTCOLOR, 0, 0L); } void SetTipTextColor(COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_SETTIPTEXTCOLOR, (WPARAM)clr, 0L); } BOOL GetCurrentTool(LPTOOLINFO lpToolInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TTM_GETCURRENTTOOL, 0, (LPARAM)lpToolInfo); } #if (_WIN32_IE >= 0x0500) SIZE GetBubbleSize(LPTOOLINFO lpToolInfo) const { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, TTM_GETBUBBLESIZE, 0, (LPARAM)lpToolInfo); SIZE size = { GET_X_LPARAM(dwRet), GET_Y_LPARAM(dwRet) }; return size; } BOOL SetTitle(UINT uIcon, LPCTSTR lpstrTitle) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TTM_SETTITLE, uIcon, (LPARAM)lpstrTitle); } #endif // (_WIN32_IE >= 0x0500) #if (_WIN32_WINNT >= 0x0501) void GetTitle(PTTGETTITLE pTTGetTitle) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_GETTITLE, 0, (LPARAM)pTTGetTitle); } void SetWindowTheme(LPCWSTR lpstrTheme) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_SETWINDOWTHEME, 0, (LPARAM)lpstrTheme); } #endif // (_WIN32_WINNT >= 0x0501) // Operations void Activate(BOOL bActivate) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_ACTIVATE, bActivate, 0L); } BOOL AddTool(LPTOOLINFO lpToolInfo) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TTM_ADDTOOL, 0, (LPARAM)lpToolInfo); } BOOL AddTool(HWND hWnd, ATL::_U_STRINGorID text = LPSTR_TEXTCALLBACK, LPCRECT lpRectTool = NULL, UINT nIDTool = 0) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hWnd != NULL); // the toolrect and toolid must both be zero or both valid ATLASSERT((lpRectTool != NULL && nIDTool != 0) || (lpRectTool == NULL && nIDTool == 0)); CToolInfo ti(0, hWnd, nIDTool, (LPRECT)lpRectTool, (LPTSTR)text.m_lpstr); return (BOOL)::SendMessage(m_hWnd, TTM_ADDTOOL, 0, ti); } void DelTool(LPTOOLINFO lpToolInfo) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_DELTOOL, 0, (LPARAM)lpToolInfo); } void DelTool(HWND hWnd, UINT nIDTool = 0) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hWnd != NULL); CToolInfo ti(0, hWnd, nIDTool, NULL, NULL); ::SendMessage(m_hWnd, TTM_DELTOOL, 0, ti); } BOOL HitTest(LPTTHITTESTINFO lpHitTestInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TTM_HITTEST, 0, (LPARAM)lpHitTestInfo); } BOOL HitTest(HWND hWnd, POINT pt, LPTOOLINFO lpToolInfo) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hWnd != NULL); ATLASSERT(lpToolInfo != NULL); TTHITTESTINFO hti = { 0 }; hti.ti.cbSize = RunTimeHelper::SizeOf_TOOLINFO(); hti.hwnd = hWnd; hti.pt.x = pt.x; hti.pt.y = pt.y; if((BOOL)::SendMessage(m_hWnd, TTM_HITTEST, 0, (LPARAM)&hti) != FALSE) { *lpToolInfo = hti.ti; return TRUE; } return FALSE; } void RelayEvent(LPMSG lpMsg) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_RELAYEVENT, 0, (LPARAM)lpMsg); } void UpdateTipText(LPTOOLINFO lpToolInfo) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_UPDATETIPTEXT, 0, (LPARAM)lpToolInfo); } void UpdateTipText(ATL::_U_STRINGorID text, HWND hWnd, UINT nIDTool = 0) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hWnd != NULL); CToolInfo ti(0, hWnd, nIDTool, NULL, (LPTSTR)text.m_lpstr); ::SendMessage(m_hWnd, TTM_UPDATETIPTEXT, 0, ti); } BOOL EnumTools(UINT nTool, LPTOOLINFO lpToolInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TTM_ENUMTOOLS, nTool, (LPARAM)lpToolInfo); } void Pop() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_POP, 0, 0L); } void TrackActivate(LPTOOLINFO lpToolInfo, BOOL bActivate) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_TRACKACTIVATE, bActivate, (LPARAM)lpToolInfo); } void TrackActivate(HWND hWnd, UINT nIDTool, BOOL bActivate) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hWnd != NULL); CToolInfo ti(0, hWnd, nIDTool); ::SendMessage(m_hWnd, TTM_TRACKACTIVATE, bActivate, ti); } void TrackPosition(int xPos, int yPos) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_TRACKPOSITION, 0, MAKELPARAM(xPos, yPos)); } #if (_WIN32_IE >= 0x0400) void Update() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_UPDATE, 0, 0L); } #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) BOOL AdjustRect(LPRECT lpRect, BOOL bLarger /*= TRUE*/) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TTM_ADJUSTRECT, bLarger, (LPARAM)lpRect); } #endif // (_WIN32_IE >= 0x0500) #if (_WIN32_WINNT >= 0x0501) void Popup() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TTM_POPUP, 0, 0L); } #endif // (_WIN32_WINNT >= 0x0501) }; typedef CToolTipCtrlT CToolTipCtrl; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CHeaderCtrl template class CHeaderCtrlT : public TBase { public: // Constructors CHeaderCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CHeaderCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return WC_HEADER; } int GetItemCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, HDM_GETITEMCOUNT, 0, 0L); } BOOL GetItem(int nIndex, LPHDITEM pHeaderItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, HDM_GETITEM, nIndex, (LPARAM)pHeaderItem); } BOOL SetItem(int nIndex, LPHDITEM pHeaderItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, HDM_SETITEM, nIndex, (LPARAM)pHeaderItem); } CImageList GetImageList() const { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, HDM_GETIMAGELIST, 0, 0L)); } CImageList SetImageList(HIMAGELIST hImageList) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, HDM_SETIMAGELIST, 0, (LPARAM)hImageList)); } BOOL GetOrderArray(int nSize, int* lpnArray) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, HDM_GETORDERARRAY, nSize, (LPARAM)lpnArray); } BOOL SetOrderArray(int nSize, int* lpnArray) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, HDM_SETORDERARRAY, nSize, (LPARAM)lpnArray); } BOOL GetItemRect(int nIndex, LPRECT lpItemRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, HDM_GETITEMRECT, nIndex, (LPARAM)lpItemRect); } int SetHotDivider(BOOL bPos, DWORD dwInputValue) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, HDM_SETHOTDIVIDER, bPos, dwInputValue); } #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) BOOL GetUnicodeFormat() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, HDM_GETUNICODEFORMAT, 0, 0L); } BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, HDM_SETUNICODEFORMAT, bUnicode, 0L); } #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) int GetBitmapMargin() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, HDM_GETBITMAPMARGIN, 0, 0L); } int SetBitmapMargin(int nWidth) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, HDM_SETBITMAPMARGIN, nWidth, 0L); } int SetFilterChangeTimeout(DWORD dwTimeOut) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, HDM_SETFILTERCHANGETIMEOUT, 0, dwTimeOut); } #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) #if (_WIN32_WINNT >= 0x0600) BOOL GetItemDropDownRect(int nIndex, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, HDM_GETITEMDROPDOWNRECT, nIndex, (LPARAM)lpRect); } BOOL GetOverflowRect(LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, HDM_GETOVERFLOWRECT, 0, (LPARAM)lpRect); } int GetFocusedItem() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, HDM_GETFOCUSEDITEM, 0, 0L); } BOOL SetFocusedItem(int nIndex) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, HDM_SETFOCUSEDITEM, 0, nIndex); } #endif // (_WIN32_WINNT >= 0x0600) // Operations int InsertItem(int nIndex, LPHDITEM phdi) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, HDM_INSERTITEM, nIndex, (LPARAM)phdi); } int AddItem(LPHDITEM phdi) { return InsertItem(GetItemCount(), phdi); } BOOL DeleteItem(int nIndex) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, HDM_DELETEITEM, nIndex, 0L); } BOOL Layout(HD_LAYOUT* pHeaderLayout) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, HDM_LAYOUT, 0, (LPARAM)pHeaderLayout); } int HitTest(LPHDHITTESTINFO lpHitTestInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, HDM_HITTEST, 0, (LPARAM)lpHitTestInfo); } int OrderToIndex(int nOrder) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, HDM_ORDERTOINDEX, nOrder, 0L); } CImageList CreateDragImage(int nIndex) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, HDM_CREATEDRAGIMAGE, nIndex, 0L)); } #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) int EditFilter(int nColumn, BOOL bDiscardChanges) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, HDM_EDITFILTER, nColumn, MAKELPARAM(bDiscardChanges, 0)); } int ClearFilter(int nColumn) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, HDM_CLEARFILTER, nColumn, 0L); } int ClearAllFilters() { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, HDM_CLEARFILTER, (WPARAM)-1, 0L); } #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) }; typedef CHeaderCtrlT CHeaderCtrl; /////////////////////////////////////////////////////////////////////////////// // CListViewCtrl template class CListViewCtrlT : public TBase { public: // Constructors CListViewCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CListViewCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return WC_LISTVIEW; } COLORREF GetBkColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, LVM_GETBKCOLOR, 0, 0L); } BOOL SetBkColor(COLORREF cr) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETBKCOLOR, 0, cr); } CImageList GetImageList(int nImageListType) const { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, LVM_GETIMAGELIST, nImageListType, 0L)); } CImageList SetImageList(HIMAGELIST hImageList, int nImageList) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, LVM_SETIMAGELIST, nImageList, (LPARAM)hImageList)); } int GetItemCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETITEMCOUNT, 0, 0L); } BOOL SetItemCount(int nItems) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETITEMCOUNT, nItems, 0L); } BOOL GetItem(LPLVITEM pItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETITEM, 0, (LPARAM)pItem); } BOOL SetItem(const LVITEM* pItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETITEM, 0, (LPARAM)pItem); } BOOL SetItem(int nItem, int nSubItem, UINT nMask, LPCTSTR lpszItem, int nImage, UINT nState, UINT nStateMask, LPARAM lParam) { ATLASSERT(::IsWindow(m_hWnd)); LVITEM lvi = { 0 }; lvi.mask = nMask; lvi.iItem = nItem; lvi.iSubItem = nSubItem; lvi.stateMask = nStateMask; lvi.state = nState; lvi.pszText = (LPTSTR) lpszItem; lvi.iImage = nImage; lvi.lParam = lParam; return (BOOL)::SendMessage(m_hWnd, LVM_SETITEM, 0, (LPARAM)&lvi); } UINT GetItemState(int nItem, UINT nMask) const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, LVM_GETITEMSTATE, nItem, nMask); } BOOL SetItemState(int nItem, UINT nState, UINT nStateMask) { ATLASSERT(::IsWindow(m_hWnd)); LVITEM lvi = { 0 }; lvi.state = nState; lvi.stateMask = nStateMask; return (BOOL)::SendMessage(m_hWnd, LVM_SETITEMSTATE, nItem, (LPARAM)&lvi); } BOOL SetItemState(int nItem, LPLVITEM pItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETITEMSTATE, nItem, (LPARAM)pItem); } #ifndef _ATL_NO_COM BOOL GetItemText(int nItem, int nSubItem, BSTR& bstrText) const { USES_CONVERSION; ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(bstrText == NULL); LVITEM lvi = { 0 }; lvi.iSubItem = nSubItem; LPTSTR lpstrText = NULL; int nRes = 0; for(int nLen = 256; ; nLen *= 2) { ATLTRY(lpstrText = new TCHAR[nLen]); if(lpstrText == NULL) break; lpstrText[0] = NULL; lvi.cchTextMax = nLen; lvi.pszText = lpstrText; nRes = (int)::SendMessage(m_hWnd, LVM_GETITEMTEXT, (WPARAM)nItem, (LPARAM)&lvi); if(nRes < nLen - 1) break; delete [] lpstrText; lpstrText = NULL; } if(lpstrText != NULL) { if(nRes != 0) bstrText = ::SysAllocString(T2OLE(lpstrText)); delete [] lpstrText; } return (bstrText != NULL) ? TRUE : FALSE; } #endif // !_ATL_NO_COM #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) int GetItemText(int nItem, int nSubItem, _CSTRING_NS::CString& strText) const { ATLASSERT(::IsWindow(m_hWnd)); LVITEM lvi = { 0 }; lvi.iSubItem = nSubItem; strText.Empty(); int nRes = 0; for(int nLen = 256; ; nLen *= 2) { lvi.cchTextMax = nLen; lvi.pszText = strText.GetBufferSetLength(nLen); if(lvi.pszText == NULL) { nRes = 0; break; } nRes = (int)::SendMessage(m_hWnd, LVM_GETITEMTEXT, (WPARAM)nItem, (LPARAM)&lvi); if(nRes < nLen - 1) break; } strText.ReleaseBuffer(); return nRes; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) int GetItemText(int nItem, int nSubItem, LPTSTR lpszText, int nLen) const { ATLASSERT(::IsWindow(m_hWnd)); LVITEM lvi = { 0 }; lvi.iSubItem = nSubItem; lvi.cchTextMax = nLen; lvi.pszText = lpszText; return (int)::SendMessage(m_hWnd, LVM_GETITEMTEXT, (WPARAM)nItem, (LPARAM)&lvi); } BOOL SetItemText(int nItem, int nSubItem, LPCTSTR lpszText) { ATLASSERT(::IsWindow(m_hWnd)); return SetItem(nItem, nSubItem, LVIF_TEXT, lpszText, 0, 0, 0, 0); } DWORD_PTR GetItemData(int nItem) const { ATLASSERT(::IsWindow(m_hWnd)); LVITEM lvi = { 0 }; lvi.iItem = nItem; lvi.mask = LVIF_PARAM; BOOL bRet = (BOOL)::SendMessage(m_hWnd, LVM_GETITEM, 0, (LPARAM)&lvi); return (DWORD_PTR)(bRet ? lvi.lParam : NULL); } BOOL SetItemData(int nItem, DWORD_PTR dwData) { ATLASSERT(::IsWindow(m_hWnd)); return SetItem(nItem, 0, LVIF_PARAM, NULL, 0, 0, 0, (LPARAM)dwData); } UINT GetCallbackMask() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, LVM_GETCALLBACKMASK, 0, 0L); } BOOL SetCallbackMask(UINT nMask) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETCALLBACKMASK, nMask, 0L); } BOOL GetItemPosition(int nItem, LPPOINT lpPoint) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETITEMPOSITION, nItem, (LPARAM)lpPoint); } BOOL SetItemPosition(int nItem, POINT pt) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(((GetStyle() & LVS_TYPEMASK) == LVS_ICON) || ((GetStyle() & LVS_TYPEMASK) == LVS_SMALLICON)); return (BOOL)::SendMessage(m_hWnd, LVM_SETITEMPOSITION32, nItem, (LPARAM)&pt); } BOOL SetItemPosition(int nItem, int x, int y) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(((GetStyle() & LVS_TYPEMASK) == LVS_ICON) || ((GetStyle() & LVS_TYPEMASK) == LVS_SMALLICON)); POINT pt = { x, y }; return (BOOL)::SendMessage(m_hWnd, LVM_SETITEMPOSITION32, nItem, (LPARAM)&pt); } int GetStringWidth(LPCTSTR lpsz) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETSTRINGWIDTH, 0, (LPARAM)lpsz); } CEdit GetEditControl() const { ATLASSERT(::IsWindow(m_hWnd)); return CEdit((HWND)::SendMessage(m_hWnd, LVM_GETEDITCONTROL, 0, 0L)); } BOOL GetColumn(int nCol, LVCOLUMN* pColumn) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETCOLUMN, nCol, (LPARAM)pColumn); } BOOL SetColumn(int nCol, const LVCOLUMN* pColumn) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETCOLUMN, nCol, (LPARAM)pColumn); } int GetColumnWidth(int nCol) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETCOLUMNWIDTH, nCol, 0L); } BOOL SetColumnWidth(int nCol, int cx) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETCOLUMNWIDTH, nCol, MAKELPARAM(cx, 0)); } BOOL GetViewRect(LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETVIEWRECT, 0, (LPARAM)lpRect); } COLORREF GetTextColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, LVM_GETTEXTCOLOR, 0, 0L); } BOOL SetTextColor(COLORREF cr) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETTEXTCOLOR, 0, cr); } COLORREF GetTextBkColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, LVM_GETTEXTBKCOLOR, 0, 0L); } BOOL SetTextBkColor(COLORREF cr) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETTEXTBKCOLOR, 0, cr); } int GetTopIndex() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETTOPINDEX, 0, 0L); } int GetCountPerPage() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETCOUNTPERPAGE, 0, 0L); } BOOL GetOrigin(LPPOINT lpPoint) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETORIGIN, 0, (LPARAM)lpPoint); } UINT GetSelectedCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, LVM_GETSELECTEDCOUNT, 0, 0L); } BOOL GetItemRect(int nItem, LPRECT lpRect, UINT nCode) const { ATLASSERT(::IsWindow(m_hWnd)); lpRect->left = nCode; return (BOOL)::SendMessage(m_hWnd, LVM_GETITEMRECT, (WPARAM)nItem, (LPARAM)lpRect); } #ifndef _WIN32_WCE HCURSOR GetHotCursor() const { ATLASSERT(::IsWindow(m_hWnd)); return (HCURSOR)::SendMessage(m_hWnd, LVM_GETHOTCURSOR, 0, 0L); } HCURSOR SetHotCursor(HCURSOR hHotCursor) { ATLASSERT(::IsWindow(m_hWnd)); return (HCURSOR)::SendMessage(m_hWnd, LVM_SETHOTCURSOR, 0, (LPARAM)hHotCursor); } int GetHotItem() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETHOTITEM, 0, 0L); } int SetHotItem(int nIndex) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_SETHOTITEM, nIndex, 0L); } #endif // !_WIN32_WCE BOOL GetColumnOrderArray(int nCount, int* lpnArray) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETCOLUMNORDERARRAY, nCount, (LPARAM)lpnArray); } BOOL SetColumnOrderArray(int nCount, int* lpnArray) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETCOLUMNORDERARRAY, nCount, (LPARAM)lpnArray); } CHeaderCtrl GetHeader() const { ATLASSERT(::IsWindow(m_hWnd)); return CHeaderCtrl((HWND)::SendMessage(m_hWnd, LVM_GETHEADER, 0, 0L)); } BOOL GetSubItemRect(int nItem, int nSubItem, int nFlag, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & LVS_TYPEMASK) == LVS_REPORT); ATLASSERT(lpRect != NULL); lpRect->top = nSubItem; lpRect->left = nFlag; return (BOOL)::SendMessage(m_hWnd, LVM_GETSUBITEMRECT, nItem, (LPARAM)lpRect); } DWORD SetIconSpacing(int cx, int cy) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & LVS_TYPEMASK) == LVS_ICON); return (DWORD)::SendMessage(m_hWnd, LVM_SETICONSPACING, 0, MAKELPARAM(cx, cy)); } int GetISearchString(LPTSTR lpstr) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETISEARCHSTRING, 0, (LPARAM)lpstr); } void GetItemSpacing(SIZE& sizeSpacing, BOOL bSmallIconView = FALSE) const { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, LVM_GETITEMSPACING, bSmallIconView, 0L); sizeSpacing.cx = GET_X_LPARAM(dwRet); sizeSpacing.cy = GET_Y_LPARAM(dwRet); } #if (_WIN32_WCE >= 410) void SetItemSpacing(INT cySpacing) { ATLASSERT(::IsWindow(m_hWnd)); ListView_SetItemSpacing(m_hWnd, cySpacing); } #endif // (_WIN32_WCE >= 410) // single-selection only int GetSelectedIndex() const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & LVS_SINGLESEL) != 0); return (int)::SendMessage(m_hWnd, LVM_GETNEXTITEM, (WPARAM)-1, MAKELPARAM(LVNI_ALL | LVNI_SELECTED, 0)); } BOOL GetSelectedItem(LPLVITEM pItem) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & LVS_SINGLESEL) != 0); ATLASSERT(pItem != NULL); pItem->iItem = (int)::SendMessage(m_hWnd, LVM_GETNEXTITEM, (WPARAM)-1, MAKELPARAM(LVNI_ALL | LVNI_SELECTED, 0)); if(pItem->iItem == -1) return FALSE; return (BOOL)::SendMessage(m_hWnd, LVM_GETITEM, 0, (LPARAM)pItem); } // extended list view styles DWORD GetExtendedListViewStyle() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0L); } // dwExMask = 0 means all styles DWORD SetExtendedListViewStyle(DWORD dwExStyle, DWORD dwExMask = 0) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, dwExMask, dwExStyle); } // checkboxes only BOOL GetCheckState(int nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetExtendedListViewStyle() & LVS_EX_CHECKBOXES) != 0); UINT uRet = GetItemState(nIndex, LVIS_STATEIMAGEMASK); return (uRet >> 12) - 1; } BOOL SetCheckState(int nItem, BOOL bCheck) { int nCheck = bCheck ? 2 : 1; // one based index return SetItemState(nItem, INDEXTOSTATEIMAGEMASK(nCheck), LVIS_STATEIMAGEMASK); } // view type DWORD GetViewType() const { ATLASSERT(::IsWindow(m_hWnd)); return (GetStyle() & LVS_TYPEMASK); } DWORD SetViewType(DWORD dwType) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(dwType == LVS_ICON || dwType == LVS_SMALLICON || dwType == LVS_LIST || dwType == LVS_REPORT); DWORD dwOldType = GetViewType(); if(dwType != dwOldType) ModifyStyle(LVS_TYPEMASK, (dwType & LVS_TYPEMASK)); return dwOldType; } #if (_WIN32_IE >= 0x0400) #ifndef _WIN32_WCE BOOL GetBkImage(LPLVBKIMAGE plvbki) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETBKIMAGE, 0, (LPARAM)plvbki); } BOOL SetBkImage(LPLVBKIMAGE plvbki) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETBKIMAGE, 0, (LPARAM)plvbki); } #endif // !_WIN32_WCE int GetSelectionMark() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETSELECTIONMARK, 0, 0L); } int SetSelectionMark(int nIndex) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_SETSELECTIONMARK, 0, nIndex); } #ifndef _WIN32_WCE BOOL GetWorkAreas(int nWorkAreas, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETWORKAREAS, nWorkAreas, (LPARAM)lpRect); } BOOL SetWorkAreas(int nWorkAreas, LPRECT lpRect) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETWORKAREAS, nWorkAreas, (LPARAM)lpRect); } DWORD GetHoverTime() const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetExtendedListViewStyle() & (LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE)) != 0); return (DWORD)::SendMessage(m_hWnd, LVM_GETHOVERTIME, 0, 0L); } DWORD SetHoverTime(DWORD dwHoverTime) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetExtendedListViewStyle() & (LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE)) != 0); return (DWORD)::SendMessage(m_hWnd, LVM_SETHOVERTIME, 0, dwHoverTime); } BOOL GetNumberOfWorkAreas(int* pnWorkAreas) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETNUMBEROFWORKAREAS, 0, (LPARAM)pnWorkAreas); } #endif // !_WIN32_WCE BOOL SetItemCountEx(int nItems, DWORD dwFlags) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(((GetStyle() & LVS_OWNERDATA) != 0) && (((GetStyle() & LVS_TYPEMASK) == LVS_REPORT) || ((GetStyle() & LVS_TYPEMASK) == LVS_LIST))); return (BOOL)::SendMessage(m_hWnd, LVM_SETITEMCOUNT, nItems, dwFlags); } #ifndef _WIN32_WCE CToolTipCtrl GetToolTips() const { ATLASSERT(::IsWindow(m_hWnd)); return CToolTipCtrl((HWND)::SendMessage(m_hWnd, LVM_GETTOOLTIPS, 0, 0L)); } CToolTipCtrl SetToolTips(HWND hWndTT) { ATLASSERT(::IsWindow(m_hWnd)); return CToolTipCtrl((HWND)::SendMessage(m_hWnd, LVM_SETTOOLTIPS, (WPARAM)hWndTT, 0L)); } BOOL GetUnicodeFormat() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETUNICODEFORMAT, 0, 0L); } BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETUNICODEFORMAT, bUnicode, 0L); } #endif // !_WIN32_WCE #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_WINNT >= 0x0501) int GetSelectedColumn() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETSELECTEDCOLUMN, 0, 0L); } void SetSelectedColumn(int nColumn) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LVM_SETSELECTEDCOLUMN, nColumn, 0L); } DWORD GetView() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, LVM_GETVIEW, 0, 0L); } int SetView(DWORD dwView) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_SETVIEW, dwView, 0L); } BOOL IsGroupViewEnabled() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_ISGROUPVIEWENABLED, 0, 0L); } int GetGroupInfo(int nGroupID, PLVGROUP pGroup) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETGROUPINFO, nGroupID, (LPARAM)pGroup); } int SetGroupInfo(int nGroupID, PLVGROUP pGroup) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_SETGROUPINFO, nGroupID, (LPARAM)pGroup); } void GetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LVM_GETGROUPMETRICS, 0, (LPARAM)pGroupMetrics); } void SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LVM_SETGROUPMETRICS, 0, (LPARAM)pGroupMetrics); } void GetTileViewInfo(PLVTILEVIEWINFO pTileViewInfo) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LVM_GETTILEVIEWINFO, 0, (LPARAM)pTileViewInfo); } BOOL SetTileViewInfo(PLVTILEVIEWINFO pTileViewInfo) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETTILEVIEWINFO, 0, (LPARAM)pTileViewInfo); } void GetTileInfo(PLVTILEINFO pTileInfo) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LVM_GETTILEINFO, 0, (LPARAM)pTileInfo); } BOOL SetTileInfo(PLVTILEINFO pTileInfo) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETTILEINFO, 0, (LPARAM)pTileInfo); } BOOL GetInsertMark(LPLVINSERTMARK pInsertMark) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETINSERTMARK, 0, (LPARAM)pInsertMark); } BOOL SetInsertMark(LPLVINSERTMARK pInsertMark) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETINSERTMARK, 0, (LPARAM)pInsertMark); } int GetInsertMarkRect(LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETINSERTMARKRECT, 0, (LPARAM)lpRect); } COLORREF GetInsertMarkColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, LVM_GETINSERTMARKCOLOR, 0, 0L); } COLORREF SetInsertMarkColor(COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, LVM_SETINSERTMARKCOLOR, 0, clr); } COLORREF GetOutlineColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, LVM_GETOUTLINECOLOR, 0, 0L); } COLORREF SetOutlineColor(COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, LVM_SETOUTLINECOLOR, 0, clr); } #endif // (_WIN32_WINNT >= 0x0501) #if (_WIN32_WINNT >= 0x0600) int GetGroupCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETGROUPCOUNT, 0, 0L); } BOOL GetGroupInfoByIndex(int nIndex, PLVGROUP pGroup) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETGROUPINFOBYINDEX, nIndex, (LPARAM)pGroup); } BOOL GetGroupRect(int nGroupID, int nType, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(lpRect != NULL); if(lpRect != NULL) lpRect->top = nType; return (BOOL)::SendMessage(m_hWnd, LVM_GETGROUPRECT, nGroupID, (LPARAM)lpRect); } UINT GetGroupState(int nGroupID, UINT uMask) const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, LVM_GETGROUPSTATE, nGroupID, (LPARAM)uMask); } int GetFocusedGroup() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETFOCUSEDGROUP, 0, 0L); } BOOL GetEmptyText(LPWSTR lpstrText, int cchText) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETEMPTYTEXT, cchText, (LPARAM)lpstrText); } BOOL GetFooterRect(LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETFOOTERRECT, 0, (LPARAM)lpRect); } BOOL GetFooterInfo(LPLVFOOTERINFO lpFooterInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETFOOTERINFO, 0, (LPARAM)lpFooterInfo); } BOOL GetFooterItemRect(int nItem, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETFOOTERITEMRECT, nItem, (LPARAM)lpRect); } BOOL GetFooterItem(int nItem, LPLVFOOTERITEM lpFooterItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETFOOTERITEM, nItem, (LPARAM)lpFooterItem); } BOOL GetItemIndexRect(PLVITEMINDEX pItemIndex, int nSubItem, int nType, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pItemIndex != NULL); ATLASSERT(lpRect != NULL); if(lpRect != NULL) { lpRect->top = nSubItem; lpRect->left = nType; } return (BOOL)::SendMessage(m_hWnd, LVM_GETITEMINDEXRECT, (WPARAM)pItemIndex, (LPARAM)lpRect); } BOOL SetItemIndexState(PLVITEMINDEX pItemIndex, UINT uState, UINT dwMask) { ATLASSERT(::IsWindow(m_hWnd)); LVITEM lvi = { 0 }; lvi.state = uState; lvi.stateMask = dwMask; return (BOOL)::SendMessage(m_hWnd, LVM_SETITEMINDEXSTATE, (WPARAM)pItemIndex, (LPARAM)&lvi); } BOOL GetNextItemIndex(PLVITEMINDEX pItemIndex, WORD wFlags) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_GETNEXTITEMINDEX, (WPARAM)pItemIndex, MAKELPARAM(wFlags, 0)); } #endif // (_WIN32_WINNT >= 0x0600) // Operations int InsertColumn(int nCol, const LVCOLUMN* pColumn) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_INSERTCOLUMN, nCol, (LPARAM)pColumn); } int InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat = LVCFMT_LEFT, int nWidth = -1, int nSubItem = -1, int iImage = -1, int iOrder = -1) { LVCOLUMN column = { 0 }; column.mask = LVCF_TEXT|LVCF_FMT; column.pszText = (LPTSTR)lpszColumnHeading; column.fmt = nFormat; if (nWidth != -1) { column.mask |= LVCF_WIDTH; column.cx = nWidth; } if (nSubItem != -1) { column.mask |= LVCF_SUBITEM; column.iSubItem = nSubItem; } if (iImage != -1) { column.mask |= LVCF_IMAGE; column.iImage = iImage; } if (iOrder != -1) { column.mask |= LVCF_ORDER; column.iOrder = iOrder; } return InsertColumn(nCol, &column); } BOOL DeleteColumn(int nCol) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_DELETECOLUMN, nCol, 0L); } int InsertItem(UINT nMask, int nItem, LPCTSTR lpszItem, UINT nState, UINT nStateMask, int nImage, LPARAM lParam) { ATLASSERT(::IsWindow(m_hWnd)); LVITEM item = { 0 }; item.mask = nMask; item.iItem = nItem; item.iSubItem = 0; item.pszText = (LPTSTR)lpszItem; item.state = nState; item.stateMask = nStateMask; item.iImage = nImage; item.lParam = lParam; return InsertItem(&item); } int InsertItem(const LVITEM* pItem) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_INSERTITEM, 0, (LPARAM)pItem); } int InsertItem(int nItem, LPCTSTR lpszItem) { ATLASSERT(::IsWindow(m_hWnd)); return InsertItem(LVIF_TEXT, nItem, lpszItem, 0, 0, 0, 0); } int InsertItem(int nItem, LPCTSTR lpszItem, int nImage) { ATLASSERT(::IsWindow(m_hWnd)); return InsertItem(LVIF_TEXT|LVIF_IMAGE, nItem, lpszItem, 0, 0, nImage, 0); } int GetNextItem(int nItem, int nFlags) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_GETNEXTITEM, nItem, MAKELPARAM(nFlags, 0)); } BOOL DeleteItem(int nItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_DELETEITEM, nItem, 0L); } BOOL DeleteAllItems() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_DELETEALLITEMS, 0, 0L); } int FindItem(LVFINDINFO* pFindInfo, int nStart = -1) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_FINDITEM, nStart, (LPARAM)pFindInfo); } int FindItem(LPCTSTR lpstrFind, bool bPartial = true, bool bWrap = false, int nStart = -1) const { ATLASSERT(::IsWindow(m_hWnd)); LVFINDINFO lvfi = { 0 }; lvfi.flags = LVFI_STRING | (bWrap ? LVFI_WRAP : 0) | (bPartial ? LVFI_PARTIAL : 0); lvfi.psz = lpstrFind; return (int)::SendMessage(m_hWnd, LVM_FINDITEM, nStart, (LPARAM)&lvfi); } int HitTest(LVHITTESTINFO* pHitTestInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_HITTEST, 0, (LPARAM)pHitTestInfo); } int HitTest(POINT pt, UINT* pFlags) const { ATLASSERT(::IsWindow(m_hWnd)); LVHITTESTINFO hti = { 0 }; hti.pt = pt; int nRes = (int)::SendMessage(m_hWnd, LVM_HITTEST, 0, (LPARAM)&hti); if (pFlags != NULL) *pFlags = hti.flags; return nRes; } BOOL EnsureVisible(int nItem, BOOL bPartialOK) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_ENSUREVISIBLE, nItem, MAKELPARAM(bPartialOK, 0)); } BOOL Scroll(SIZE size) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SCROLL, size.cx, size.cy); } BOOL RedrawItems(int nFirst, int nLast) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_REDRAWITEMS, nFirst, nLast); } BOOL Arrange(UINT nCode) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_ARRANGE, nCode, 0L); } CEdit EditLabel(int nItem) { ATLASSERT(::IsWindow(m_hWnd)); return CEdit((HWND)::SendMessage(m_hWnd, LVM_EDITLABEL, nItem, 0L)); } BOOL Update(int nItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_UPDATE, nItem, 0L); } BOOL SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SORTITEMS, (WPARAM)lParamSort, (LPARAM)pfnCompare); } CImageList RemoveImageList(int nImageList) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, LVM_SETIMAGELIST, (WPARAM)nImageList, NULL)); } CImageList CreateDragImage(int nItem, LPPOINT lpPoint) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, LVM_CREATEDRAGIMAGE, nItem, (LPARAM)lpPoint)); } DWORD ApproximateViewRect(int cx = -1, int cy = -1, int nCount = -1) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, LVM_APPROXIMATEVIEWRECT, nCount, MAKELPARAM(cx, cy)); } int SubItemHitTest(LPLVHITTESTINFO lpInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_SUBITEMHITTEST, 0, (LPARAM)lpInfo); } int AddColumn(LPCTSTR strItem, int nItem, int nSubItem = -1, int nMask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, int nFmt = LVCFMT_LEFT) { const int cxOffset = 15; ATLASSERT(::IsWindow(m_hWnd)); LVCOLUMN lvc = { 0 }; lvc.mask = nMask; lvc.fmt = nFmt; lvc.pszText = (LPTSTR)strItem; lvc.cx = GetStringWidth(lvc.pszText) + cxOffset; if(nMask & LVCF_SUBITEM) lvc.iSubItem = (nSubItem != -1) ? nSubItem : nItem; return InsertColumn(nItem, &lvc); } int AddItem(int nItem, int nSubItem, LPCTSTR strItem, int nImageIndex = -3) { ATLASSERT(::IsWindow(m_hWnd)); LVITEM lvItem = { 0 }; lvItem.mask = LVIF_TEXT; lvItem.iItem = nItem; lvItem.iSubItem = nSubItem; lvItem.pszText = (LPTSTR)strItem; if(nImageIndex != -3) { lvItem.mask |= LVIF_IMAGE; lvItem.iImage = nImageIndex; } if(nSubItem == 0) return InsertItem(&lvItem); return SetItem(&lvItem) ? nItem : -1; } #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) BOOL SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SORTITEMSEX, (WPARAM)lParamSort, (LPARAM)pfnCompare); } #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) #if (_WIN32_WINNT >= 0x0501) int InsertGroup(int nItem, PLVGROUP pGroup) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_INSERTGROUP, nItem, (LPARAM)pGroup); } int AddGroup(PLVGROUP pGroup) { return InsertGroup(-1, pGroup); } int RemoveGroup(int nGroupID) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_REMOVEGROUP, nGroupID, 0L); } void MoveGroup(int nGroupID, int nItem) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LVM_MOVEGROUP, nGroupID, nItem); } void MoveItemToGroup(int nItem, int nGroupID) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LVM_MOVEITEMTOGROUP, nItem, nGroupID); } int EnableGroupView(BOOL bEnable) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_ENABLEGROUPVIEW, bEnable, 0L); } int SortGroups(PFNLVGROUPCOMPARE pCompareFunc, LPVOID lpVoid = NULL) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_SORTGROUPS, (WPARAM)pCompareFunc, (LPARAM)lpVoid); } void InsertGroupSorted(PLVINSERTGROUPSORTED pInsertGroupSorted) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LVM_INSERTGROUPSORTED, (WPARAM)pInsertGroupSorted, 0L); } void RemoveAllGroups() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LVM_REMOVEALLGROUPS, 0, 0L); } BOOL HasGroup(int nGroupID) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_HASGROUP, nGroupID, 0L); } BOOL InsertMarkHitTest(LPPOINT lpPoint, LPLVINSERTMARK pInsertMark) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_INSERTMARKHITTEST, (WPARAM)lpPoint, (LPARAM)pInsertMark); } BOOL SetInfoTip(PLVSETINFOTIP pSetInfoTip) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LVM_SETINFOTIP, 0, (LPARAM)pSetInfoTip); } void CancelEditLabel() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LVM_CANCELEDITLABEL, 0, 0L); } UINT MapIndexToID(int nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, LVM_MAPINDEXTOID, nIndex, 0L); } int MapIDToIndex(UINT uID) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_MAPIDTOINDEX, uID, 0L); } #endif // (_WIN32_WINNT >= 0x0501) #if (_WIN32_WINNT >= 0x0600) int HitTestEx(LPLVHITTESTINFO lpHitTestInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_HITTEST, (WPARAM)-1, (LPARAM)lpHitTestInfo); } int HitTestEx(POINT pt, UINT* pFlags) const { ATLASSERT(::IsWindow(m_hWnd)); LVHITTESTINFO hti = { 0 }; hti.pt = pt; int nRes = (int)::SendMessage(m_hWnd, LVM_HITTEST, (WPARAM)-1, (LPARAM)&hti); if (pFlags != NULL) *pFlags = hti.flags; return nRes; } int SubItemHitTestEx(LPLVHITTESTINFO lpHitTestInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LVM_SUBITEMHITTEST, (WPARAM)-1, (LPARAM)lpHitTestInfo); } #endif // (_WIN32_WINNT >= 0x0600) // Note: selects only one item BOOL SelectItem(int nIndex) { ATLASSERT(::IsWindow(m_hWnd)); // multi-selection only: de-select all items if((GetStyle() & LVS_SINGLESEL) == 0) SetItemState(-1, 0, LVIS_SELECTED); BOOL bRet = SetItemState(nIndex, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); if(bRet) bRet = EnsureVisible(nIndex, FALSE); return bRet; } }; typedef CListViewCtrlT CListViewCtrl; /////////////////////////////////////////////////////////////////////////////// // CTreeViewCtrl template class CTreeViewCtrlT : public TBase { public: // Constructors CTreeViewCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CTreeViewCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return WC_TREEVIEW; } UINT GetCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, TVM_GETCOUNT, 0, 0L); } UINT GetIndent() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, TVM_GETINDENT, 0, 0L); } void SetIndent(UINT nIndent) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TVM_SETINDENT, nIndent, 0L); } CImageList GetImageList(int nImageListType = TVSIL_NORMAL) const { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TVM_GETIMAGELIST, (WPARAM)nImageListType, 0L)); } CImageList SetImageList(HIMAGELIST hImageList, int nImageListType = TVSIL_NORMAL) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TVM_SETIMAGELIST, (WPARAM)nImageListType, (LPARAM)hImageList)); } BOOL GetItem(LPTVITEM pItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_GETITEM, 0, (LPARAM)pItem); } BOOL SetItem(LPTVITEM pItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_SETITEM, 0, (LPARAM)pItem); } BOOL SetItem(HTREEITEM hItem, UINT nMask, LPCTSTR lpszItem, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam) { ATLASSERT(::IsWindow(m_hWnd)); TVITEM item = { 0 }; item.hItem = hItem; item.mask = nMask; item.pszText = (LPTSTR) lpszItem; item.iImage = nImage; item.iSelectedImage = nSelectedImage; item.state = nState; item.stateMask = nStateMask; item.lParam = lParam; return (BOOL)::SendMessage(m_hWnd, TVM_SETITEM, 0, (LPARAM)&item); } BOOL GetItemText(HTREEITEM hItem, LPTSTR lpstrText, int nLen) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(lpstrText != NULL); TVITEM item = { 0 }; item.hItem = hItem; item.mask = TVIF_TEXT; item.pszText = lpstrText; item.cchTextMax = nLen; return (BOOL)::SendMessage(m_hWnd, TVM_GETITEM, 0, (LPARAM)&item); } #ifndef _ATL_NO_COM BOOL GetItemText(HTREEITEM hItem, BSTR& bstrText) const { USES_CONVERSION; ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(bstrText == NULL); TVITEM item = { 0 }; item.hItem = hItem; item.mask = TVIF_TEXT; LPTSTR lpstrText = NULL; BOOL bRet = FALSE; for(int nLen = 256; ; nLen *= 2) { ATLTRY(lpstrText = new TCHAR[nLen]); if(lpstrText == NULL) break; lpstrText[0] = NULL; item.pszText = lpstrText; item.cchTextMax = nLen; bRet = (BOOL)::SendMessage(m_hWnd, TVM_GETITEM, 0, (LPARAM)&item); if(!bRet || (lstrlen(item.pszText) < nLen - 1)) break; delete [] lpstrText; lpstrText = NULL; } if(lpstrText != NULL) { if(bRet) bstrText = ::SysAllocString(T2OLE(lpstrText)); delete [] lpstrText; } return (bstrText != NULL) ? TRUE : FALSE; } #endif // !_ATL_NO_COM #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) BOOL GetItemText(HTREEITEM hItem, _CSTRING_NS::CString& strText) const { ATLASSERT(::IsWindow(m_hWnd)); TVITEM item = { 0 }; item.hItem = hItem; item.mask = TVIF_TEXT; strText.Empty(); BOOL bRet = FALSE; for(int nLen = 256; ; nLen *= 2) { item.pszText = strText.GetBufferSetLength(nLen); if(item.pszText == NULL) { bRet = FALSE; break; } item.cchTextMax = nLen; bRet = (BOOL)::SendMessage(m_hWnd, TVM_GETITEM, 0, (LPARAM)&item); if(!bRet || (lstrlen(item.pszText) < nLen - 1)) break; } strText.ReleaseBuffer(); return bRet; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) BOOL SetItemText(HTREEITEM hItem, LPCTSTR lpszItem) { ATLASSERT(::IsWindow(m_hWnd)); return SetItem(hItem, TVIF_TEXT, lpszItem, 0, 0, 0, 0, NULL); } BOOL GetItemImage(HTREEITEM hItem, int& nImage, int& nSelectedImage) const { ATLASSERT(::IsWindow(m_hWnd)); TVITEM item = { 0 }; item.hItem = hItem; item.mask = TVIF_IMAGE|TVIF_SELECTEDIMAGE; BOOL bRes = (BOOL)::SendMessage(m_hWnd, TVM_GETITEM, 0, (LPARAM)&item); if (bRes) { nImage = item.iImage; nSelectedImage = item.iSelectedImage; } return bRes; } BOOL SetItemImage(HTREEITEM hItem, int nImage, int nSelectedImage) { ATLASSERT(::IsWindow(m_hWnd)); return SetItem(hItem, TVIF_IMAGE|TVIF_SELECTEDIMAGE, NULL, nImage, nSelectedImage, 0, 0, NULL); } UINT GetItemState(HTREEITEM hItem, UINT nStateMask) const { ATLASSERT(::IsWindow(m_hWnd)); #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) return (((UINT)::SendMessage(m_hWnd, TVM_GETITEMSTATE, (WPARAM)hItem, (LPARAM)nStateMask)) & nStateMask); #else // !((_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE)) TVITEM item = { 0 }; item.hItem = hItem; item.mask = TVIF_STATE; item.state = 0; item.stateMask = nStateMask; ::SendMessage(m_hWnd, TVM_GETITEM, 0, (LPARAM)&item); return (item.state & nStateMask); #endif // !((_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE)) } BOOL SetItemState(HTREEITEM hItem, UINT nState, UINT nStateMask) { ATLASSERT(::IsWindow(m_hWnd)); return SetItem(hItem, TVIF_STATE, NULL, 0, 0, nState, nStateMask, NULL); } DWORD_PTR GetItemData(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); TVITEM item = { 0 }; item.hItem = hItem; item.mask = TVIF_PARAM; BOOL bRet = (BOOL)::SendMessage(m_hWnd, TVM_GETITEM, 0, (LPARAM)&item); return (DWORD_PTR)(bRet ? item.lParam : NULL); } BOOL SetItemData(HTREEITEM hItem, DWORD_PTR dwData) { ATLASSERT(::IsWindow(m_hWnd)); return SetItem(hItem, TVIF_PARAM, NULL, 0, 0, 0, 0, (LPARAM)dwData); } CEdit GetEditControl() const { ATLASSERT(::IsWindow(m_hWnd)); return CEdit((HWND)::SendMessage(m_hWnd, TVM_GETEDITCONTROL, 0, 0L)); } UINT GetVisibleCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, TVM_GETVISIBLECOUNT, 0, 0L); } BOOL GetItemRect(HTREEITEM hItem, LPRECT lpRect, BOOL bTextOnly) const { ATLASSERT(::IsWindow(m_hWnd)); *(HTREEITEM*)lpRect = hItem; return (BOOL)::SendMessage(m_hWnd, TVM_GETITEMRECT, (WPARAM)bTextOnly, (LPARAM)lpRect); } BOOL ItemHasChildren(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); TVITEM item = { 0 }; item.hItem = hItem; item.mask = TVIF_CHILDREN; ::SendMessage(m_hWnd, TVM_GETITEM, 0, (LPARAM)&item); return item.cChildren; } #ifndef _WIN32_WCE CToolTipCtrl GetToolTips() const { ATLASSERT(::IsWindow(m_hWnd)); return CToolTipCtrl((HWND)::SendMessage(m_hWnd, TVM_GETTOOLTIPS, 0, 0L)); } CToolTipCtrl SetToolTips(HWND hWndTT) { ATLASSERT(::IsWindow(m_hWnd)); return CToolTipCtrl((HWND)::SendMessage(m_hWnd, TVM_SETTOOLTIPS, (WPARAM)hWndTT, 0L)); } #endif // !_WIN32_WCE int GetISearchString(LPTSTR lpstr) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TVM_GETISEARCHSTRING, 0, (LPARAM)lpstr); } // checkboxes only BOOL GetCheckState(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & TVS_CHECKBOXES) != 0); UINT uRet = GetItemState(hItem, TVIS_STATEIMAGEMASK); return (uRet >> 12) - 1; } BOOL SetCheckState(HTREEITEM hItem, BOOL bCheck) { int nCheck = bCheck ? 2 : 1; // one based index return SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nCheck), TVIS_STATEIMAGEMASK); } #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) COLORREF GetBkColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, TVM_GETBKCOLOR, 0, 0L); } COLORREF SetBkColor(COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, TVM_SETBKCOLOR, 0, (LPARAM)clr); } COLORREF GetInsertMarkColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, TVM_GETINSERTMARKCOLOR, 0, 0L); } COLORREF SetInsertMarkColor(COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, TVM_SETINSERTMARKCOLOR, 0, (LPARAM)clr); } int GetItemHeight() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TVM_GETITEMHEIGHT, 0, 0L); } int SetItemHeight(int cyHeight) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TVM_SETITEMHEIGHT, cyHeight, 0L); } int GetScrollTime() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TVM_GETSCROLLTIME, 0, 0L); } int SetScrollTime(int nScrollTime) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TVM_SETSCROLLTIME, nScrollTime, 0L); } COLORREF GetTextColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, TVM_GETTEXTCOLOR, 0, 0L); } COLORREF SetTextColor(COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, TVM_SETTEXTCOLOR, 0, (LPARAM)clr); } BOOL GetUnicodeFormat() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_GETUNICODEFORMAT, 0, 0L); } BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_SETUNICODEFORMAT, bUnicode, 0L); } #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) COLORREF GetLineColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, TVM_GETLINECOLOR, 0, 0L); } COLORREF SetLineColor(COLORREF clrNew /*= CLR_DEFAULT*/) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, TVM_SETLINECOLOR, 0, (LPARAM)clrNew); } #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) BOOL GetItem(LPTVITEMEX pItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_GETITEM, 0, (LPARAM)pItem); } BOOL SetItem(LPTVITEMEX pItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_SETITEM, 0, (LPARAM)pItem); } #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) DWORD GetExtendedStyle() const { #ifndef TVM_GETEXTENDEDSTYLE const UINT TVM_GETEXTENDEDSTYLE = (TV_FIRST + 45); #endif ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, TVM_GETEXTENDEDSTYLE, 0, 0L); } DWORD SetExtendedStyle(DWORD dwStyle, DWORD dwMask) { #ifndef TVM_SETEXTENDEDSTYLE const UINT TVM_SETEXTENDEDSTYLE = (TV_FIRST + 44); #endif ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, TVM_SETEXTENDEDSTYLE, dwMask, dwStyle); } #if (_WIN32_WINNT >= 0x0600) BOOL SetAutoScrollInfo(UINT uPixPerSec, UINT uUpdateTime) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_SETAUTOSCROLLINFO, (WPARAM)uPixPerSec, (LPARAM)uUpdateTime); } DWORD GetSelectedCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, TVM_GETSELECTEDCOUNT, 0, 0L); } BOOL GetItemPartRect(HTREEITEM hItem, TVITEMPART partID, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); TVGETITEMPARTRECTINFO gipri = { hItem, lpRect, partID }; return (BOOL)::SendMessage(m_hWnd, TVM_GETITEMPARTRECT, 0, (LPARAM)&gipri); } #endif // (_WIN32_WINNT >= 0x0600) // Operations HTREEITEM InsertItem(LPTVINSERTSTRUCT lpInsertStruct) { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_INSERTITEM, 0, (LPARAM)lpInsertStruct); } HTREEITEM InsertItem(LPCTSTR lpszItem, int nImage, int nSelectedImage, HTREEITEM hParent, HTREEITEM hInsertAfter) { ATLASSERT(::IsWindow(m_hWnd)); return InsertItem(TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE, lpszItem, nImage, nSelectedImage, 0, 0, 0, hParent, hInsertAfter); } HTREEITEM InsertItem(LPCTSTR lpszItem, HTREEITEM hParent, HTREEITEM hInsertAfter) { ATLASSERT(::IsWindow(m_hWnd)); return InsertItem(TVIF_TEXT, lpszItem, 0, 0, 0, 0, 0, hParent, hInsertAfter); } HTREEITEM InsertItem(UINT nMask, LPCTSTR lpszItem, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam, HTREEITEM hParent, HTREEITEM hInsertAfter) { ATLASSERT(::IsWindow(m_hWnd)); TVINSERTSTRUCT tvis = { 0 }; tvis.hParent = hParent; tvis.hInsertAfter = hInsertAfter; tvis.item.mask = nMask; tvis.item.pszText = (LPTSTR) lpszItem; tvis.item.iImage = nImage; tvis.item.iSelectedImage = nSelectedImage; tvis.item.state = nState; tvis.item.stateMask = nStateMask; tvis.item.lParam = lParam; return (HTREEITEM)::SendMessage(m_hWnd, TVM_INSERTITEM, 0, (LPARAM)&tvis); } BOOL DeleteItem(HTREEITEM hItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_DELETEITEM, 0, (LPARAM)hItem); } BOOL DeleteAllItems() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT); } BOOL Expand(HTREEITEM hItem, UINT nCode = TVE_EXPAND) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_EXPAND, nCode, (LPARAM)hItem); } HTREEITEM GetNextItem(HTREEITEM hItem, UINT nCode) const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, nCode, (LPARAM)hItem); } HTREEITEM GetChildItem(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem); } HTREEITEM GetNextSiblingItem(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem); } HTREEITEM GetPrevSiblingItem(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_PREVIOUS, (LPARAM)hItem); } HTREEITEM GetParentItem(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hItem); } HTREEITEM GetFirstVisibleItem() const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_FIRSTVISIBLE, 0L); } HTREEITEM GetNextVisibleItem(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_NEXTVISIBLE, (LPARAM)hItem); } HTREEITEM GetPrevVisibleItem(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_PREVIOUSVISIBLE, (LPARAM)hItem); } HTREEITEM GetSelectedItem() const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_CARET, 0L); } HTREEITEM GetDropHilightItem() const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_DROPHILITE, 0L); } HTREEITEM GetRootItem() const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_ROOT, 0L); } #if !defined(_WIN32_WCE) && (_WIN32_IE >= 0x0400) HTREEITEM GetLastVisibleItem() const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_LASTVISIBLE, 0L); } #endif // !defined(_WIN32_WCE) && (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0600) HTREEITEM GetNextSelectedItem() const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, 0L); } #endif // (_WIN32_IE >= 0x0600) BOOL Select(HTREEITEM hItem, UINT nCode) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_SELECTITEM, nCode, (LPARAM)hItem); } BOOL SelectItem(HTREEITEM hItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hItem); } BOOL SelectDropTarget(HTREEITEM hItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_SELECTITEM, TVGN_DROPHILITE, (LPARAM)hItem); } BOOL SelectSetFirstVisible(HTREEITEM hItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_SELECTITEM, TVGN_FIRSTVISIBLE, (LPARAM)hItem); } CEdit EditLabel(HTREEITEM hItem) { ATLASSERT(::IsWindow(m_hWnd)); return CEdit((HWND)::SendMessage(m_hWnd, TVM_EDITLABEL, 0, (LPARAM)hItem)); } BOOL EndEditLabelNow(BOOL bCancel) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_ENDEDITLABELNOW, bCancel, 0L); } HTREEITEM HitTest(TVHITTESTINFO* pHitTestInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_HITTEST, 0, (LPARAM)pHitTestInfo); } HTREEITEM HitTest(POINT pt, UINT* pFlags) const { ATLASSERT(::IsWindow(m_hWnd)); TVHITTESTINFO hti = { 0 }; hti.pt = pt; HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_HITTEST, 0, (LPARAM)&hti); if (pFlags != NULL) *pFlags = hti.flags; return hTreeItem; } BOOL SortChildren(HTREEITEM hItem, BOOL bRecurse = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_SORTCHILDREN, (WPARAM)bRecurse, (LPARAM)hItem); } BOOL EnsureVisible(HTREEITEM hItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_ENSUREVISIBLE, 0, (LPARAM)hItem); } BOOL SortChildrenCB(LPTVSORTCB pSort, BOOL bRecurse = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_SORTCHILDRENCB, (WPARAM)bRecurse, (LPARAM)pSort); } CImageList RemoveImageList(int nImageList) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TVM_SETIMAGELIST, (WPARAM)nImageList, NULL)); } CImageList CreateDragImage(HTREEITEM hItem) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TVM_CREATEDRAGIMAGE, 0, (LPARAM)hItem)); } #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) BOOL SetInsertMark(HTREEITEM hTreeItem, BOOL bAfter) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_SETINSERTMARK, bAfter, (LPARAM)hTreeItem); } BOOL RemoveInsertMark() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TVM_SETINSERTMARK, 0, 0L); } #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) #if (_WIN32_WINNT >= 0x0501) HTREEITEM MapAccIDToHTREEITEM(UINT uID) const { ATLASSERT(::IsWindow(m_hWnd)); return (HTREEITEM)::SendMessage(m_hWnd, TVM_MAPACCIDTOHTREEITEM, uID, 0L); } UINT MapHTREEITEMToAccID(HTREEITEM hTreeItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, TVM_MAPHTREEITEMTOACCID, (WPARAM)hTreeItem, 0L); } #endif // (_WIN32_WINNT >= 0x0501) #if (_WIN32_WINNT >= 0x0600) void ShowInfoTip(HTREEITEM hItem) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TVM_SHOWINFOTIP, 0, (LPARAM)hItem); } #endif // (_WIN32_WINNT >= 0x0600) }; typedef CTreeViewCtrlT CTreeViewCtrl; /////////////////////////////////////////////////////////////////////////////// // CTreeViewCtrlEx // forward declaration template class CTreeViewCtrlExT; // Note: TBase here is for CTreeViewCtrlExT, and not for CTreeItemT itself template class CTreeItemT { public: HTREEITEM m_hTreeItem; CTreeViewCtrlExT* m_pTreeView; // Construction CTreeItemT(HTREEITEM hTreeItem = NULL, CTreeViewCtrlExT* pTreeView = NULL) : m_hTreeItem(hTreeItem), m_pTreeView(pTreeView) { } CTreeItemT(const CTreeItemT& posSrc) { *this = posSrc; } operator HTREEITEM() { return m_hTreeItem; } CTreeItemT& operator =(const CTreeItemT& itemSrc) { m_hTreeItem = itemSrc.m_hTreeItem; m_pTreeView = itemSrc.m_pTreeView; return *this; } // Attributes CTreeViewCtrlExT* GetTreeView() const { return m_pTreeView; } BOOL operator !() const { return m_hTreeItem == NULL; } BOOL IsNull() const { return m_hTreeItem == NULL; } BOOL GetRect(LPRECT lpRect, BOOL bTextOnly) const; BOOL GetText(LPTSTR lpstrText, int nLen) const; #ifndef _ATL_NO_COM BOOL GetText(BSTR& bstrText) const; #endif // !_ATL_NO_COM #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) BOOL GetText(_CSTRING_NS::CString& strText) const; #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) BOOL SetText(LPCTSTR lpszItem); BOOL GetImage(int& nImage, int& nSelectedImage) const; BOOL SetImage(int nImage, int nSelectedImage); UINT GetState(UINT nStateMask) const; BOOL SetState(UINT nState, UINT nStateMask); DWORD_PTR GetData() const; BOOL SetData(DWORD_PTR dwData); BOOL SetItem(UINT nMask, LPCTSTR lpszItem, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam); // Operations CTreeItemT InsertAfter(LPCTSTR lpstrItem, HTREEITEM hItemAfter, int nImageIndex) { return _Insert(lpstrItem, nImageIndex, hItemAfter); } CTreeItemT AddHead(LPCTSTR lpstrItem, int nImageIndex) { return _Insert(lpstrItem, nImageIndex, TVI_FIRST); } CTreeItemT AddTail(LPCTSTR lpstrItem, int nImageIndex) { return _Insert(lpstrItem, nImageIndex, TVI_LAST); } CTreeItemT GetChild() const; CTreeItemT GetNext(UINT nCode) const; CTreeItemT GetNextSibling() const; CTreeItemT GetPrevSibling() const; CTreeItemT GetParent() const; CTreeItemT GetFirstVisible() const; CTreeItemT GetNextVisible() const; CTreeItemT GetPrevVisible() const; CTreeItemT GetSelected() const; CTreeItemT GetDropHilight() const; CTreeItemT GetRoot() const; #if !defined(_WIN32_WCE) && (_WIN32_IE >= 0x0400) CTreeItemT GetLastVisible() const; #endif // !defined(_WIN32_WCE) && (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0600) CTreeItemT GetNextSelected() const; #endif // (_WIN32_IE >= 0x0600) BOOL HasChildren() const; BOOL Delete(); BOOL Expand(UINT nCode = TVE_EXPAND); BOOL Select(UINT nCode); BOOL Select(); BOOL SelectDropTarget(); BOOL SelectSetFirstVisible(); HWND EditLabel(); HIMAGELIST CreateDragImage(); BOOL SortChildren(BOOL bRecurse = FALSE); BOOL EnsureVisible(); CTreeItemT _Insert(LPCTSTR lpstrItem, int nImageIndex, HTREEITEM hItemAfter); int GetImageIndex() const; #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) BOOL SetInsertMark(BOOL bAfter); #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) #if (_WIN32_WINNT >= 0x0501) UINT MapHTREEITEMToAccID() const; #endif // (_WIN32_WINNT >= 0x0501) #if (_WIN32_WINNT >= 0x0600) void ShowInfoTip(); BOOL GetPartRect(TVITEMPART partID, LPRECT lpRect) const; #endif // (_WIN32_WINNT >= 0x0600) }; typedef CTreeItemT CTreeItem; template class CTreeViewCtrlExT : public CTreeViewCtrlT< TBase > { public: // Constructors CTreeViewCtrlExT(HWND hWnd = NULL) : CTreeViewCtrlT< TBase >(hWnd) { } CTreeViewCtrlExT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } // Operations (overides that return CTreeItem) CTreeItemT InsertItem(LPTVINSERTSTRUCT lpInsertStruct) { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_INSERTITEM, 0, (LPARAM)lpInsertStruct); return CTreeItemT(hTreeItem, this); } CTreeItemT InsertItem(LPCTSTR lpszItem, int nImage, int nSelectedImage, HTREEITEM hParent, HTREEITEM hInsertAfter) { ATLASSERT(::IsWindow(m_hWnd)); return InsertItem(TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE, lpszItem, nImage, nSelectedImage, 0, 0, 0, hParent, hInsertAfter); } CTreeItemT InsertItem(LPCTSTR lpszItem, HTREEITEM hParent, HTREEITEM hInsertAfter) { ATLASSERT(::IsWindow(m_hWnd)); return InsertItem(TVIF_TEXT, lpszItem, 0, 0, 0, 0, 0, hParent, hInsertAfter); } CTreeItemT GetNextItem(HTREEITEM hItem, UINT nCode) const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, nCode, (LPARAM)hItem); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } CTreeItemT GetChildItem(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } CTreeItemT GetNextSiblingItem(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } CTreeItemT GetPrevSiblingItem(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_PREVIOUS, (LPARAM)hItem); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } CTreeItemT GetParentItem(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hItem); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } CTreeItemT GetFirstVisibleItem() const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_FIRSTVISIBLE, 0L); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } CTreeItemT GetNextVisibleItem(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_NEXTVISIBLE, (LPARAM)hItem); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } CTreeItemT GetPrevVisibleItem(HTREEITEM hItem) const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_PREVIOUSVISIBLE, (LPARAM)hItem); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } CTreeItemT GetSelectedItem() const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_CARET, 0L); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } CTreeItemT GetDropHilightItem() const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_DROPHILITE, 0L); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } CTreeItemT GetRootItem() const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_ROOT, 0L); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } #if !defined(_WIN32_WCE) && (_WIN32_IE >= 0x0400) CTreeItemT GetLastVisibleItem() const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_LASTVISIBLE, 0L); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } #endif // !defined(_WIN32_WCE) && (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0600) CTreeItemT GetNextSelectedItem() const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, 0L); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } #endif // (_WIN32_IE >= 0x0600) CTreeItemT HitTest(TVHITTESTINFO* pHitTestInfo) const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_HITTEST, 0, (LPARAM)pHitTestInfo); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } CTreeItemT InsertItem(UINT nMask, LPCTSTR lpszItem, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam, HTREEITEM hParent, HTREEITEM hInsertAfter) { ATLASSERT(::IsWindow(m_hWnd)); TVINSERTSTRUCT tvis = { 0 }; tvis.hParent = hParent; tvis.hInsertAfter = hInsertAfter; tvis.item.mask = nMask; tvis.item.pszText = (LPTSTR) lpszItem; tvis.item.iImage = nImage; tvis.item.iSelectedImage = nSelectedImage; tvis.item.state = nState; tvis.item.stateMask = nStateMask; tvis.item.lParam = lParam; HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_INSERTITEM, 0, (LPARAM)&tvis); return CTreeItemT(hTreeItem, this); } CTreeItemT HitTest(POINT pt, UINT* pFlags) const { ATLASSERT(::IsWindow(m_hWnd)); TVHITTESTINFO hti = { 0 }; hti.pt = pt; HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_HITTEST, 0, (LPARAM)&hti); if (pFlags != NULL) *pFlags = hti.flags; return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } #if (_WIN32_WINNT >= 0x0501) CTreeItemT MapAccIDToHTREEITEM(UINT uID) const { ATLASSERT(::IsWindow(m_hWnd)); HTREEITEM hTreeItem = (HTREEITEM)::SendMessage(m_hWnd, TVM_MAPACCIDTOHTREEITEM, uID, 0L); return CTreeItemT(hTreeItem, (CTreeViewCtrlExT*)this); } #endif // (_WIN32_WINNT >= 0x0501) }; typedef CTreeViewCtrlExT CTreeViewCtrlEx; // CTreeItem inline methods template inline BOOL CTreeItemT::GetRect(LPRECT lpRect, BOOL bTextOnly) const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetItemRect(m_hTreeItem,lpRect,bTextOnly); } template inline CTreeItemT CTreeItemT::GetNext(UINT nCode) const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetNextItem(m_hTreeItem,nCode); } template inline CTreeItemT CTreeItemT::GetChild() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetChildItem(m_hTreeItem); } template inline CTreeItemT CTreeItemT::GetNextSibling() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetNextSiblingItem(m_hTreeItem); } template inline CTreeItemT CTreeItemT::GetPrevSibling() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetPrevSiblingItem(m_hTreeItem); } template inline CTreeItemT CTreeItemT::GetParent() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetParentItem(m_hTreeItem); } template inline CTreeItemT CTreeItemT::GetFirstVisible() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetFirstVisibleItem(); } template inline CTreeItemT CTreeItemT::GetNextVisible() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetNextVisibleItem(m_hTreeItem); } template inline CTreeItemT CTreeItemT::GetPrevVisible() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetPrevVisibleItem(m_hTreeItem); } template inline CTreeItemT CTreeItemT::GetSelected() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetSelectedItem(); } template inline CTreeItemT CTreeItemT::GetDropHilight() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetDropHilightItem(); } template inline CTreeItemT CTreeItemT::GetRoot() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetRootItem(); } #if !defined(_WIN32_WCE) && (_WIN32_IE >= 0x0400) template inline CTreeItemT CTreeItemT::GetLastVisible() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetLastVisibleItem(); } #endif // !defined(_WIN32_WCE) && (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0600) template inline CTreeItemT CTreeItemT::GetNextSelected() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetNextSelectedItem(); } #endif // (_WIN32_IE >= 0x0600) template inline BOOL CTreeItemT::GetText(LPTSTR lpstrText, int nLen) const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetItemText(m_hTreeItem, lpstrText, nLen); } #ifndef _ATL_NO_COM #ifdef _OLEAUTO_H_ template inline BOOL CTreeItemT::GetText(BSTR& bstrText) const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetItemText(m_hTreeItem, bstrText); } #endif // _OLEAUTO_H_ #endif // !_ATL_NO_COM #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) template inline BOOL CTreeItemT::GetText(_CSTRING_NS::CString& strText) const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetItemText(m_hTreeItem, strText); } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) template inline BOOL CTreeItemT::GetImage(int& nImage, int& nSelectedImage) const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetItemImage(m_hTreeItem,nImage,nSelectedImage); } template inline UINT CTreeItemT::GetState(UINT nStateMask) const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetItemState(m_hTreeItem,nStateMask); } template inline DWORD_PTR CTreeItemT::GetData() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetItemData(m_hTreeItem); } template inline BOOL CTreeItemT::SetItem(UINT nMask, LPCTSTR lpszItem, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam) { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->SetItem(m_hTreeItem, nMask, lpszItem, nImage, nSelectedImage, nState, nStateMask, lParam); } template inline BOOL CTreeItemT::SetText(LPCTSTR lpszItem) { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->SetItemText(m_hTreeItem,lpszItem); } template inline BOOL CTreeItemT::SetImage(int nImage, int nSelectedImage) { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->SetItemImage(m_hTreeItem,nImage,nSelectedImage); } template inline BOOL CTreeItemT::SetState(UINT nState, UINT nStateMask) { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->SetItemState(m_hTreeItem,nState,nStateMask); } template inline BOOL CTreeItemT::SetData(DWORD_PTR dwData) { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->SetItemData(m_hTreeItem,dwData); } template inline BOOL CTreeItemT::HasChildren() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->ItemHasChildren(m_hTreeItem); } template inline BOOL CTreeItemT::Delete() { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->DeleteItem(m_hTreeItem); } template inline BOOL CTreeItemT::Expand(UINT nCode /*= TVE_EXPAND*/) { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->Expand(m_hTreeItem,nCode); } template inline BOOL CTreeItemT::Select(UINT nCode) { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->Select(m_hTreeItem,nCode); } template inline BOOL CTreeItemT::Select() { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->SelectItem(m_hTreeItem); } template inline BOOL CTreeItemT::SelectDropTarget() { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->SelectDropTarget(m_hTreeItem); } template inline BOOL CTreeItemT::SelectSetFirstVisible() { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->SelectSetFirstVisible(m_hTreeItem); } template inline HWND CTreeItemT::EditLabel() { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->EditLabel(m_hTreeItem); } template inline HIMAGELIST CTreeItemT::CreateDragImage() { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->CreateDragImage(m_hTreeItem); } template inline BOOL CTreeItemT::SortChildren(BOOL bRecurse /*= FALSE*/) { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->SortChildren(m_hTreeItem, bRecurse); } template inline BOOL CTreeItemT::EnsureVisible() { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->EnsureVisible(m_hTreeItem); } template inline CTreeItemT CTreeItemT::_Insert(LPCTSTR lpstrItem, int nImageIndex, HTREEITEM hItemAfter) { ATLASSERT(m_pTreeView != NULL); TVINSERTSTRUCT ins = { 0 }; ins.hParent = m_hTreeItem; ins.hInsertAfter = hItemAfter; ins.item.mask = TVIF_TEXT; ins.item.pszText = (LPTSTR)lpstrItem; if(nImageIndex != -1) { ins.item.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE; ins.item.iImage = nImageIndex; ins.item.iSelectedImage = nImageIndex; } return CTreeItemT(m_pTreeView->InsertItem(&ins), m_pTreeView); } template inline int CTreeItemT::GetImageIndex() const { ATLASSERT(m_pTreeView != NULL); TVITEM item = { 0 }; item.mask = TVIF_HANDLE | TVIF_IMAGE; item.hItem = m_hTreeItem; m_pTreeView->GetItem(&item); return item.iImage; } #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) template inline BOOL CTreeItemT::SetInsertMark(BOOL bAfter) { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->SetInsertMark(m_hTreeItem, bAfter); } #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) #if (_WIN32_WINNT >= 0x0501) template inline UINT CTreeItemT::MapHTREEITEMToAccID() const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->MapHTREEITEMToAccID(m_hTreeItem); } #endif // (_WIN32_WINNT >= 0x0501) #if (_WIN32_WINNT >= 0x0600) template inline void CTreeItemT::ShowInfoTip() { ATLASSERT(m_pTreeView != NULL); m_pTreeView->ShowInfoTip(m_hTreeItem); } template inline BOOL CTreeItemT::GetPartRect(TVITEMPART partID, LPRECT lpRect) const { ATLASSERT(m_pTreeView != NULL); return m_pTreeView->GetItemPartRect(m_hTreeItem, partID, lpRect); } #endif // (_WIN32_WINNT >= 0x0600) /////////////////////////////////////////////////////////////////////////////// // CToolBarCtrl template class CToolBarCtrlT : public TBase { public: // Construction CToolBarCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CToolBarCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return TOOLBARCLASSNAME; } BOOL IsButtonEnabled(int nID) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_ISBUTTONENABLED, nID, 0L); } BOOL IsButtonChecked(int nID) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_ISBUTTONCHECKED, nID, 0L); } BOOL IsButtonPressed(int nID) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_ISBUTTONPRESSED, nID, 0L); } BOOL IsButtonHidden(int nID) const { ATLASSERT(::IsWindow(m_hWnd)); return(BOOL) ::SendMessage(m_hWnd, TB_ISBUTTONHIDDEN, nID, 0L); } BOOL IsButtonIndeterminate(int nID) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_ISBUTTONINDETERMINATE, nID, 0L); } int GetState(int nID) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_GETSTATE, nID, 0L); } BOOL SetState(int nID, UINT nState) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_SETSTATE, nID, MAKELPARAM(nState, 0)); } BOOL GetButton(int nIndex, LPTBBUTTON lpButton) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_GETBUTTON, nIndex, (LPARAM)lpButton); } int GetButtonCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_BUTTONCOUNT, 0, 0L); } BOOL GetItemRect(int nIndex, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_GETITEMRECT, nIndex, (LPARAM)lpRect); } void SetButtonStructSize(int nSize = sizeof(TBBUTTON)) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TB_BUTTONSTRUCTSIZE, nSize, 0L); } BOOL SetButtonSize(SIZE size) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_SETBUTTONSIZE, 0, MAKELPARAM(size.cx, size.cy)); } BOOL SetButtonSize(int cx, int cy) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_SETBUTTONSIZE, 0, MAKELPARAM(cx, cy)); } BOOL SetBitmapSize(SIZE size) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_SETBITMAPSIZE, 0, MAKELPARAM(size.cx, size.cy)); } BOOL SetBitmapSize(int cx, int cy) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_SETBITMAPSIZE, 0, MAKELPARAM(cx, cy)); } #ifndef _WIN32_WCE CToolTipCtrl GetToolTips() const { ATLASSERT(::IsWindow(m_hWnd)); return CToolTipCtrl((HWND)::SendMessage(m_hWnd, TB_GETTOOLTIPS, 0, 0L)); } void SetToolTips(HWND hWndToolTip) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TB_SETTOOLTIPS, (WPARAM)hWndToolTip, 0L); } #endif // !_WIN32_WCE void SetNotifyWnd(HWND hWnd) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TB_SETPARENT, (WPARAM)hWnd, 0L); } int GetRows() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_GETROWS, 0, 0L); } void SetRows(int nRows, BOOL bLarger, LPRECT lpRect) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TB_SETROWS, MAKELPARAM(nRows, bLarger), (LPARAM)lpRect); } BOOL SetCmdID(int nIndex, UINT nID) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_SETCMDID, nIndex, nID); } DWORD GetBitmapFlags() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, TB_GETBITMAPFLAGS, 0, 0L); } int GetBitmap(int nID) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_GETBITMAP, nID, 0L); } int GetButtonText(int nID, LPTSTR lpstrText) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_GETBUTTONTEXT, nID, (LPARAM)lpstrText); } // nIndex - IE5 or higher only CImageList GetImageList(int nIndex = 0) const { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TB_GETIMAGELIST, nIndex, 0L)); } // nIndex - IE5 or higher only CImageList SetImageList(HIMAGELIST hImageList, int nIndex = 0) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TB_SETIMAGELIST, nIndex, (LPARAM)hImageList)); } // nIndex - IE5 or higher only CImageList GetDisabledImageList(int nIndex = 0) const { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TB_GETDISABLEDIMAGELIST, nIndex, 0L)); } // nIndex - IE5 or higher only CImageList SetDisabledImageList(HIMAGELIST hImageList, int nIndex = 0) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TB_SETDISABLEDIMAGELIST, nIndex, (LPARAM)hImageList)); } #ifndef _WIN32_WCE // nIndex - IE5 or higher only CImageList GetHotImageList(int nIndex = 0) const { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TB_GETHOTIMAGELIST, nIndex, 0L)); } // nIndex - IE5 or higher only CImageList SetHotImageList(HIMAGELIST hImageList, int nIndex = 0) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TB_SETHOTIMAGELIST, nIndex, (LPARAM)hImageList)); } #endif // !_WIN32_WCE DWORD GetStyle() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, TB_GETSTYLE, 0, 0L); } void SetStyle(DWORD dwStyle) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TB_SETSTYLE, 0, dwStyle); } DWORD GetButtonSize() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, TB_GETBUTTONSIZE, 0, 0L); } void GetButtonSize(SIZE& size) const { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, TB_GETBUTTONSIZE, 0, 0L); size.cx = LOWORD(dwRet); size.cy = HIWORD(dwRet); } BOOL GetRect(int nID, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_GETRECT, nID, (LPARAM)lpRect); } int GetTextRows() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_GETTEXTROWS, 0, 0L); } BOOL SetButtonWidth(int cxMin, int cxMax) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_SETBUTTONWIDTH, 0, MAKELPARAM(cxMin, cxMax)); } BOOL SetIndent(int nIndent) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_SETINDENT, nIndent, 0L); } BOOL SetMaxTextRows(int nMaxTextRows) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_SETMAXTEXTROWS, nMaxTextRows, 0L); } #if (_WIN32_IE >= 0x0400) #ifndef _WIN32_WCE BOOL GetAnchorHighlight() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_GETANCHORHIGHLIGHT, 0, 0L); } BOOL SetAnchorHighlight(BOOL bEnable = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_SETANCHORHIGHLIGHT, bEnable, 0L); } #endif // !_WIN32_WCE int GetButtonInfo(int nID, LPTBBUTTONINFO lptbbi) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_GETBUTTONINFO, nID, (LPARAM)lptbbi); } BOOL SetButtonInfo(int nID, LPTBBUTTONINFO lptbbi) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_SETBUTTONINFO, nID, (LPARAM)lptbbi); } BOOL SetButtonInfo(int nID, DWORD dwMask, BYTE Style, BYTE State, LPCTSTR lpszItem, int iImage, WORD cx, int iCommand, DWORD_PTR lParam) { ATLASSERT(::IsWindow(m_hWnd)); TBBUTTONINFO tbbi = { 0 }; tbbi.cbSize = sizeof(TBBUTTONINFO); tbbi.dwMask = dwMask; tbbi.idCommand = iCommand; tbbi.iImage = iImage; tbbi.fsState = State; tbbi.fsStyle = Style; tbbi.cx = cx; tbbi.pszText = (LPTSTR) lpszItem; tbbi.lParam = lParam; return (BOOL)::SendMessage(m_hWnd, TB_SETBUTTONINFO, nID, (LPARAM)&tbbi); } #ifndef _WIN32_WCE int GetHotItem() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_GETHOTITEM, 0, 0L); } int SetHotItem(int nItem) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_SETHOTITEM, nItem, 0L); } #endif // !_WIN32_WCE BOOL IsButtonHighlighted(int nButtonID) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_ISBUTTONHIGHLIGHTED, nButtonID, 0L); } DWORD SetDrawTextFlags(DWORD dwMask, DWORD dwFlags) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, TB_SETDRAWTEXTFLAGS, dwMask, dwFlags); } #ifndef _WIN32_WCE BOOL GetColorScheme(LPCOLORSCHEME lpcs) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_GETCOLORSCHEME, 0, (LPARAM)lpcs); } void SetColorScheme(LPCOLORSCHEME lpcs) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TB_SETCOLORSCHEME, 0, (LPARAM)lpcs); } DWORD GetExtendedStyle() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, TB_GETEXTENDEDSTYLE, 0, 0L); } DWORD SetExtendedStyle(DWORD dwStyle) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, TB_SETEXTENDEDSTYLE, 0, dwStyle); } void GetInsertMark(LPTBINSERTMARK lptbim) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TB_GETINSERTMARK, 0, (LPARAM)lptbim); } void SetInsertMark(LPTBINSERTMARK lptbim) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TB_SETINSERTMARK, 0, (LPARAM)lptbim); } COLORREF GetInsertMarkColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, TB_GETINSERTMARKCOLOR, 0, 0L); } COLORREF SetInsertMarkColor(COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, TB_SETINSERTMARKCOLOR, 0, (LPARAM)clr); } BOOL GetMaxSize(LPSIZE lpSize) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_GETMAXSIZE, 0, (LPARAM)lpSize); } void GetPadding(LPSIZE lpSizePadding) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(lpSizePadding != NULL); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, TB_GETPADDING, 0, 0L); lpSizePadding->cx = GET_X_LPARAM(dwRet); lpSizePadding->cy = GET_Y_LPARAM(dwRet); } void SetPadding(int cx, int cy, LPSIZE lpSizePadding = NULL) { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, TB_SETPADDING, 0, MAKELPARAM(cx, cy)); if(lpSizePadding != NULL) { lpSizePadding->cx = GET_X_LPARAM(dwRet); lpSizePadding->cy = GET_Y_LPARAM(dwRet); } } BOOL GetUnicodeFormat() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_GETUNICODEFORMAT, 0, 0L); } BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_SETUNICODEFORMAT, bUnicode, 0L); } #endif // !_WIN32_WCE #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) int GetString(int nString, LPTSTR lpstrString, int cchMaxLen) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_GETSTRING, MAKEWPARAM(cchMaxLen, nString), (LPARAM)lpstrString); } int GetStringBSTR(int nString, BSTR& bstrString) const { USES_CONVERSION; ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(bstrString == NULL); int nLength = (int)(short)LOWORD(::SendMessage(m_hWnd, TB_GETSTRING, MAKEWPARAM(0, nString), NULL)); if(nLength != -1) { CTempBuffer buff; LPTSTR lpstrText = buff.Allocate(nLength + 1); if(lpstrText != NULL) { nLength = (int)::SendMessage(m_hWnd, TB_GETSTRING, MAKEWPARAM(nLength + 1, nString), (LPARAM)lpstrText); if(nLength != -1) bstrString = ::SysAllocString(T2OLE(lpstrText)); } else { nLength = -1; } } return nLength; } #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) int GetString(int nString, _CSTRING_NS::CString& str) const { ATLASSERT(::IsWindow(m_hWnd)); int nLength = (int)(short)LOWORD(::SendMessage(m_hWnd, TB_GETSTRING, MAKEWPARAM(0, nString), NULL)); if(nLength != -1) { LPTSTR lpstr = str.GetBufferSetLength(nLength + 1); if(lpstr != NULL) nLength = (int)::SendMessage(m_hWnd, TB_GETSTRING, MAKEWPARAM(nLength + 1, nString), (LPARAM)lpstr); else nLength = -1; str.ReleaseBuffer(); } return nLength; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) #if (_WIN32_WINNT >= 0x0501) void GetMetrics(LPTBMETRICS lptbm) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TB_GETMETRICS, 0, (LPARAM)lptbm); } void SetMetrics(LPTBMETRICS lptbm) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TB_SETMETRICS, 0, (LPARAM)lptbm); } void SetWindowTheme(LPCWSTR lpstrTheme) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TB_SETWINDOWTHEME, 0, (LPARAM)lpstrTheme); } #endif // (_WIN32_WINNT >= 0x0501) #if (_WIN32_WINNT >= 0x0600) CImageList GetPressedImageList(int nIndex = 0) const { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TB_GETPRESSEDIMAGELIST, nIndex, 0L)); } CImageList SetPressedImageList(HIMAGELIST hImageList, int nIndex = 0) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TB_SETPRESSEDIMAGELIST, nIndex, (LPARAM)hImageList)); } void GetItemDropDownRect(int nIndex, LPRECT lpRect) const { #ifndef TB_GETITEMDROPDOWNRECT const int TB_GETITEMDROPDOWNRECT = WM_USER + 103; #endif ATLASSERT(::IsWindow(m_hWnd)); BOOL bRet = (BOOL)::SendMessage(m_hWnd, TB_GETITEMDROPDOWNRECT, nIndex, (LPARAM)lpRect); bRet; // avoid level 4 warning ATLASSERT(bRet != FALSE); } #endif // (_WIN32_WINNT >= 0x0600) // Operations BOOL EnableButton(int nID, BOOL bEnable = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_ENABLEBUTTON, nID, MAKELPARAM(bEnable, 0)); } BOOL CheckButton(int nID, BOOL bCheck = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_CHECKBUTTON, nID, MAKELPARAM(bCheck, 0)); } BOOL PressButton(int nID, BOOL bPress = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_PRESSBUTTON, nID, MAKELPARAM(bPress, 0)); } BOOL HideButton(int nID, BOOL bHide = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_HIDEBUTTON, nID, MAKELPARAM(bHide, 0)); } BOOL Indeterminate(int nID, BOOL bIndeterminate = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_INDETERMINATE, nID, MAKELPARAM(bIndeterminate, 0)); } int AddBitmap(int nNumButtons, UINT nBitmapID) { ATLASSERT(::IsWindow(m_hWnd)); TBADDBITMAP tbab = { 0 }; tbab.hInst = ModuleHelper::GetResourceInstance(); ATLASSERT(tbab.hInst != NULL); tbab.nID = nBitmapID; return (int)::SendMessage(m_hWnd, TB_ADDBITMAP, (WPARAM)nNumButtons, (LPARAM)&tbab); } int AddBitmap(int nNumButtons, HBITMAP hBitmap) { ATLASSERT(::IsWindow(m_hWnd)); TBADDBITMAP tbab = { 0 }; tbab.hInst = NULL; tbab.nID = (UINT_PTR)hBitmap; return (int)::SendMessage(m_hWnd, TB_ADDBITMAP, (WPARAM)nNumButtons, (LPARAM)&tbab); } BOOL AddButtons(int nNumButtons, LPTBBUTTON lpButtons) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_ADDBUTTONS, nNumButtons, (LPARAM)lpButtons); } BOOL InsertButton(int nIndex, LPTBBUTTON lpButton) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_INSERTBUTTON, nIndex, (LPARAM)lpButton); } BOOL InsertButton(int nIndex, int iCommand, BYTE Style, BYTE State, int iBitmap, INT_PTR iString, DWORD_PTR lParam) { ATLASSERT(::IsWindow(m_hWnd)); TBBUTTON tbb = { 0 }; tbb.fsStyle = Style; tbb.fsState = State; tbb.idCommand = iCommand; tbb.iBitmap = iBitmap; tbb.iString = iString; tbb.dwData = lParam; return (BOOL)::SendMessage(m_hWnd, TB_INSERTBUTTON, nIndex, (LPARAM)&tbb); } BOOL InsertButton(int nIndex, int iCommand, BYTE Style, BYTE State, int iBitmap, LPCTSTR lpszItem, DWORD_PTR lParam) { return InsertButton(nIndex, iCommand, Style, State, iBitmap, (INT_PTR)lpszItem, lParam); } BOOL AddButton(LPTBBUTTON lpButton) { return InsertButton(-1, lpButton); } BOOL AddButton(int iCommand, BYTE Style, BYTE State, int iBitmap, INT_PTR iString, DWORD_PTR lParam) { return InsertButton(-1, iCommand, Style, State, iBitmap, iString, lParam); } BOOL AddButton(int iCommand, BYTE Style, BYTE State, int iBitmap, LPCTSTR lpszItem, DWORD_PTR lParam) { return InsertButton(-1, iCommand, Style, State, iBitmap, lpszItem, lParam); } BOOL DeleteButton(int nIndex) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_DELETEBUTTON, nIndex, 0L); } BOOL InsertSeparator(int nIndex, int cxWidth = 8) { return InsertButton(nIndex, 0, BTNS_SEP, 0, cxWidth, (INT_PTR)0, 0); } BOOL AddSeparator(int cxWidth = 8) { return AddButton(0, BTNS_SEP, 0, cxWidth, (INT_PTR)0, 0); } int CommandToIndex(UINT nID) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_COMMANDTOINDEX, nID, 0L); } #ifndef _WIN32_WCE void SaveState(HKEY hKeyRoot, LPCTSTR lpszSubKey, LPCTSTR lpszValueName) { ATLASSERT(::IsWindow(m_hWnd)); TBSAVEPARAMS tbs = { 0 }; tbs.hkr = hKeyRoot; tbs.pszSubKey = lpszSubKey; tbs.pszValueName = lpszValueName; ::SendMessage(m_hWnd, TB_SAVERESTORE, (WPARAM)TRUE, (LPARAM)&tbs); } void RestoreState(HKEY hKeyRoot, LPCTSTR lpszSubKey, LPCTSTR lpszValueName) { ATLASSERT(::IsWindow(m_hWnd)); TBSAVEPARAMS tbs = { 0 }; tbs.hkr = hKeyRoot; tbs.pszSubKey = lpszSubKey; tbs.pszValueName = lpszValueName; ::SendMessage(m_hWnd, TB_SAVERESTORE, (WPARAM)FALSE, (LPARAM)&tbs); } void Customize() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TB_CUSTOMIZE, 0, 0L); } #endif // !_WIN32_WCE int AddString(UINT nStringID) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_ADDSTRING, (WPARAM)ModuleHelper::GetResourceInstance(), (LPARAM)nStringID); } int AddStrings(LPCTSTR lpszStrings) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_ADDSTRING, 0, (LPARAM)lpszStrings); } void AutoSize() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TB_AUTOSIZE, 0, 0L); } BOOL ChangeBitmap(int nID, int nBitmap) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_CHANGEBITMAP, nID, MAKELPARAM(nBitmap, 0)); } int LoadImages(int nBitmapID) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_LOADIMAGES, nBitmapID, (LPARAM)ModuleHelper::GetResourceInstance()); } int LoadStdImages(int nBitmapID) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_LOADIMAGES, nBitmapID, (LPARAM)HINST_COMMCTRL); } BOOL ReplaceBitmap(LPTBREPLACEBITMAP ptbrb) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_REPLACEBITMAP, 0, (LPARAM)ptbrb); } #if (_WIN32_IE >= 0x0400) int HitTest(LPPOINT lpPoint) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TB_HITTEST, 0, (LPARAM)lpPoint); } #ifndef _WIN32_WCE BOOL InsertMarkHitTest(LPPOINT lpPoint, LPTBINSERTMARK lptbim) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_INSERTMARKHITTEST, (WPARAM)lpPoint, (LPARAM)lptbim); } BOOL InsertMarkHitTest(int x, int y, LPTBINSERTMARK lptbim) const { ATLASSERT(::IsWindow(m_hWnd)); POINT pt = { x, y }; return (BOOL)::SendMessage(m_hWnd, TB_INSERTMARKHITTEST, (WPARAM)&pt, (LPARAM)lptbim); } BOOL MapAccelerator(TCHAR chAccel, int& nID) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_MAPACCELERATOR, (WPARAM)chAccel, (LPARAM)&nID); } BOOL MarkButton(int nID, BOOL bHighlight = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_MARKBUTTON, nID, MAKELPARAM(bHighlight, 0)); } BOOL MoveButton(int nOldPos, int nNewPos) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TB_MOVEBUTTON, nOldPos, nNewPos); } HRESULT GetObject(REFIID iid, LPVOID* ppvObject) { ATLASSERT(::IsWindow(m_hWnd)); return (HRESULT)::SendMessage(m_hWnd, TB_GETOBJECT, (WPARAM)&iid, (LPARAM)ppvObject); } #endif // !_WIN32_WCE #endif // (_WIN32_IE >= 0x0400) }; typedef CToolBarCtrlT CToolBarCtrl; /////////////////////////////////////////////////////////////////////////////// // CStatusBarCtrl template class CStatusBarCtrlT : public TBase { public: // Constructors CStatusBarCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CStatusBarCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Methods static LPCTSTR GetWndClassName() { return STATUSCLASSNAME; } int GetParts(int nParts, int* pParts) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, SB_GETPARTS, nParts, (LPARAM)pParts); } BOOL SetParts(int nParts, int* pWidths) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, SB_SETPARTS, nParts, (LPARAM)pWidths); } int GetTextLength(int nPane, int* pType = NULL) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nPane < 256); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, SB_GETTEXTLENGTH, (WPARAM)nPane, 0L); if (pType != NULL) *pType = (int)(short)HIWORD(dwRet); return (int)(short)LOWORD(dwRet); } int GetText(int nPane, LPTSTR lpszText, int* pType = NULL) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nPane < 256); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, SB_GETTEXT, (WPARAM)nPane, (LPARAM)lpszText); if(pType != NULL) *pType = (int)(short)HIWORD(dwRet); return (int)(short)LOWORD(dwRet); } #ifndef _ATL_NO_COM BOOL GetTextBSTR(int nPane, BSTR& bstrText, int* pType = NULL) const { USES_CONVERSION; ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nPane < 256); ATLASSERT(bstrText == NULL); int nLength = (int)(short)LOWORD(::SendMessage(m_hWnd, SB_GETTEXTLENGTH, (WPARAM)nPane, 0L)); if(nLength == 0) return FALSE; CTempBuffer buff; LPTSTR lpstrText = buff.Allocate(nLength + 1); if(lpstrText == NULL) return FALSE; if(!GetText(nPane, lpstrText, pType)) return FALSE; bstrText = ::SysAllocString(T2OLE(lpstrText)); return (bstrText != NULL) ? TRUE : FALSE; } #endif // !_ATL_NO_COM #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) int GetText(int nPane, _CSTRING_NS::CString& strText, int* pType = NULL) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nPane < 256); int nLength = (int)(short)LOWORD(::SendMessage(m_hWnd, SB_GETTEXTLENGTH, (WPARAM)nPane, 0L)); if(nLength == 0) return 0; LPTSTR lpstr = strText.GetBufferSetLength(nLength); if(lpstr == NULL) return 0; return GetText(nPane, lpstr, pType); } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) BOOL SetText(int nPane, LPCTSTR lpszText, int nType = 0) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nPane < 256); return (BOOL)::SendMessage(m_hWnd, SB_SETTEXT, (nPane | nType), (LPARAM)lpszText); } BOOL GetRect(int nPane, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nPane < 256); return (BOOL)::SendMessage(m_hWnd, SB_GETRECT, nPane, (LPARAM)lpRect); } BOOL GetBorders(int* pBorders) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, SB_GETBORDERS, 0, (LPARAM)pBorders); } BOOL GetBorders(int& nHorz, int& nVert, int& nSpacing) const { ATLASSERT(::IsWindow(m_hWnd)); int borders[3] = { 0, 0, 0 }; BOOL bResult = (BOOL)::SendMessage(m_hWnd, SB_GETBORDERS, 0, (LPARAM)&borders); if(bResult) { nHorz = borders[0]; nVert = borders[1]; nSpacing = borders[2]; } return bResult; } void SetMinHeight(int nMin) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, SB_SETMINHEIGHT, nMin, 0L); } BOOL SetSimple(BOOL bSimple = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, SB_SIMPLE, bSimple, 0L); } BOOL IsSimple() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, SB_ISSIMPLE, 0, 0L); } #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) BOOL GetUnicodeFormat() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, SB_GETUNICODEFORMAT, 0, 0L); } BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, SB_SETUNICODEFORMAT, bUnicode, 0L); } void GetTipText(int nPane, LPTSTR lpstrText, int nSize) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nPane < 256); ::SendMessage(m_hWnd, SB_GETTIPTEXT, MAKEWPARAM(nPane, nSize), (LPARAM)lpstrText); } void SetTipText(int nPane, LPCTSTR lpstrText) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nPane < 256); ::SendMessage(m_hWnd, SB_SETTIPTEXT, nPane, (LPARAM)lpstrText); } #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) #if ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500)) COLORREF SetBkColor(COLORREF clrBk) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, SB_SETBKCOLOR, 0, (LPARAM)clrBk); } HICON GetIcon(int nPane) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nPane < 256); return (HICON)::SendMessage(m_hWnd, SB_GETICON, nPane, 0L); } BOOL SetIcon(int nPane, HICON hIcon) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nPane < 256); return (BOOL)::SendMessage(m_hWnd, SB_SETICON, nPane, (LPARAM)hIcon); } #endif // ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500)) }; typedef CStatusBarCtrlT CStatusBarCtrl; /////////////////////////////////////////////////////////////////////////////// // CTabCtrl template class CTabCtrlT : public TBase { public: // Constructors CTabCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CTabCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return WC_TABCONTROL; } CImageList GetImageList() const { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TCM_GETIMAGELIST, 0, 0L)); } CImageList SetImageList(HIMAGELIST hImageList) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, TCM_SETIMAGELIST, 0, (LPARAM)hImageList)); } int GetItemCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TCM_GETITEMCOUNT, 0, 0L); } BOOL GetItem(int nItem, LPTCITEM pTabCtrlItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TCM_GETITEM, nItem, (LPARAM)pTabCtrlItem); } BOOL SetItem(int nItem, LPTCITEM pTabCtrlItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TCM_SETITEM, nItem, (LPARAM)pTabCtrlItem); } int SetItem(int nItem, UINT mask, LPCTSTR lpszItem, DWORD dwState, DWORD dwStateMask, int iImage, LPARAM lParam) { ATLASSERT(::IsWindow(m_hWnd)); TCITEM tci = { 0 }; tci.mask = mask; tci.pszText = (LPTSTR) lpszItem; tci.dwState = dwState; tci.dwStateMask = dwStateMask; tci.iImage = iImage; tci.lParam = lParam; return (int)::SendMessage(m_hWnd, TCM_SETITEM, nItem, (LPARAM)&tci); } BOOL GetItemRect(int nItem, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TCM_GETITEMRECT, nItem, (LPARAM)lpRect); } int GetCurSel() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TCM_GETCURSEL, 0, 0L); } int SetCurSel(int nItem) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TCM_SETCURSEL, nItem, 0L); } SIZE SetItemSize(SIZE size) { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwSize = (DWORD)::SendMessage(m_hWnd, TCM_SETITEMSIZE, 0, MAKELPARAM(size.cx, size.cy)); SIZE sizeRet = { GET_X_LPARAM(dwSize), GET_Y_LPARAM(dwSize) }; return sizeRet; } void SetItemSize(int cx, int cy) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TCM_SETITEMSIZE, 0, MAKELPARAM(cx, cy)); } void SetPadding(SIZE size) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TCM_SETPADDING, 0, MAKELPARAM(size.cx, size.cy)); } int GetRowCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TCM_GETROWCOUNT, 0, 0L); } #ifndef _WIN32_WCE CToolTipCtrl GetToolTips() const { ATLASSERT(::IsWindow(m_hWnd)); return CToolTipCtrl((HWND)::SendMessage(m_hWnd, TCM_GETTOOLTIPS, 0, 0L)); } // this method is deprecated, please use GetToolTips CToolTipCtrl GetTooltips() const { return GetToolTips(); } void SetToolTips(HWND hWndToolTip) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TCM_SETTOOLTIPS, (WPARAM)hWndToolTip, 0L); } // this method is deprecated, please use SetToolTips void SetTooltips(HWND hWndToolTip) { SetToolTips(hWndToolTip); } #endif // !_WIN32_WCE int GetCurFocus() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TCM_GETCURFOCUS, 0, 0L); } void SetCurFocus(int nItem) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TCM_SETCURFOCUS, nItem, 0L); } BOOL SetItemExtra(int cbExtra) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetItemCount() == 0); // must be empty return (BOOL)::SendMessage(m_hWnd, TCM_SETITEMEXTRA, cbExtra, 0L); } int SetMinTabWidth(int nWidth = -1) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TCM_SETMINTABWIDTH, 0, nWidth); } #if (_WIN32_IE >= 0x0400) DWORD GetExtendedStyle() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, TCM_GETEXTENDEDSTYLE, 0, 0L); } DWORD SetExtendedStyle(DWORD dwExMask, DWORD dwExStyle) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, TCM_SETEXTENDEDSTYLE, dwExMask, dwExStyle); } #ifndef _WIN32_WCE BOOL GetUnicodeFormat() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TCM_GETUNICODEFORMAT, 0, 0L); } BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TCM_SETUNICODEFORMAT, bUnicode, 0L); } #endif // !_WIN32_WCE #endif // (_WIN32_IE >= 0x0400) // Operations int InsertItem(int nItem, LPTCITEM pTabCtrlItem) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TCM_INSERTITEM, nItem, (LPARAM)pTabCtrlItem); } int InsertItem(int nItem, UINT mask, LPCTSTR lpszItem, int iImage, LPARAM lParam) { ATLASSERT(::IsWindow(m_hWnd)); TCITEM tci = { 0 }; tci.mask = mask; tci.pszText = (LPTSTR) lpszItem; tci.iImage = iImage; tci.lParam = lParam; return (int)::SendMessage(m_hWnd, TCM_INSERTITEM, nItem, (LPARAM)&tci); } int InsertItem(int nItem, LPCTSTR lpszItem) { ATLASSERT(::IsWindow(m_hWnd)); TCITEM tci = { 0 }; tci.mask = TCIF_TEXT; tci.pszText = (LPTSTR) lpszItem; return (int)::SendMessage(m_hWnd, TCM_INSERTITEM, nItem, (LPARAM)&tci); } int AddItem(LPTCITEM pTabCtrlItem) { return InsertItem(GetItemCount(), pTabCtrlItem); } int AddItem(UINT mask, LPCTSTR lpszItem, int iImage, LPARAM lParam) { return InsertItem(GetItemCount(), mask, lpszItem, iImage, lParam); } int AddItem(LPCTSTR lpszItem) { return InsertItem(GetItemCount(), lpszItem); } BOOL DeleteItem(int nItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TCM_DELETEITEM, nItem, 0L); } BOOL DeleteAllItems() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TCM_DELETEALLITEMS, 0, 0L); } void AdjustRect(BOOL bLarger, LPRECT lpRect) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TCM_ADJUSTRECT, bLarger, (LPARAM)lpRect); } void RemoveImage(int nImage) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TCM_REMOVEIMAGE, nImage, 0L); } int HitTest(TC_HITTESTINFO* pHitTestInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TCM_HITTEST, 0, (LPARAM)pHitTestInfo); } void DeselectAll(BOOL bExcludeFocus = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TCM_DESELECTALL, bExcludeFocus, 0L); } #if (_WIN32_IE >= 0x0400) BOOL HighlightItem(int nIndex, BOOL bHighlight = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TCM_HIGHLIGHTITEM, nIndex, MAKELPARAM(bHighlight, 0)); } #endif // (_WIN32_IE >= 0x0400) }; typedef CTabCtrlT CTabCtrl; /////////////////////////////////////////////////////////////////////////////// // CTrackBarCtrl template class CTrackBarCtrlT : public TBase { public: // Constructors CTrackBarCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CTrackBarCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return TRACKBAR_CLASS; } int GetLineSize() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TBM_GETLINESIZE, 0, 0L); } int SetLineSize(int nSize) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TBM_SETLINESIZE, 0, nSize); } int GetPageSize() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TBM_GETPAGESIZE, 0, 0L); } int SetPageSize(int nSize) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TBM_SETPAGESIZE, 0, nSize); } int GetRangeMin() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TBM_GETRANGEMIN, 0, 0L); } void SetRangeMin(int nMin, BOOL bRedraw = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_SETRANGEMIN, bRedraw, nMin); } int GetRangeMax() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TBM_GETRANGEMAX, 0, 0L); } void SetRangeMax(int nMax, BOOL bRedraw = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_SETRANGEMAX, bRedraw, nMax); } void GetRange(int& nMin, int& nMax) const { nMin = GetRangeMin(); nMax = GetRangeMax(); } void SetRange(int nMin, int nMax, BOOL bRedraw = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_SETRANGE, bRedraw, MAKELPARAM(nMin, nMax)); } int GetSelStart() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TBM_GETSELSTART, 0, 0L); } void SetSelStart(int nMin, BOOL bRedraw = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_SETSELSTART, bRedraw, (LPARAM)nMin); } int GetSelEnd() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TBM_GETSELEND, 0, 0L); } void SetSelEnd(int nMax, BOOL bRedraw = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_SETSELEND, bRedraw, (LPARAM)nMax); } void GetSelection(int& nMin, int& nMax) const { nMin = GetSelStart(); nMax = GetSelEnd(); } void SetSelection(int nMin, int nMax, BOOL bRedraw = TRUE) { SetSelStart(nMin, FALSE); SetSelEnd(nMax, bRedraw); } void GetChannelRect(LPRECT lprc) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_GETCHANNELRECT, 0, (LPARAM)lprc); } void GetThumbRect(LPRECT lprc) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_GETTHUMBRECT, 0, (LPARAM)lprc); } int GetPos() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TBM_GETPOS, 0, 0L); } void SetPos(int nPos) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_SETPOS, TRUE, nPos); } UINT GetNumTics() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, TBM_GETNUMTICS, 0, 0L); } DWORD* GetTicArray() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD*)::SendMessage(m_hWnd, TBM_GETPTICS, 0, 0L); } int GetTic(int nTic) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TBM_GETTIC, nTic, 0L); } BOOL SetTic(int nTic) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TBM_SETTIC, 0, nTic); } int GetTicPos(int nTic) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TBM_GETTICPOS, nTic, 0L); } void SetTicFreq(int nFreq) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_SETTICFREQ, nFreq, 0L); } int GetThumbLength() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TBM_GETTHUMBLENGTH, 0, 0L); } void SetThumbLength(int nLength) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_SETTHUMBLENGTH, nLength, 0L); } void SetSel(int nStart, int nEnd, BOOL bRedraw = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & TBS_ENABLESELRANGE) != 0); ::SendMessage(m_hWnd, TBM_SETSEL, bRedraw, MAKELPARAM(nStart, nEnd)); } ATL::CWindow GetBuddy(BOOL bLeft = TRUE) const { ATLASSERT(::IsWindow(m_hWnd)); return ATL::CWindow((HWND)::SendMessage(m_hWnd, TBM_GETBUDDY, bLeft, 0L)); } ATL::CWindow SetBuddy(HWND hWndBuddy, BOOL bLeft = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return ATL::CWindow((HWND)::SendMessage(m_hWnd, TBM_SETBUDDY, bLeft, (LPARAM)hWndBuddy)); } #ifndef _WIN32_WCE CToolTipCtrl GetToolTips() const { ATLASSERT(::IsWindow(m_hWnd)); return CToolTipCtrl((HWND)::SendMessage(m_hWnd, TBM_GETTOOLTIPS, 0, 0L)); } void SetToolTips(HWND hWndTT) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_SETTOOLTIPS, (WPARAM)hWndTT, 0L); } int SetTipSide(int nSide) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, TBM_SETTIPSIDE, nSide, 0L); } #endif // !_WIN32_WCE #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) BOOL GetUnicodeFormat() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TBM_GETUNICODEFORMAT, 0, 0L); } BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, TBM_SETUNICODEFORMAT, bUnicode, 0L); } #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) // Operations void ClearSel(BOOL bRedraw = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_CLEARSEL, bRedraw, 0L); } void VerifyPos() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_SETPOS, FALSE, 0L); } void ClearTics(BOOL bRedraw = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_CLEARTICS, bRedraw, 0L); } }; typedef CTrackBarCtrlT CTrackBarCtrl; /////////////////////////////////////////////////////////////////////////////// // CUpDownCtrl template class CUpDownCtrlT : public TBase { public: // Constructors CUpDownCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CUpDownCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return UPDOWN_CLASS; } UINT GetAccel(int nAccel, UDACCEL* pAccel) const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)LOWORD(::SendMessage(m_hWnd, UDM_GETACCEL, nAccel, (LPARAM)pAccel)); } BOOL SetAccel(int nAccel, UDACCEL* pAccel) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)LOWORD(::SendMessage(m_hWnd, UDM_SETACCEL, nAccel, (LPARAM)pAccel)); } UINT GetBase() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)LOWORD(::SendMessage(m_hWnd, UDM_GETBASE, 0, 0L)); } int SetBase(int nBase) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, UDM_SETBASE, nBase, 0L); } ATL::CWindow GetBuddy() const { ATLASSERT(::IsWindow(m_hWnd)); return ATL::CWindow((HWND)::SendMessage(m_hWnd, UDM_GETBUDDY, 0, 0L)); } ATL::CWindow SetBuddy(HWND hWndBuddy) { ATLASSERT(::IsWindow(m_hWnd)); return ATL::CWindow((HWND)::SendMessage(m_hWnd, UDM_SETBUDDY, (WPARAM)hWndBuddy, 0L)); } int GetPos(LPBOOL lpbError = NULL) const { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, UDM_GETPOS, 0, 0L); // Note: Seems that Windows always sets error to TRUE if // UDS_SETBUDDYINT style is not used if(lpbError != NULL) *lpbError = (HIWORD(dwRet) != 0) ? TRUE : FALSE; return (int)(short)LOWORD(dwRet); } int SetPos(int nPos) { ATLASSERT(::IsWindow(m_hWnd)); return (int)(short)LOWORD(::SendMessage(m_hWnd, UDM_SETPOS, 0, MAKELPARAM(nPos, 0))); } DWORD GetRange() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, UDM_GETRANGE, 0, 0L); } void GetRange(int& nLower, int& nUpper) const { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, UDM_GETRANGE, 0, 0L); nLower = (int)(short)HIWORD(dwRet); nUpper = (int)(short)LOWORD(dwRet); } void SetRange(int nLower, int nUpper) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, UDM_SETRANGE, 0, MAKELPARAM(nUpper, nLower)); } #if (_WIN32_IE >= 0x0400) void SetRange32(int nLower, int nUpper) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, UDM_SETRANGE32, nLower, nUpper); } void GetRange32(int& nLower, int& nUpper) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, UDM_GETRANGE32, (WPARAM)&nLower, (LPARAM)&nUpper); } #ifndef _WIN32_WCE BOOL GetUnicodeFormat() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, UDM_GETUNICODEFORMAT, 0, 0L); } BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, UDM_SETUNICODEFORMAT, bUnicode, 0L); } #endif // !_WIN32_WCE #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) int GetPos32(LPBOOL lpbError = NULL) const { ATLASSERT(::IsWindow(m_hWnd)); // Note: Seems that Windows always sets error to TRUE if // UDS_SETBUDDYINT style is not used return (int)::SendMessage(m_hWnd, UDM_GETPOS32, 0, (LPARAM)lpbError); } int SetPos32(int nPos) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, UDM_SETPOS32, 0, (LPARAM)nPos); } #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) }; typedef CUpDownCtrlT CUpDownCtrl; /////////////////////////////////////////////////////////////////////////////// // CProgressBarCtrl template class CProgressBarCtrlT : public TBase { public: // Constructors CProgressBarCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CProgressBarCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return PROGRESS_CLASS; } DWORD SetRange(int nLower, int nUpper) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, PBM_SETRANGE, 0, MAKELPARAM(nLower, nUpper)); } int SetPos(int nPos) { ATLASSERT(::IsWindow(m_hWnd)); return (int)(short)LOWORD(::SendMessage(m_hWnd, PBM_SETPOS, nPos, 0L)); } int OffsetPos(int nPos) { ATLASSERT(::IsWindow(m_hWnd)); return (int)(short)LOWORD(::SendMessage(m_hWnd, PBM_DELTAPOS, nPos, 0L)); } int SetStep(int nStep) { ATLASSERT(::IsWindow(m_hWnd)); return (int)(short)LOWORD(::SendMessage(m_hWnd, PBM_SETSTEP, nStep, 0L)); } UINT GetPos() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, PBM_GETPOS, 0, 0L); } void GetRange(PPBRANGE pPBRange) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pPBRange != NULL); ::SendMessage(m_hWnd, PBM_GETRANGE, TRUE, (LPARAM)pPBRange); } void GetRange(int& nLower, int& nUpper) const { ATLASSERT(::IsWindow(m_hWnd)); PBRANGE range = { 0 }; ::SendMessage(m_hWnd, PBM_GETRANGE, TRUE, (LPARAM)&range); nLower = range.iLow; nUpper = range.iHigh; } int GetRangeLimit(BOOL bLowLimit) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PBM_GETRANGE, bLowLimit, (LPARAM)NULL); } DWORD SetRange32(int nMin, int nMax) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, PBM_SETRANGE32, nMin, nMax); } #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) COLORREF SetBarColor(COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, PBM_SETBARCOLOR, 0, (LPARAM)clr); } COLORREF SetBkColor(COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, PBM_SETBKCOLOR, 0, (LPARAM)clr); } #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) #if (_WIN32_WINNT >= 0x0501) && defined(PBM_SETMARQUEE) BOOL SetMarquee(BOOL bMarquee, UINT uUpdateTime = 0U) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, PBM_SETMARQUEE, (WPARAM)bMarquee, (LPARAM)uUpdateTime); } #endif // (_WIN32_WINNT >= 0x0501) && defined(PBM_SETMARQUEE) #if (_WIN32_WINNT >= 0x0600) int GetStep() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PBM_GETSTEP, 0, 0L); } COLORREF GetBkColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, PBM_GETBKCOLOR, 0, 0L); } COLORREF GetBarColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, PBM_GETBARCOLOR, 0, 0L); } int GetState() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PBM_GETSTATE, 0, 0L); } int SetState(int nState) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PBM_SETSTATE, nState, 0L); } #endif // (_WIN32_WINNT >= 0x0600) // Operations int StepIt() { ATLASSERT(::IsWindow(m_hWnd)); return (int)(short)LOWORD(::SendMessage(m_hWnd, PBM_STEPIT, 0, 0L)); } }; typedef CProgressBarCtrlT CProgressBarCtrl; /////////////////////////////////////////////////////////////////////////////// // CHotKeyCtrl #ifndef _WIN32_WCE template class CHotKeyCtrlT : public TBase { public: // Constructors CHotKeyCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CHotKeyCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return HOTKEY_CLASS; } DWORD GetHotKey() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, HKM_GETHOTKEY, 0, 0L); } void GetHotKey(WORD &wVirtualKeyCode, WORD &wModifiers) const { ATLASSERT(::IsWindow(m_hWnd)); DWORD dw = (DWORD)::SendMessage(m_hWnd, HKM_GETHOTKEY, 0, 0L); wVirtualKeyCode = LOBYTE(LOWORD(dw)); wModifiers = HIBYTE(LOWORD(dw)); } void SetHotKey(WORD wVirtualKeyCode, WORD wModifiers) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, HKM_SETHOTKEY, MAKEWORD(wVirtualKeyCode, wModifiers), 0L); } void SetRules(WORD wInvalidComb, WORD wModifiers) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, HKM_SETRULES, wInvalidComb, MAKELPARAM(wModifiers, 0)); } }; typedef CHotKeyCtrlT CHotKeyCtrl; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CAnimateCtrl #ifndef _WIN32_WCE template class CAnimateCtrlT : public TBase { public: // Constructors CAnimateCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CAnimateCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return ANIMATE_CLASS; } // Operations BOOL Open(ATL::_U_STRINGorID FileName) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, ACM_OPEN, 0, (LPARAM)FileName.m_lpstr); } BOOL Play(UINT nFrom, UINT nTo, UINT nRep) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, ACM_PLAY, nRep, MAKELPARAM(nFrom, nTo)); } BOOL Stop() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, ACM_STOP, 0, 0L); } BOOL Close() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, ACM_OPEN, 0, 0L); } BOOL Seek(UINT nTo) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, ACM_PLAY, 0, MAKELPARAM(nTo, nTo)); } // Vista only BOOL IsPlaying() const { #ifndef ACM_ISPLAYING const UINT ACM_ISPLAYING = (WM_USER+104); #endif ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, ACM_ISPLAYING, 0, 0L); } }; typedef CAnimateCtrlT CAnimateCtrl; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CRichEditCtrl #ifndef _WIN32_WCE #if defined(_UNICODE) && (_RICHEDIT_VER == 0x0100) #undef RICHEDIT_CLASS #define RICHEDIT_CLASS L"RICHEDIT" #endif #if !defined(_UNICODE) && (_RICHEDIT_VER >= 0x0500) #undef MSFTEDIT_CLASS #define MSFTEDIT_CLASS "RICHEDIT50W" #endif template class CRichEditCtrlT : public TBase { public: // Constructors CRichEditCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CRichEditCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { #if (_RICHEDIT_VER >= 0x0500) return MSFTEDIT_CLASS; #else return RICHEDIT_CLASS; #endif } static LPCTSTR GetLibraryName() { #if (_RICHEDIT_VER >= 0x0500) return _T("MSFTEDIT.DLL"); #elif (_RICHEDIT_VER >= 0x0200) return _T("RICHED20.DLL"); #else return _T("RICHED32.DLL"); #endif } int GetLineCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_GETLINECOUNT, 0, 0L); } BOOL GetModify() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_GETMODIFY, 0, 0L); } void SetModify(BOOL bModified = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETMODIFY, bModified, 0L); } void GetRect(LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_GETRECT, 0, (LPARAM)lpRect); } DWORD GetOptions() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, EM_GETOPTIONS, 0, 0L); } DWORD SetOptions(WORD wOperation, DWORD dwOptions) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, EM_SETOPTIONS, wOperation, dwOptions); } // NOTE: first word in lpszBuffer must contain the size of the buffer! int GetLine(int nIndex, LPTSTR lpszBuffer) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer); } int GetLine(int nIndex, LPTSTR lpszBuffer, int nMaxLength) const { ATLASSERT(::IsWindow(m_hWnd)); *(LPWORD)lpszBuffer = (WORD)nMaxLength; return (int)::SendMessage(m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer); } BOOL CanUndo() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_CANUNDO, 0, 0L); } BOOL CanPaste(UINT nFormat = 0) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_CANPASTE, nFormat, 0L); } void GetSel(LONG& nStartChar, LONG& nEndChar) const { ATLASSERT(::IsWindow(m_hWnd)); CHARRANGE cr = { 0, 0 }; ::SendMessage(m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr); nStartChar = cr.cpMin; nEndChar = cr.cpMax; } void GetSel(CHARRANGE &cr) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr); } int SetSel(LONG nStartChar, LONG nEndChar) { ATLASSERT(::IsWindow(m_hWnd)); CHARRANGE cr = { nStartChar, nEndChar }; return (int)::SendMessage(m_hWnd, EM_EXSETSEL, 0, (LPARAM)&cr); } int SetSel(CHARRANGE &cr) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_EXSETSEL, 0, (LPARAM)&cr); } int SetSelAll() { return SetSel(0, -1); } int SetSelNone() { return SetSel(-1, 0); } DWORD GetDefaultCharFormat(CHARFORMAT& cf) const { ATLASSERT(::IsWindow(m_hWnd)); cf.cbSize = sizeof(CHARFORMAT); return (DWORD)::SendMessage(m_hWnd, EM_GETCHARFORMAT, 0, (LPARAM)&cf); } DWORD GetSelectionCharFormat(CHARFORMAT& cf) const { ATLASSERT(::IsWindow(m_hWnd)); cf.cbSize = sizeof(CHARFORMAT); return (DWORD)::SendMessage(m_hWnd, EM_GETCHARFORMAT, 1, (LPARAM)&cf); } DWORD GetEventMask() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, EM_GETEVENTMASK, 0, 0L); } LONG GetLimitText() const { ATLASSERT(::IsWindow(m_hWnd)); return (LONG)::SendMessage(m_hWnd, EM_GETLIMITTEXT, 0, 0L); } DWORD GetParaFormat(PARAFORMAT& pf) const { ATLASSERT(::IsWindow(m_hWnd)); pf.cbSize = sizeof(PARAFORMAT); return (DWORD)::SendMessage(m_hWnd, EM_GETPARAFORMAT, 0, (LPARAM)&pf); } #if (_RICHEDIT_VER >= 0x0200) LONG GetSelText(LPTSTR lpstrBuff) const { ATLASSERT(::IsWindow(m_hWnd)); return (LONG)::SendMessage(m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpstrBuff); } #else // !(_RICHEDIT_VER >= 0x0200) // RichEdit 1.0 EM_GETSELTEXT is ANSI only LONG GetSelText(LPSTR lpstrBuff) const { ATLASSERT(::IsWindow(m_hWnd)); return (LONG)::SendMessage(m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpstrBuff); } #endif // !(_RICHEDIT_VER >= 0x0200) #ifndef _ATL_NO_COM BOOL GetSelTextBSTR(BSTR& bstrText) const { USES_CONVERSION; ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(bstrText == NULL); CHARRANGE cr = { 0, 0 }; ::SendMessage(m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr); #if (_RICHEDIT_VER >= 0x0200) CTempBuffer buff; LPTSTR lpstrText = buff.Allocate(cr.cpMax - cr.cpMin + 1); if(lpstrText == NULL) return FALSE; if(::SendMessage(m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpstrText) == 0) return FALSE; bstrText = ::SysAllocString(T2W(lpstrText)); #else // !(_RICHEDIT_VER >= 0x0200) CTempBuffer buff; LPSTR lpstrText = buff.Allocate(cr.cpMax - cr.cpMin + 1); if(lpstrText == NULL) return FALSE; if(::SendMessage(m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpstrText) == 0) return FALSE; bstrText = ::SysAllocString(A2W(lpstrText)); #endif // !(_RICHEDIT_VER >= 0x0200) return (bstrText != NULL) ? TRUE : FALSE; } #endif // !_ATL_NO_COM #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) LONG GetSelText(_CSTRING_NS::CString& strText) const { ATLASSERT(::IsWindow(m_hWnd)); CHARRANGE cr = { 0, 0 }; ::SendMessage(m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr); #if (_RICHEDIT_VER >= 0x0200) LONG lLen = 0; LPTSTR lpstrText = strText.GetBufferSetLength(cr.cpMax - cr.cpMin); if(lpstrText != NULL) { lLen = (LONG)::SendMessage(m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpstrText); strText.ReleaseBuffer(); } #else // !(_RICHEDIT_VER >= 0x0200) CTempBuffer buff; LPSTR lpstrText = buff.Allocate(cr.cpMax - cr.cpMin + 1); if(lpstrText == NULL) return 0; LONG lLen = (LONG)::SendMessage(m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpstrText); if(lLen == 0) return 0; USES_CONVERSION; strText = A2T(lpstrText); #endif // !(_RICHEDIT_VER >= 0x0200) return lLen; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) WORD GetSelectionType() const { ATLASSERT(::IsWindow(m_hWnd)); return (WORD)::SendMessage(m_hWnd, EM_SELECTIONTYPE, 0, 0L); } COLORREF SetBackgroundColor(COLORREF cr) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, EM_SETBKGNDCOLOR, 0, cr); } COLORREF SetBackgroundColor() // sets to system background { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, EM_SETBKGNDCOLOR, 1, 0); } BOOL SetCharFormat(CHARFORMAT& cf, WORD wFlags) { ATLASSERT(::IsWindow(m_hWnd)); cf.cbSize = sizeof(CHARFORMAT); return (BOOL)::SendMessage(m_hWnd, EM_SETCHARFORMAT, (WPARAM)wFlags, (LPARAM)&cf); } BOOL SetDefaultCharFormat(CHARFORMAT& cf) { ATLASSERT(::IsWindow(m_hWnd)); cf.cbSize = sizeof(CHARFORMAT); return (BOOL)::SendMessage(m_hWnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf); } BOOL SetSelectionCharFormat(CHARFORMAT& cf) { ATLASSERT(::IsWindow(m_hWnd)); cf.cbSize = sizeof(CHARFORMAT); return (BOOL)::SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); } BOOL SetWordCharFormat(CHARFORMAT& cf) { ATLASSERT(::IsWindow(m_hWnd)); cf.cbSize = sizeof(CHARFORMAT); return (BOOL)::SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION | SCF_WORD, (LPARAM)&cf); } DWORD SetEventMask(DWORD dwEventMask) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, EM_SETEVENTMASK, 0, dwEventMask); } BOOL SetParaFormat(PARAFORMAT& pf) { ATLASSERT(::IsWindow(m_hWnd)); pf.cbSize = sizeof(PARAFORMAT); return (BOOL)::SendMessage(m_hWnd, EM_SETPARAFORMAT, 0, (LPARAM)&pf); } BOOL SetTargetDevice(HDC hDC, int cxLineWidth) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETTARGETDEVICE, (WPARAM)hDC, cxLineWidth); } int GetTextLength() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, WM_GETTEXTLENGTH, 0, 0L); } BOOL SetReadOnly(BOOL bReadOnly = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETREADONLY, bReadOnly, 0L); } int GetFirstVisibleLine() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_GETFIRSTVISIBLELINE, 0, 0L); } int GetTextRange(TEXTRANGE* pTextRange) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_GETTEXTRANGE, 0, (LPARAM)pTextRange); } #if (_RICHEDIT_VER < 0x0200) EDITWORDBREAKPROCEX GetWordBreakProcEx() const { ATLASSERT(::IsWindow(m_hWnd)); return (EDITWORDBREAKPROCEX)::SendMessage(m_hWnd, EM_GETWORDBREAKPROCEX, 0, 0L); } EDITWORDBREAKPROCEX SetWordBreakProcEx(EDITWORDBREAKPROCEX pfnEditWordBreakProcEx) { ATLASSERT(::IsWindow(m_hWnd)); return (EDITWORDBREAKPROCEX)::SendMessage(m_hWnd, EM_SETWORDBREAKPROCEX, 0, (LPARAM)pfnEditWordBreakProcEx); } #endif // (_RICHEDIT_VER < 0x0200) #if (_RICHEDIT_VER >= 0x0200) int GetTextRange(LONG nStartChar, LONG nEndChar, LPTSTR lpstrText) const { ATLASSERT(::IsWindow(m_hWnd)); TEXTRANGE tr = { 0 }; tr.chrg.cpMin = nStartChar; tr.chrg.cpMax = nEndChar; tr.lpstrText = lpstrText; return (int)::SendMessage(m_hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr); } #else // !(_RICHEDIT_VER >= 0x0200) int GetTextRange(LONG nStartChar, LONG nEndChar, LPSTR lpstrText) const { ATLASSERT(::IsWindow(m_hWnd)); TEXTRANGE tr = { 0 }; tr.chrg.cpMin = nStartChar; tr.chrg.cpMax = nEndChar; tr.lpstrText = lpstrText; return (int)::SendMessage(m_hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr); } #endif // !(_RICHEDIT_VER >= 0x0200) #if (_RICHEDIT_VER >= 0x0200) DWORD GetDefaultCharFormat(CHARFORMAT2& cf) const { ATLASSERT(::IsWindow(m_hWnd)); cf.cbSize = sizeof(CHARFORMAT2); return (DWORD)::SendMessage(m_hWnd, EM_GETCHARFORMAT, 0, (LPARAM)&cf); } BOOL SetCharFormat(CHARFORMAT2& cf, WORD wFlags) { ATLASSERT(::IsWindow(m_hWnd)); cf.cbSize = sizeof(CHARFORMAT2); return (BOOL)::SendMessage(m_hWnd, EM_SETCHARFORMAT, (WPARAM)wFlags, (LPARAM)&cf); } BOOL SetDefaultCharFormat(CHARFORMAT2& cf) { ATLASSERT(::IsWindow(m_hWnd)); cf.cbSize = sizeof(CHARFORMAT2); return (BOOL)::SendMessage(m_hWnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf); } DWORD GetSelectionCharFormat(CHARFORMAT2& cf) const { ATLASSERT(::IsWindow(m_hWnd)); cf.cbSize = sizeof(CHARFORMAT2); return (DWORD)::SendMessage(m_hWnd, EM_GETCHARFORMAT, 1, (LPARAM)&cf); } BOOL SetSelectionCharFormat(CHARFORMAT2& cf) { ATLASSERT(::IsWindow(m_hWnd)); cf.cbSize = sizeof(CHARFORMAT2); return (BOOL)::SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); } BOOL SetWordCharFormat(CHARFORMAT2& cf) { ATLASSERT(::IsWindow(m_hWnd)); cf.cbSize = sizeof(CHARFORMAT2); return (BOOL)::SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION | SCF_WORD, (LPARAM)&cf); } DWORD GetParaFormat(PARAFORMAT2& pf) const { ATLASSERT(::IsWindow(m_hWnd)); pf.cbSize = sizeof(PARAFORMAT2); return (DWORD)::SendMessage(m_hWnd, EM_GETPARAFORMAT, 0, (LPARAM)&pf); } BOOL SetParaFormat(PARAFORMAT2& pf) { ATLASSERT(::IsWindow(m_hWnd)); pf.cbSize = sizeof(PARAFORMAT2); return (BOOL)::SendMessage(m_hWnd, EM_SETPARAFORMAT, 0, (LPARAM)&pf); } TEXTMODE GetTextMode() const { ATLASSERT(::IsWindow(m_hWnd)); return (TEXTMODE)::SendMessage(m_hWnd, EM_GETTEXTMODE, 0, 0L); } BOOL SetTextMode(TEXTMODE enumTextMode) { ATLASSERT(::IsWindow(m_hWnd)); return !(BOOL)::SendMessage(m_hWnd, EM_SETTEXTMODE, enumTextMode, 0L); } UNDONAMEID GetUndoName() const { ATLASSERT(::IsWindow(m_hWnd)); return (UNDONAMEID)::SendMessage(m_hWnd, EM_GETUNDONAME, 0, 0L); } UNDONAMEID GetRedoName() const { ATLASSERT(::IsWindow(m_hWnd)); return (UNDONAMEID)::SendMessage(m_hWnd, EM_GETREDONAME, 0, 0L); } BOOL CanRedo() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_CANREDO, 0, 0L); } BOOL GetAutoURLDetect() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_GETAUTOURLDETECT, 0, 0L); } BOOL SetAutoURLDetect(BOOL bAutoDetect = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return !(BOOL)::SendMessage(m_hWnd, EM_AUTOURLDETECT, bAutoDetect, 0L); } // this method is deprecated, please use SetAutoURLDetect BOOL EnableAutoURLDetect(BOOL bEnable = TRUE) { return SetAutoURLDetect(bEnable); } UINT SetUndoLimit(UINT uUndoLimit) { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, EM_SETUNDOLIMIT, uUndoLimit, 0L); } void SetPalette(HPALETTE hPalette) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETPALETTE, (WPARAM)hPalette, 0L); } int GetTextEx(GETTEXTEX* pGetTextEx, LPTSTR lpstrText) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_GETTEXTEX, (WPARAM)pGetTextEx, (LPARAM)lpstrText); } int GetTextEx(LPTSTR lpstrText, int nTextLen, DWORD dwFlags = GT_DEFAULT, UINT uCodePage = CP_ACP, LPCSTR lpDefaultChar = NULL, LPBOOL lpUsedDefChar = NULL) const { ATLASSERT(::IsWindow(m_hWnd)); GETTEXTEX gte = { 0 }; gte.cb = nTextLen * sizeof(TCHAR); gte.codepage = uCodePage; gte.flags = dwFlags; gte.lpDefaultChar = lpDefaultChar; gte.lpUsedDefChar = lpUsedDefChar; return (int)::SendMessage(m_hWnd, EM_GETTEXTEX, (WPARAM)>e, (LPARAM)lpstrText); } int GetTextLengthEx(GETTEXTLENGTHEX* pGetTextLengthEx) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_GETTEXTLENGTHEX, (WPARAM)pGetTextLengthEx, 0L); } int GetTextLengthEx(DWORD dwFlags = GTL_DEFAULT, UINT uCodePage = CP_ACP) const { ATLASSERT(::IsWindow(m_hWnd)); GETTEXTLENGTHEX gtle = { 0 }; gtle.codepage = uCodePage; gtle.flags = dwFlags; return (int)::SendMessage(m_hWnd, EM_GETTEXTLENGTHEX, (WPARAM)>le, 0L); } EDITWORDBREAKPROC GetWordBreakProc() const { ATLASSERT(::IsWindow(m_hWnd)); return (EDITWORDBREAKPROC)::SendMessage(m_hWnd, EM_GETWORDBREAKPROC, 0, 0L); } void SetWordBreakProc(EDITWORDBREAKPROC ewbprc) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETWORDBREAKPROC, 0, (LPARAM)ewbprc); } #endif // (_RICHEDIT_VER >= 0x0200) #if (_RICHEDIT_VER >= 0x0300) int SetTextEx(SETTEXTEX* pSetTextEx, LPCTSTR lpstrText) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_SETTEXTEX, (WPARAM)pSetTextEx, (LPARAM)lpstrText); } int SetTextEx(LPCTSTR lpstrText, DWORD dwFlags = ST_DEFAULT, UINT uCodePage = CP_ACP) { ATLASSERT(::IsWindow(m_hWnd)); SETTEXTEX ste = { 0 }; ste.flags = dwFlags; ste.codepage = uCodePage; return (int)::SendMessage(m_hWnd, EM_SETTEXTEX, (WPARAM)&ste, (LPARAM)lpstrText); } int GetEditStyle() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_GETEDITSTYLE, 0, 0L); } int SetEditStyle(int nStyle, int nMask = -1) { ATLASSERT(::IsWindow(m_hWnd)); if(nMask == -1) nMask = nStyle; // set everything specified return (int)::SendMessage(m_hWnd, EM_SETEDITSTYLE, nStyle, nMask); } BOOL SetFontSize(int nFontSizeDelta) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nFontSizeDelta >= -1637 && nFontSizeDelta <= 1638); return (BOOL)::SendMessage(m_hWnd, EM_SETFONTSIZE, nFontSizeDelta, 0L); } void GetScrollPos(LPPOINT lpPoint) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(lpPoint != NULL); ::SendMessage(m_hWnd, EM_GETSCROLLPOS, 0, (LPARAM)lpPoint); } void SetScrollPos(LPPOINT lpPoint) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(lpPoint != NULL); ::SendMessage(m_hWnd, EM_SETSCROLLPOS, 0, (LPARAM)lpPoint); } BOOL GetZoom(int& nNum, int& nDen) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_GETZOOM, (WPARAM)&nNum, (LPARAM)&nDen); } BOOL SetZoom(int nNum, int nDen) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nNum >= 0 && nNum <= 64); ATLASSERT(nDen >= 0 && nDen <= 64); return (BOOL)::SendMessage(m_hWnd, EM_SETZOOM, nNum, nDen); } BOOL SetZoomOff() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETZOOM, 0, 0L); } void SetMargins(UINT nLeft, UINT nRight, WORD wFlags = EC_LEFTMARGIN | EC_RIGHTMARGIN) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETMARGINS, wFlags, MAKELONG(nLeft, nRight)); } #endif // (_RICHEDIT_VER >= 0x0300) // Operations void LimitText(LONG nChars = 0) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_EXLIMITTEXT, 0, nChars); } int LineFromChar(LONG nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_EXLINEFROMCHAR, 0, nIndex); } POINT PosFromChar(LONG nChar) const { ATLASSERT(::IsWindow(m_hWnd)); POINT point = { 0, 0 }; ::SendMessage(m_hWnd, EM_POSFROMCHAR, (WPARAM)&point, nChar); return point; } int CharFromPos(POINT pt) const { ATLASSERT(::IsWindow(m_hWnd)); POINTL ptl = { pt.x, pt.y }; return (int)::SendMessage(m_hWnd, EM_CHARFROMPOS, 0, (LPARAM)&ptl); } void EmptyUndoBuffer() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_EMPTYUNDOBUFFER, 0, 0L); } int LineIndex(int nLine = -1) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_LINEINDEX, nLine, 0L); } int LineLength(int nLine = -1) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_LINELENGTH, nLine, 0L); } BOOL LineScroll(int nLines) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_LINESCROLL, 0, nLines); } void ReplaceSel(LPCTSTR lpszNewText, BOOL bCanUndo = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_REPLACESEL, (WPARAM) bCanUndo, (LPARAM)lpszNewText); } void SetRect(LPCRECT lpRect) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETRECT, 0, (LPARAM)lpRect); } BOOL DisplayBand(LPRECT pDisplayRect) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_DISPLAYBAND, 0, (LPARAM)pDisplayRect); } LONG FindText(DWORD dwFlags, FINDTEXT& ft) const { ATLASSERT(::IsWindow(m_hWnd)); #if (_RICHEDIT_VER >= 0x0200) && defined(_UNICODE) return (LONG)::SendMessage(m_hWnd, EM_FINDTEXTW, dwFlags, (LPARAM)&ft); #else return (LONG)::SendMessage(m_hWnd, EM_FINDTEXT, dwFlags, (LPARAM)&ft); #endif } LONG FindText(DWORD dwFlags, FINDTEXTEX& ft) const { ATLASSERT(::IsWindow(m_hWnd)); #if (_RICHEDIT_VER >= 0x0200) && defined(_UNICODE) return (LONG)::SendMessage(m_hWnd, EM_FINDTEXTEXW, dwFlags, (LPARAM)&ft); #else return (LONG)::SendMessage(m_hWnd, EM_FINDTEXTEX, dwFlags, (LPARAM)&ft); #endif } LONG FormatRange(FORMATRANGE& fr, BOOL bDisplay = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (LONG)::SendMessage(m_hWnd, EM_FORMATRANGE, bDisplay, (LPARAM)&fr); } LONG FormatRange(FORMATRANGE* pFormatRange, BOOL bDisplay = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (LONG)::SendMessage(m_hWnd, EM_FORMATRANGE, bDisplay, (LPARAM)pFormatRange); } void HideSelection(BOOL bHide = TRUE, BOOL bChangeStyle = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_HIDESELECTION, bHide, bChangeStyle); } void PasteSpecial(UINT uClipFormat, DWORD dwAspect = 0, HMETAFILE hMF = 0) { ATLASSERT(::IsWindow(m_hWnd)); REPASTESPECIAL reps = { dwAspect, (DWORD_PTR)hMF }; ::SendMessage(m_hWnd, EM_PASTESPECIAL, uClipFormat, (LPARAM)&reps); } void RequestResize() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_REQUESTRESIZE, 0, 0L); } LONG StreamIn(UINT uFormat, EDITSTREAM& es) { ATLASSERT(::IsWindow(m_hWnd)); return (LONG)::SendMessage(m_hWnd, EM_STREAMIN, uFormat, (LPARAM)&es); } LONG StreamOut(UINT uFormat, EDITSTREAM& es) { ATLASSERT(::IsWindow(m_hWnd)); return (LONG)::SendMessage(m_hWnd, EM_STREAMOUT, uFormat, (LPARAM)&es); } DWORD FindWordBreak(int nCode, LONG nStartChar) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, EM_FINDWORDBREAK, nCode, nStartChar); } // Additional operations void ScrollCaret() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SCROLLCARET, 0, 0L); } int InsertText(long nInsertAfterChar, LPCTSTR lpstrText, BOOL bCanUndo = FALSE) { int nRet = SetSel(nInsertAfterChar, nInsertAfterChar); ReplaceSel(lpstrText, bCanUndo); return nRet; } int AppendText(LPCTSTR lpstrText, BOOL bCanUndo = FALSE) { return InsertText(GetWindowTextLength(), lpstrText, bCanUndo); } // Clipboard operations BOOL Undo() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_UNDO, 0, 0L); } void Clear() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_CLEAR, 0, 0L); } void Copy() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_COPY, 0, 0L); } void Cut() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_CUT, 0, 0L); } void Paste() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_PASTE, 0, 0L); } // OLE support IRichEditOle* GetOleInterface() const { ATLASSERT(::IsWindow(m_hWnd)); IRichEditOle *pRichEditOle = NULL; ::SendMessage(m_hWnd, EM_GETOLEINTERFACE, 0, (LPARAM)&pRichEditOle); return pRichEditOle; } BOOL SetOleCallback(IRichEditOleCallback* pCallback) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETOLECALLBACK, 0, (LPARAM)pCallback); } #if (_RICHEDIT_VER >= 0x0200) BOOL Redo() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_REDO, 0, 0L); } void StopGroupTyping() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_STOPGROUPTYPING, 0, 0L); } void ShowScrollBar(int nBarType, BOOL bVisible = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SHOWSCROLLBAR, nBarType, bVisible); } #endif // (_RICHEDIT_VER >= 0x0200) #if (_RICHEDIT_VER >= 0x0300) BOOL SetTabStops(int nTabStops, LPINT rgTabStops) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETTABSTOPS, nTabStops, (LPARAM)rgTabStops); } BOOL SetTabStops() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETTABSTOPS, 0, 0L); } BOOL SetTabStops(const int& cxEachStop) // takes an 'int' { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETTABSTOPS, 1, (LPARAM)(LPINT)&cxEachStop); } #endif // (_RICHEDIT_VER >= 0x0300) #if (_RICHEDIT_VER >= 0x0800) AutoCorrectProc GetAutoCorrectProc() const { ATLASSERT(::IsWindow(m_hWnd)); return (AutoCorrectProc)::SendMessage(m_hWnd, EM_GETAUTOCORRECTPROC, 0, 0L); } BOOL SetAutoCorrectProc(AutoCorrectProc pfn) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETAUTOCORRECTPROC, (WPARAM)pfn, 0L); } BOOL CallAutoCorrectProc(WCHAR ch) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_CALLAUTOCORRECTPROC, (WPARAM)ch, 0L); } DWORD GetEditStyleEx() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, EM_GETEDITSTYLEEX, 0, 0L); } DWORD SetEditStyleEx(DWORD dwStyleEx, DWORD dwMask) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, EM_SETEDITSTYLEEX, dwStyleEx, dwMask); } DWORD GetStoryType(int nStoryIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, EM_GETSTORYTYPE, nStoryIndex, 0L); } DWORD SetStoryType(int nStoryIndex, DWORD dwStoryType) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, EM_SETSTORYTYPE, nStoryIndex, dwStoryType); } DWORD GetEllipsisMode() const { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwMode = 0; BOOL bRet = (BOOL)::SendMessage(m_hWnd, EM_GETELLIPSISMODE, 0, (LPARAM)&dwMode); bRet; // avoid level 4 warning ATLASSERT(bRet != FALSE); return dwMode; } BOOL SetEllipsisMode(DWORD dwEllipsisMode) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETELLIPSISMODE, 0, dwEllipsisMode); } BOOL GetEllipsisState() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_GETELLIPSISSTATE, 0, 0L); } BOOL GetTouchOptions(int nTouchOptions) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_GETTOUCHOPTIONS, nTouchOptions, 0L); } void SetTouchOptions(int nTouchOptions, BOOL bEnable) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETTOUCHOPTIONS, nTouchOptions, bEnable); } HRESULT InsertTable(TABLEROWPARMS* pRowParams, TABLECELLPARMS* pCellParams) { ATLASSERT(::IsWindow(m_hWnd)); return (HRESULT)::SendMessage(m_hWnd, EM_INSERTTABLE, (WPARAM)pRowParams, (LPARAM)pCellParams); } HRESULT GetTableParams(TABLEROWPARMS* pRowParams, TABLECELLPARMS* pCellParams) const { ATLASSERT(::IsWindow(m_hWnd)); return (HRESULT)::SendMessage(m_hWnd, EM_GETTABLEPARMS, (WPARAM)pRowParams, (LPARAM)pCellParams); } HRESULT SetTableParams(TABLEROWPARMS* pRowParams, TABLECELLPARMS* pCellParams) { ATLASSERT(::IsWindow(m_hWnd)); return (HRESULT)::SendMessage(m_hWnd, EM_SETTABLEPARMS, (WPARAM)pRowParams, (LPARAM)pCellParams); } HRESULT InsertImage(RICHEDIT_IMAGE_PARAMETERS* pParams) { ATLASSERT(::IsWindow(m_hWnd)); return (HRESULT)::SendMessage(m_hWnd, EM_INSERTIMAGE, 0, (LPARAM)pParams); } BOOL SetUiaName(LPCTSTR lpstrName) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_SETUIANAME, 0, (LPARAM)lpstrName); } #endif // (_RICHEDIT_VER >= 0x0800) }; typedef CRichEditCtrlT CRichEditCtrl; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CRichEditCommands - message handlers for standard EDIT commands #ifndef _WIN32_WCE // Chain to CRichEditCommands message map. Your class must also derive from CRichEditCtrl. // Example: // class CMyRichEdit : public CWindowImpl, // public CRichEditCommands // { // public: // BEGIN_MSG_MAP(CMyRichEdit) // // your handlers... // CHAIN_MSG_MAP_ALT(CRichEditCommands, 1) // END_MSG_MAP() // // other stuff... // }; template class CRichEditCommands : public CEditCommands< T > { public: BEGIN_MSG_MAP(CRichEditCommands< T >) ALT_MSG_MAP(1) COMMAND_ID_HANDLER(ID_EDIT_CLEAR, CEditCommands< T >::OnEditClear) COMMAND_ID_HANDLER(ID_EDIT_CLEAR_ALL, CEditCommands< T >::OnEditClearAll) COMMAND_ID_HANDLER(ID_EDIT_COPY, CEditCommands< T >::OnEditCopy) COMMAND_ID_HANDLER(ID_EDIT_CUT, CEditCommands< T >::OnEditCut) COMMAND_ID_HANDLER(ID_EDIT_PASTE, CEditCommands< T >::OnEditPaste) COMMAND_ID_HANDLER(ID_EDIT_SELECT_ALL, CEditCommands< T >::OnEditSelectAll) COMMAND_ID_HANDLER(ID_EDIT_UNDO, CEditCommands< T >::OnEditUndo) #if (_RICHEDIT_VER >= 0x0200) COMMAND_ID_HANDLER(ID_EDIT_REDO, OnEditRedo) #endif // (_RICHEDIT_VER >= 0x0200) END_MSG_MAP() #if (_RICHEDIT_VER >= 0x0200) LRESULT OnEditRedo(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->Redo(); return 0; } #endif // (_RICHEDIT_VER >= 0x0200) // State (update UI) helpers BOOL CanCut() const { return HasSelection(); } BOOL CanCopy() const { return HasSelection(); } BOOL CanClear() const { return HasSelection(); } // Implementation BOOL HasSelection() const { const T* pT = static_cast(this); return (pT->GetSelectionType() != SEL_EMPTY); } }; #endif // _WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CDragListBox #ifndef _WIN32_WCE template class CDragListBoxT : public CListBoxT< TBase > { public: // Constructors CDragListBoxT(HWND hWnd = NULL) : CListBoxT< TBase >(hWnd) { } CDragListBoxT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { HWND hWnd = TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); if(hWnd != NULL) MakeDragList(); return hWnd; } // Operations BOOL MakeDragList() { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((GetStyle() & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) == 0); return ::MakeDragList(m_hWnd); } int LBItemFromPt(POINT pt, BOOL bAutoScroll = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return ::LBItemFromPt(m_hWnd, pt, bAutoScroll); } void DrawInsert(int nItem) { ATLASSERT(::IsWindow(m_hWnd)); ::DrawInsert(GetParent(), m_hWnd, nItem); } static UINT GetDragListMessage() { static UINT uDragListMessage = 0; if(uDragListMessage == 0) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CDragListBox::GetDragListMessage.\n")); ATLASSERT(FALSE); return 0; } if(uDragListMessage == 0) uDragListMessage = ::RegisterWindowMessage(DRAGLISTMSGSTRING); lock.Unlock(); } ATLASSERT(uDragListMessage != 0); return uDragListMessage; } }; typedef CDragListBoxT CDragListBox; template class CDragListNotifyImpl { public: BEGIN_MSG_MAP(CDragListNotifyImpl< T >) MESSAGE_HANDLER(CDragListBox::GetDragListMessage(), OnDragListNotify) END_MSG_MAP() LRESULT OnDragListNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { uMsg; // avoid level 4 warning ATLASSERT(uMsg == CDragListBox::GetDragListMessage()); T* pT = static_cast(this); LPDRAGLISTINFO lpDragListInfo = (LPDRAGLISTINFO)lParam; LRESULT lRet = 0; switch(lpDragListInfo->uNotification) { case DL_BEGINDRAG: lRet = (LPARAM)pT->OnBeginDrag((int)wParam, lpDragListInfo->hWnd, lpDragListInfo->ptCursor); break; case DL_CANCELDRAG: pT->OnCancelDrag((int)wParam, lpDragListInfo->hWnd, lpDragListInfo->ptCursor); break; case DL_DRAGGING: lRet = (LPARAM)pT->OnDragging((int)wParam, lpDragListInfo->hWnd, lpDragListInfo->ptCursor); break; case DL_DROPPED: pT->OnDropped((int)wParam, lpDragListInfo->hWnd, lpDragListInfo->ptCursor); break; default: ATLTRACE2(atlTraceUI, 0, _T("Unknown DragListBox notification\n")); bHandled = FALSE; // don't handle it break; } return lRet; } // Overrideables BOOL OnBeginDrag(int /*nCtlID*/, HWND /*hWndDragList*/, POINT /*ptCursor*/) { return TRUE; // allow dragging } void OnCancelDrag(int /*nCtlID*/, HWND /*hWndDragList*/, POINT /*ptCursor*/) { // nothing to do } int OnDragging(int /*nCtlID*/, HWND /*hWndDragList*/, POINT /*ptCursor*/) { return 0; // don't change cursor } void OnDropped(int /*nCtlID*/, HWND /*hWndDragList*/, POINT /*ptCursor*/) { // nothing to do } }; #endif // _WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CReBarCtrl template class CReBarCtrlT : public TBase { public: // Constructors CReBarCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CReBarCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return REBARCLASSNAME; } UINT GetBandCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, RB_GETBANDCOUNT, 0, 0L); } BOOL GetBandInfo(int nBand, LPREBARBANDINFO lprbbi) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, RB_GETBANDINFO, nBand, (LPARAM)lprbbi); } BOOL SetBandInfo(int nBand, LPREBARBANDINFO lprbbi) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, RB_SETBANDINFO, nBand, (LPARAM)lprbbi); } BOOL GetBarInfo(LPREBARINFO lprbi) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, RB_GETBARINFO, 0, (LPARAM)lprbi); } BOOL SetBarInfo(LPREBARINFO lprbi) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, RB_SETBARINFO, 0, (LPARAM)lprbi); } CImageList GetImageList() const { ATLASSERT(::IsWindow(m_hWnd)); REBARINFO rbi = { 0 }; rbi.cbSize = sizeof(REBARINFO); rbi.fMask = RBIM_IMAGELIST; BOOL bRet = (BOOL)::SendMessage(m_hWnd, RB_GETBARINFO, 0, (LPARAM)&rbi); return CImageList((bRet != FALSE) ? rbi.himl : NULL); } BOOL SetImageList(HIMAGELIST hImageList) { ATLASSERT(::IsWindow(m_hWnd)); REBARINFO rbi = { 0 }; rbi.cbSize = sizeof(REBARINFO); rbi.fMask = RBIM_IMAGELIST; rbi.himl = hImageList; return (BOOL)::SendMessage(m_hWnd, RB_SETBARINFO, 0, (LPARAM)&rbi); } UINT GetRowCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, RB_GETROWCOUNT, 0, 0L); } UINT GetRowHeight(int nBand) const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, RB_GETROWHEIGHT, nBand, 0L); } #if (_WIN32_IE >= 0x0400) COLORREF GetTextColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, RB_GETTEXTCOLOR, 0, 0L); } COLORREF SetTextColor(COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, RB_SETTEXTCOLOR, 0, (LPARAM)clr); } COLORREF GetBkColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, RB_GETBKCOLOR, 0, 0L); } COLORREF SetBkColor(COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, RB_SETBKCOLOR, 0, (LPARAM)clr); } UINT GetBarHeight() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, RB_GETBARHEIGHT, 0, 0L); } BOOL GetRect(int nBand, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, RB_GETRECT, nBand, (LPARAM)lpRect); } #ifndef _WIN32_WCE CToolTipCtrl GetToolTips() const { ATLASSERT(::IsWindow(m_hWnd)); return CToolTipCtrl((HWND)::SendMessage(m_hWnd, RB_GETTOOLTIPS, 0, 0L)); } void SetToolTips(HWND hwndToolTip) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, RB_SETTOOLTIPS, (WPARAM)hwndToolTip, 0L); } #endif // !_WIN32_WCE void GetBandBorders(int nBand, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(lpRect != NULL); ::SendMessage(m_hWnd, RB_GETBANDBORDERS, nBand, (LPARAM)lpRect); } #ifndef _WIN32_WCE BOOL GetColorScheme(LPCOLORSCHEME lpColorScheme) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(lpColorScheme != NULL); return (BOOL)::SendMessage(m_hWnd, RB_GETCOLORSCHEME, 0, (LPARAM)lpColorScheme); } void SetColorScheme(LPCOLORSCHEME lpColorScheme) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(lpColorScheme != NULL); ::SendMessage(m_hWnd, RB_SETCOLORSCHEME, 0, (LPARAM)lpColorScheme); } HPALETTE GetPalette() const { ATLASSERT(::IsWindow(m_hWnd)); return (HPALETTE)::SendMessage(m_hWnd, RB_GETPALETTE, 0, 0L); } HPALETTE SetPalette(HPALETTE hPalette) { ATLASSERT(::IsWindow(m_hWnd)); return (HPALETTE)::SendMessage(m_hWnd, RB_SETPALETTE, 0, (LPARAM)hPalette); } BOOL GetUnicodeFormat() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, RB_GETUNICODEFORMAT, 0, 0L); } BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, RB_SETUNICODEFORMAT, bUnicode, 0L); } #endif // !_WIN32_WCE #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_WINNT >= 0x0501) // requires uxtheme.h to be included to use MARGINS struct #ifndef _UXTHEME_H_ typedef struct _MARGINS* PMARGINS; #endif // !_UXTHEME_H_ void GetBandMargins(PMARGINS pMargins) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, RB_GETBANDMARGINS, 0, (LPARAM)pMargins); } void SetWindowTheme(LPCWSTR lpstrTheme) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, RB_SETWINDOWTHEME, 0, (LPARAM)lpstrTheme); } #endif // (_WIN32_WINNT >= 0x0501) #if (_WIN32_IE >= 0x0600) DWORD GetExtendedStyle() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, RB_GETEXTENDEDSTYLE, 0, 0L); } DWORD SetExtendedStyle(DWORD dwStyle, DWORD dwMask) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, RB_SETEXTENDEDSTYLE, dwMask, dwStyle); } #endif // (_WIN32_IE >= 0x0600) // Operations BOOL InsertBand(int nBand, LPREBARBANDINFO lprbbi) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, RB_INSERTBAND, nBand, (LPARAM)lprbbi); } BOOL AddBand(LPREBARBANDINFO lprbbi) { return InsertBand(-1, lprbbi); } BOOL DeleteBand(int nBand) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, RB_DELETEBAND, nBand, 0L); } ATL::CWindow SetNotifyWnd(HWND hWnd) { ATLASSERT(::IsWindow(m_hWnd)); return ATL::CWindow((HWND)::SendMessage(m_hWnd, RB_SETPARENT, (WPARAM)hWnd, 0L)); } #if (_WIN32_IE >= 0x0400) void BeginDrag(int nBand, DWORD dwPos) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, RB_BEGINDRAG, nBand, dwPos); } void BeginDrag(int nBand, int xPos, int yPos) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, RB_BEGINDRAG, nBand, MAKELPARAM(xPos, yPos)); } void EndDrag() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, RB_ENDDRAG, 0, 0L); } void DragMove(DWORD dwPos) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, RB_DRAGMOVE, 0, dwPos); } void DragMove(int xPos, int yPos) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, RB_DRAGMOVE, 0, MAKELPARAM(xPos, yPos)); } #ifndef _WIN32_WCE void GetDropTarget(IDropTarget** ppDropTarget) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, RB_GETDROPTARGET, 0, (LPARAM)ppDropTarget); } #endif // !_WIN32_WCE void MaximizeBand(int nBand, BOOL bIdeal = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, RB_MAXIMIZEBAND, nBand, bIdeal); } void MinimizeBand(int nBand) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, RB_MINIMIZEBAND, nBand, 0L); } BOOL SizeToRect(LPRECT lpRect) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, RB_SIZETORECT, 0, (LPARAM)lpRect); } int IdToIndex(UINT uBandID) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, RB_IDTOINDEX, uBandID, 0L); } int HitTest(LPRBHITTESTINFO lprbht) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, RB_HITTEST, 0, (LPARAM)lprbht); } BOOL ShowBand(int nBand, BOOL bShow) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, RB_SHOWBAND, nBand, bShow); } #ifndef _WIN32_WCE BOOL MoveBand(int nBand, int nNewPos) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nNewPos >= 0 && nNewPos <= ((int)GetBandCount() - 1)); return (BOOL)::SendMessage(m_hWnd, RB_MOVEBAND, nBand, nNewPos); } #endif // !_WIN32_WCE #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) void PushChevron(int nBand, LPARAM lAppValue) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, RB_PUSHCHEVRON, nBand, lAppValue); } #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) // Extra operations #if (_WIN32_IE >= 0x0400) void LockBands(bool bLock) { int nBandCount = GetBandCount(); for(int i =0; i < nBandCount; i++) { REBARBANDINFO rbbi = { RunTimeHelper::SizeOf_REBARBANDINFO() }; rbbi.fMask = RBBIM_STYLE; BOOL bRet = GetBandInfo(i, &rbbi); ATLASSERT(bRet); if((rbbi.fStyle & RBBS_GRIPPERALWAYS) == 0) { rbbi.fStyle |= RBBS_GRIPPERALWAYS; bRet = SetBandInfo(i, &rbbi); ATLASSERT(bRet); rbbi.fStyle &= ~RBBS_GRIPPERALWAYS; } if(bLock) rbbi.fStyle |= RBBS_NOGRIPPER; else rbbi.fStyle &= ~RBBS_NOGRIPPER; bRet = SetBandInfo(i, &rbbi); ATLASSERT(bRet); } } #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_WINNT >= 0x0600) BOOL SetBandWidth(int nBand, int cxWidth) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, RB_SETBANDWIDTH, nBand, cxWidth); } #endif // (_WIN32_WINNT >= 0x0600) }; typedef CReBarCtrlT CReBarCtrl; /////////////////////////////////////////////////////////////////////////////// // CComboBoxEx #ifndef _WIN32_WCE template class CComboBoxExT : public CComboBoxT< TBase > { public: // Constructors CComboBoxExT(HWND hWnd = NULL) : CComboBoxT< TBase >(hWnd) { } CComboBoxExT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return WC_COMBOBOXEX; } CImageList GetImageList() const { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, CBEM_GETIMAGELIST, 0, 0L)); } CImageList SetImageList(HIMAGELIST hImageList) { ATLASSERT(::IsWindow(m_hWnd)); return CImageList((HIMAGELIST)::SendMessage(m_hWnd, CBEM_SETIMAGELIST, 0, (LPARAM)hImageList)); } #if (_WIN32_IE >= 0x0400) DWORD GetExtendedStyle() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, CBEM_GETEXTENDEDSTYLE, 0, 0L); } DWORD SetExtendedStyle(DWORD dwExMask, DWORD dwExStyle) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, CBEM_SETEXTENDEDSTYLE, dwExMask, dwExStyle); } BOOL GetUnicodeFormat() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, CBEM_GETUNICODEFORMAT, 0, 0L); } BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, CBEM_SETUNICODEFORMAT, bUnicode, 0L); } #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_WINNT >= 0x0501) void SetWindowTheme(LPCWSTR lpstrTheme) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, CBEM_SETWINDOWTHEME, 0, (LPARAM)lpstrTheme); } #endif // (_WIN32_WINNT >= 0x0501) // Operations int InsertItem(const COMBOBOXEXITEM* lpcCBItem) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CBEM_INSERTITEM, 0, (LPARAM)lpcCBItem); } int InsertItem(UINT nMask, int nIndex, LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, int iOverlay, LPARAM lParam) { ATLASSERT(::IsWindow(m_hWnd)); COMBOBOXEXITEM cbex = { 0 }; cbex.mask = nMask; cbex.iItem = nIndex; cbex.pszText = (LPTSTR) lpszItem; cbex.iImage = nImage; cbex.iSelectedImage = nSelImage; cbex.iIndent = iIndent; cbex.iOverlay = iOverlay; cbex.lParam = lParam; return (int)::SendMessage(m_hWnd, CBEM_INSERTITEM, 0, (LPARAM)&cbex); } int InsertItem(int nIndex, LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, LPARAM lParam = 0) { ATLASSERT(::IsWindow(m_hWnd)); COMBOBOXEXITEM cbex = { 0 }; cbex.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_INDENT | CBEIF_LPARAM; cbex.iItem = nIndex; cbex.pszText = (LPTSTR) lpszItem; cbex.iImage = nImage; cbex.iSelectedImage = nSelImage; cbex.iIndent = iIndent; cbex.lParam = lParam; return (int)::SendMessage(m_hWnd, CBEM_INSERTITEM, 0, (LPARAM)&cbex); } int AddItem(UINT nMask, LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, int iOverlay, LPARAM lParam) { return InsertItem(nMask, -1, lpszItem, nImage, nSelImage, iIndent, iOverlay, lParam); } int AddItem(LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, LPARAM lParam = 0) { return InsertItem(-1, lpszItem, nImage, nSelImage, iIndent, lParam); } int DeleteItem(int nIndex) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, CBEM_DELETEITEM, nIndex, 0L); } BOOL GetItem(PCOMBOBOXEXITEM pCBItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, CBEM_GETITEM, 0, (LPARAM)pCBItem); } BOOL SetItem(const COMBOBOXEXITEM* lpcCBItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, CBEM_SETITEM, 0, (LPARAM)lpcCBItem); } int SetItem(int nIndex, UINT nMask, LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, int iOverlay, LPARAM lParam) { ATLASSERT(::IsWindow(m_hWnd)); COMBOBOXEXITEM cbex = { 0 }; cbex.mask = nMask; cbex.iItem = nIndex; cbex.pszText = (LPTSTR) lpszItem; cbex.iImage = nImage; cbex.iSelectedImage = nSelImage; cbex.iIndent = iIndent; cbex.iOverlay = iOverlay; cbex.lParam = lParam; return (int)::SendMessage(m_hWnd, CBEM_SETITEM, 0, (LPARAM)&cbex); } BOOL GetItemText(int nIndex, LPTSTR lpszItem, int nLen) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(lpszItem != NULL); COMBOBOXEXITEM cbex = { 0 }; cbex.mask = CBEIF_TEXT; cbex.iItem = nIndex; cbex.pszText = lpszItem; cbex.cchTextMax = nLen; return (BOOL)::SendMessage(m_hWnd, CBEM_GETITEM, 0, (LPARAM)&cbex); } #ifndef _ATL_NO_COM BOOL GetItemText(int nIndex, BSTR& bstrText) const { USES_CONVERSION; ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(bstrText == NULL); COMBOBOXEXITEM cbex = { 0 }; cbex.mask = CBEIF_TEXT; cbex.iItem = nIndex; LPTSTR lpstrText = NULL; BOOL bRet = FALSE; for(int nLen = 256; ; nLen *= 2) { ATLTRY(lpstrText = new TCHAR[nLen]); if(lpstrText == NULL) break; lpstrText[0] = NULL; cbex.pszText = lpstrText; cbex.cchTextMax = nLen; bRet = (BOOL)::SendMessage(m_hWnd, CBEM_GETITEM, 0, (LPARAM)&cbex); if(!bRet || (lstrlen(cbex.pszText) < nLen - 1)) break; delete [] lpstrText; lpstrText = NULL; } if(lpstrText != NULL) { if(bRet) bstrText = ::SysAllocString(T2OLE(lpstrText)); delete [] lpstrText; } return (bstrText != NULL) ? TRUE : FALSE; } #endif // !_ATL_NO_COM #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) BOOL GetItemText(int nIndex, _CSTRING_NS::CString& strText) const { ATLASSERT(::IsWindow(m_hWnd)); COMBOBOXEXITEM cbex = { 0 }; cbex.mask = CBEIF_TEXT; cbex.iItem = nIndex; strText.Empty(); BOOL bRet = FALSE; for(int nLen = 256; ; nLen *= 2) { cbex.pszText = strText.GetBufferSetLength(nLen); if(cbex.pszText == NULL) { bRet = FALSE; break; } cbex.cchTextMax = nLen; bRet = (BOOL)::SendMessage(m_hWnd, CBEM_GETITEM, 0, (LPARAM)&cbex); if(!bRet || (lstrlen(cbex.pszText) < nLen - 1)) break; } strText.ReleaseBuffer(); return bRet; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) BOOL SetItemText(int nIndex, LPCTSTR lpszItem) { ATLASSERT(::IsWindow(m_hWnd)); return SetItem(nIndex, CBEIF_TEXT, lpszItem, 0, 0, 0, 0, 0); } CComboBox GetComboCtrl() const { ATLASSERT(::IsWindow(m_hWnd)); return CComboBox((HWND)::SendMessage(m_hWnd, CBEM_GETCOMBOCONTROL, 0, 0L)); } CEdit GetEditCtrl() const { ATLASSERT(::IsWindow(m_hWnd)); return CEdit((HWND)::SendMessage(m_hWnd, CBEM_GETEDITCONTROL, 0, 0L)); } BOOL HasEditChanged() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, CBEM_HASEDITCHANGED, 0, 0L); } // Non-functional int AddString(LPCTSTR /*lpszItem*/) { ATLASSERT(FALSE); // Not available in CComboBoxEx; use InsertItem return 0; } int InsertString(int /*nIndex*/, LPCTSTR /*lpszString*/) { ATLASSERT(FALSE); // Not available in CComboBoxEx; use InsertItem return 0; } int Dir(UINT /*attr*/, LPCTSTR /*lpszWildCard*/) { ATLASSERT(FALSE); // Not available in CComboBoxEx return 0; } int FindString(int /*nStartAfter*/, LPCTSTR /*lpszString*/) const { ATLASSERT(FALSE); // Not available in CComboBoxEx; try FindStringExact return 0; } }; typedef CComboBoxExT CComboBoxEx; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CMonthCalendarCtrl template class CMonthCalendarCtrlT : public TBase { public: // Constructors CMonthCalendarCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CMonthCalendarCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return MONTHCAL_CLASS; } COLORREF GetColor(int nColorType) const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, MCM_GETCOLOR, nColorType, 0L); } COLORREF SetColor(int nColorType, COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, MCM_SETCOLOR, nColorType, clr); } BOOL GetCurSel(LPSYSTEMTIME lpSysTime) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, MCM_GETCURSEL, 0, (LPARAM)lpSysTime); } BOOL SetCurSel(LPSYSTEMTIME lpSysTime) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, MCM_SETCURSEL, 0, (LPARAM)lpSysTime); } int GetFirstDayOfWeek(BOOL* pbLocaleVal = NULL) const { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, MCM_GETFIRSTDAYOFWEEK, 0, 0L); if(pbLocaleVal != NULL) *pbLocaleVal = (BOOL)HIWORD(dwRet); return (int)(short)LOWORD(dwRet); } int SetFirstDayOfWeek(int nDay, BOOL* pbLocaleVal = NULL) { ATLASSERT(::IsWindow(m_hWnd)); DWORD dwRet = (DWORD)::SendMessage(m_hWnd, MCM_SETFIRSTDAYOFWEEK, 0, nDay); if(pbLocaleVal != NULL) *pbLocaleVal = (BOOL)HIWORD(dwRet); return (int)(short)LOWORD(dwRet); } int GetMaxSelCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, MCM_GETMAXSELCOUNT, 0, 0L); } BOOL SetMaxSelCount(int nMax) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, MCM_SETMAXSELCOUNT, nMax, 0L); } int GetMonthDelta() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, MCM_GETMONTHDELTA, 0, 0L); } int SetMonthDelta(int nDelta) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, MCM_SETMONTHDELTA, nDelta, 0L); } DWORD GetRange(LPSYSTEMTIME lprgSysTimeArray) const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, MCM_GETRANGE, 0, (LPARAM)lprgSysTimeArray); } BOOL SetRange(DWORD dwFlags, LPSYSTEMTIME lprgSysTimeArray) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, MCM_SETRANGE, dwFlags, (LPARAM)lprgSysTimeArray); } BOOL GetSelRange(LPSYSTEMTIME lprgSysTimeArray) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, MCM_GETSELRANGE, 0, (LPARAM)lprgSysTimeArray); } BOOL SetSelRange(LPSYSTEMTIME lprgSysTimeArray) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, MCM_SETSELRANGE, 0, (LPARAM)lprgSysTimeArray); } BOOL GetToday(LPSYSTEMTIME lpSysTime) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, MCM_GETTODAY, 0, (LPARAM)lpSysTime); } void SetToday(LPSYSTEMTIME lpSysTime) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, MCM_SETTODAY, 0, (LPARAM)lpSysTime); } BOOL GetMinReqRect(LPRECT lpRectInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, MCM_GETMINREQRECT, 0, (LPARAM)lpRectInfo); } int GetMaxTodayWidth() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, MCM_GETMAXTODAYWIDTH, 0, 0L); } #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) BOOL GetUnicodeFormat() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, MCM_GETUNICODEFORMAT, 0, 0L); } BOOL SetUnicodeFormat(BOOL bUnicode = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, MCM_SETUNICODEFORMAT, bUnicode, 0L); } #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) #if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) DWORD GetCurrentView() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, MCM_GETCURRENTVIEW, 0, 0L); } BOOL SetCurrentView(DWORD dwView) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, MCM_SETCURRENTVIEW, 0, dwView); } DWORD GetCalendarCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, MCM_GETCALENDARCOUNT, 0, 0L); } BOOL GetCalendarGridInfo(PMCGRIDINFO pGridInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, MCM_GETCALENDARGRIDINFO, 0, (LPARAM)pGridInfo); } CALID GetCALID() const { ATLASSERT(::IsWindow(m_hWnd)); return (CALID)::SendMessage(m_hWnd, MCM_GETCALID, 0, 0L); } void SetCALID(CALID calid) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, MCM_SETCALID, (LPARAM)calid, 0L); } int GetCalendarBorder() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, MCM_GETCALENDARBORDER, 0, 0L); } void SetCalendarBorder(int cxyBorder, BOOL bSet = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, MCM_SETCALENDARBORDER, (WPARAM)bSet, (LPARAM)cxyBorder); } #endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) // Operations int GetMonthRange(DWORD dwFlags, LPSYSTEMTIME lprgSysTimeArray) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, MCM_GETMONTHRANGE, dwFlags, (LPARAM)lprgSysTimeArray); } BOOL SetDayState(int nMonths, LPMONTHDAYSTATE lpDayStateArray) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, MCM_SETDAYSTATE, nMonths, (LPARAM)lpDayStateArray); } DWORD HitTest(PMCHITTESTINFO pMCHitTest) const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, MCM_HITTEST, 0, (LPARAM)pMCHitTest); } #if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) void SizeRectToMin(LPRECT lpRect) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, MCM_SIZERECTTOMIN, 0, (LPARAM)lpRect); } #endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) }; typedef CMonthCalendarCtrlT CMonthCalendarCtrl; /////////////////////////////////////////////////////////////////////////////// // CDateTimePickerCtrl template class CDateTimePickerCtrlT : public TBase { public: // Constructors CDateTimePickerCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CDateTimePickerCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Operations static LPCTSTR GetWndClassName() { return DATETIMEPICK_CLASS; } BOOL SetFormat(LPCTSTR lpszFormat) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, DTM_SETFORMAT, 0, (LPARAM)lpszFormat); } COLORREF GetMonthCalColor(int nColorType) const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, DTM_GETMCCOLOR, nColorType, 0L); } COLORREF SetMonthCalColor(int nColorType, COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, DTM_SETMCCOLOR, nColorType, clr); } DWORD GetRange(LPSYSTEMTIME lpSysTimeArray) const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, DTM_GETRANGE, 0, (LPARAM)lpSysTimeArray); } BOOL SetRange(DWORD dwFlags, LPSYSTEMTIME lpSysTimeArray) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, DTM_SETRANGE, dwFlags, (LPARAM)lpSysTimeArray); } DWORD GetSystemTime(LPSYSTEMTIME lpSysTime) const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)lpSysTime); } BOOL SetSystemTime(DWORD dwFlags, LPSYSTEMTIME lpSysTime) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, DTM_SETSYSTEMTIME, dwFlags, (LPARAM)lpSysTime); } CMonthCalendarCtrl GetMonthCal() const { ATLASSERT(::IsWindow(m_hWnd)); return CMonthCalendarCtrl((HWND)::SendMessage(m_hWnd, DTM_GETMONTHCAL, 0, 0L)); } #if (_WIN32_IE >= 0x0400) CFontHandle GetMonthCalFont() const { ATLASSERT(::IsWindow(m_hWnd)); return CFontHandle((HFONT)::SendMessage(m_hWnd, DTM_GETMCFONT, 0, 0L)); } void SetMonthCalFont(HFONT hFont, BOOL bRedraw = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_SETMCFONT, (WPARAM)hFont, MAKELPARAM(bRedraw, 0)); } #endif // (_WIN32_IE >= 0x0400) #if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) DWORD GetMonthCalStyle() const { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, DTM_GETMCSTYLE, 0, 0L); } DWORD SetMonthCalStyle(DWORD dwStyle) { ATLASSERT(::IsWindow(m_hWnd)); return (DWORD)::SendMessage(m_hWnd, DTM_SETMCSTYLE, 0, (LPARAM)dwStyle); } void GetDateTimePickerInfo(LPDATETIMEPICKERINFO lpPickerInfo) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_GETDATETIMEPICKERINFO, 0, (LPARAM)lpPickerInfo); } BOOL GetIdealSize(LPSIZE lpSize) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, DTM_GETIDEALSIZE, 0, (LPARAM)lpSize); } void CloseMonthCal() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_CLOSEMONTHCAL, 0, 0L); } #endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) }; typedef CDateTimePickerCtrlT CDateTimePickerCtrl; /////////////////////////////////////////////////////////////////////////////// // CFlatScrollBarImpl - support for flat scroll bars #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) template class CFlatScrollBarImpl { public: // Initialization BOOL FlatSB_Initialize() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::InitializeFlatSB(pT->m_hWnd); } HRESULT FlatSB_Uninitialize() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::UninitializeFlatSB(pT->m_hWnd); } // Flat scroll bar properties BOOL FlatSB_GetScrollProp(UINT uIndex, LPINT lpnValue) const { const T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::FlatSB_GetScrollProp(pT->m_hWnd, uIndex, lpnValue); } BOOL FlatSB_SetScrollProp(UINT uIndex, int nValue, BOOL bRedraw = TRUE) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::FlatSB_SetScrollProp(pT->m_hWnd, uIndex, nValue, bRedraw); } // Attributes int FlatSB_GetScrollPos(int nBar) const { const T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::FlatSB_GetScrollPos(pT->m_hWnd, nBar); } int FlatSB_SetScrollPos(int nBar, int nPos, BOOL bRedraw = TRUE) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::FlatSB_SetScrollPos(pT->m_hWnd, nBar, nPos, bRedraw); } BOOL FlatSB_GetScrollRange(int nBar, LPINT lpMinPos, LPINT lpMaxPos) const { const T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::FlatSB_GetScrollRange(pT->m_hWnd, nBar, lpMinPos, lpMaxPos); } BOOL FlatSB_SetScrollRange(int nBar, int nMinPos, int nMaxPos, BOOL bRedraw = TRUE) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::FlatSB_SetScrollRange(pT->m_hWnd, nBar, nMinPos, nMaxPos, bRedraw); } BOOL FlatSB_GetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo) const { const T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::FlatSB_GetScrollInfo(pT->m_hWnd, nBar, lpScrollInfo); } int FlatSB_SetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::FlatSB_SetScrollInfo(pT->m_hWnd, nBar, lpScrollInfo, bRedraw); } // Operations BOOL FlatSB_ShowScrollBar(UINT nBar, BOOL bShow = TRUE) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::FlatSB_ShowScrollBar(pT->m_hWnd, nBar, bShow); } BOOL FlatSB_EnableScrollBar(UINT uSBFlags, UINT uArrowFlags = ESB_ENABLE_BOTH) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::FlatSB_EnableScrollBar(pT->m_hWnd, uSBFlags, uArrowFlags); } }; template class CFlatScrollBarT : public TBase, public CFlatScrollBarImpl > { public: CFlatScrollBarT(HWND hWnd = NULL) : TBase(hWnd) { } CFlatScrollBarT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } }; typedef CFlatScrollBarT CFlatScrollBar; #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) /////////////////////////////////////////////////////////////////////////////// // CIPAddressCtrl #if (_WIN32_IE >= 0x0400) template class CIPAddressCtrlT : public TBase { public: // Constructors CIPAddressCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CIPAddressCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Atteributes static LPCTSTR GetWndClassName() { return WC_IPADDRESS; } BOOL IsBlank() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, IPM_ISBLANK, 0, 0L); } int GetAddress(LPDWORD lpdwAddress) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, IPM_GETADDRESS, 0, (LPARAM)lpdwAddress); } void SetAddress(DWORD dwAddress) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, IPM_SETADDRESS, 0, dwAddress); } void ClearAddress() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, IPM_CLEARADDRESS, 0, 0L); } void SetRange(int nField, WORD wRange) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, IPM_SETRANGE, nField, wRange); } void SetRange(int nField, BYTE nMin, BYTE nMax) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, IPM_SETRANGE, nField, MAKEIPRANGE(nMin, nMax)); } void SetFocus(int nField) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, IPM_SETFOCUS, nField, 0L); } }; typedef CIPAddressCtrlT CIPAddressCtrl; #endif // (_WIN32_IE >= 0x0400) /////////////////////////////////////////////////////////////////////////////// // CPagerCtrl #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) template class CPagerCtrlT : public TBase { public: // Constructors CPagerCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CPagerCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { return WC_PAGESCROLLER; } int GetButtonSize() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PGM_GETBUTTONSIZE, 0, 0L); } int SetButtonSize(int nButtonSize) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PGM_SETBUTTONSIZE, 0, nButtonSize); } DWORD GetButtonState(int nButton) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nButton == PGB_TOPORLEFT || nButton == PGB_BOTTOMORRIGHT); return (DWORD)::SendMessage(m_hWnd, PGM_GETBUTTONSTATE, 0, nButton); } COLORREF GetBkColor() const { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, PGM_GETBKCOLOR, 0, 0L); } COLORREF SetBkColor(COLORREF clrBk) { ATLASSERT(::IsWindow(m_hWnd)); return (COLORREF)::SendMessage(m_hWnd, PGM_SETBKCOLOR, 0, (LPARAM)clrBk); } int GetBorder() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PGM_GETBORDER, 0, 0L); } int SetBorder(int nBorderSize) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PGM_SETBORDER, 0, nBorderSize); } int GetPos() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PGM_GETPOS, 0, 0L); } int SetPos(int nPos) { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PGM_SETPOS, 0, nPos); } // Operations void SetChild(HWND hWndChild) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, PGM_SETCHILD, 0, (LPARAM)hWndChild); } void ForwardMouse(BOOL bForward = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, PGM_FORWARDMOUSE, bForward, 0L); } void RecalcSize() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, PGM_RECALCSIZE, 0, 0L); } void GetDropTarget(IDropTarget** ppDropTarget) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(ppDropTarget != NULL); ::SendMessage(m_hWnd, PGM_GETDROPTARGET, 0, (LPARAM)ppDropTarget); } }; typedef CPagerCtrlT CPagerCtrl; #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) /////////////////////////////////////////////////////////////////////////////// // CLinkCtrl - Windows SYSLINK control #if (_WIN32_WINNT >= 0x0501) && !defined(_WIN32_WCE) template class CLinkCtrlT : public TBase { public: // Constructors CLinkCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CLinkCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); } // Attributes static LPCTSTR GetWndClassName() { #ifdef _UNICODE return WC_LINK; #else // !_UNICODE return "SysLink"; #endif // !_UNICODE } int GetIdealHeight(int cxMaxWidth = 0) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LM_GETIDEALHEIGHT, cxMaxWidth, 0L); } BOOL GetItem(PLITEM pLItem) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LM_GETITEM, 0, (LPARAM)pLItem); } BOOL SetItem(PLITEM pLItem) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LM_SETITEM, 0, (LPARAM)pLItem); } // Vista only int GetIdealSize(SIZE& size, int cxMaxWidth = 0) const { #ifndef LM_GETIDEALSIZE const UINT LM_GETIDEALSIZE = LM_GETIDEALHEIGHT; #endif ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, LM_GETIDEALSIZE, cxMaxWidth, (LPARAM)&size); } // Operations BOOL HitTest(PLHITTESTINFO pLHitTestInfo) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, LM_HITTEST, 0, (LPARAM)pLHitTestInfo); } }; typedef CLinkCtrlT CLinkCtrl; #endif // (_WIN32_WINNT >= 0x0501) && !defined(_WIN32_WCE) /////////////////////////////////////////////////////////////////////////////// // CCustomDraw - MI class for custom-draw support template class CCustomDraw { public: #if (_ATL_VER < 0x0700) BOOL m_bHandledCD; BOOL IsMsgHandled() const { return m_bHandledCD; } void SetMsgHandled(BOOL bHandled) { m_bHandledCD = bHandled; } #endif // !(_ATL_VER < 0x0700) // Message map and handlers BEGIN_MSG_MAP(CCustomDraw< T >) NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw) ALT_MSG_MAP(1) REFLECTED_NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw) END_MSG_MAP() // message handler LRESULT OnCustomDraw(int idCtrl, LPNMHDR pnmh, BOOL& bHandled) { T* pT = static_cast(this); pT->SetMsgHandled(TRUE); LPNMCUSTOMDRAW lpNMCustomDraw = (LPNMCUSTOMDRAW)pnmh; DWORD dwRet = 0; switch(lpNMCustomDraw->dwDrawStage) { case CDDS_PREPAINT: dwRet = pT->OnPrePaint(idCtrl, lpNMCustomDraw); break; case CDDS_POSTPAINT: dwRet = pT->OnPostPaint(idCtrl, lpNMCustomDraw); break; case CDDS_PREERASE: dwRet = pT->OnPreErase(idCtrl, lpNMCustomDraw); break; case CDDS_POSTERASE: dwRet = pT->OnPostErase(idCtrl, lpNMCustomDraw); break; case CDDS_ITEMPREPAINT: dwRet = pT->OnItemPrePaint(idCtrl, lpNMCustomDraw); break; case CDDS_ITEMPOSTPAINT: dwRet = pT->OnItemPostPaint(idCtrl, lpNMCustomDraw); break; case CDDS_ITEMPREERASE: dwRet = pT->OnItemPreErase(idCtrl, lpNMCustomDraw); break; case CDDS_ITEMPOSTERASE: dwRet = pT->OnItemPostErase(idCtrl, lpNMCustomDraw); break; #if (_WIN32_IE >= 0x0400) case (CDDS_ITEMPREPAINT | CDDS_SUBITEM): dwRet = pT->OnSubItemPrePaint(idCtrl, lpNMCustomDraw); break; #endif // (_WIN32_IE >= 0x0400) default: pT->SetMsgHandled(FALSE); break; } bHandled = pT->IsMsgHandled(); return dwRet; } // Overrideables DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) { return CDRF_DODEFAULT; } DWORD OnPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) { return CDRF_DODEFAULT; } DWORD OnPreErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) { return CDRF_DODEFAULT; } DWORD OnPostErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) { return CDRF_DODEFAULT; } DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) { return CDRF_DODEFAULT; } DWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) { return CDRF_DODEFAULT; } DWORD OnItemPreErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) { return CDRF_DODEFAULT; } DWORD OnItemPostErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) { return CDRF_DODEFAULT; } #if (_WIN32_IE >= 0x0400) DWORD OnSubItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) { return CDRF_DODEFAULT; } #endif // (_WIN32_IE >= 0x0400) }; // --- Windows CE common controls --- #ifdef _WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CCECommandBarCtrl template class CCECommandBarCtrlT : public TBase { public: // Constructors CCECommandBarCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CCECommandBarCtrlT< TBase >& operator=(HWND hWnd) { m_hWnd = hWnd; return *this; } // Attributes BOOL IsVisible() const { return IsWindowVisible(); } int GetHeight() const { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBar_Height(m_hWnd); } HMENU GetMenu(WORD wButton) const { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBar_GetMenu(m_hWnd, wButton); } // Operations HWND Create(HWND hWndParent, int nCmdBarID) { m_hWnd = ::CommandBar_Create(ModuleHelper::GetModuleInstance(), hWndParent, nCmdBarID); ATLASSERT(::IsWindow(m_hWnd)); return m_hWnd; } void Destroy() { DestroyWindow(); } BOOL Show(BOOL bShow = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBar_Show(m_hWnd, bShow); } BOOL DrawMenuBar(WORD wButton) { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBar_DrawMenuBar(m_hWnd, wButton); } BOOL AddAdornments(DWORD dwFlags = 0) { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBar_AddAdornments(m_hWnd, dwFlags, 0); } int AddBitmap(int nBitmapID, int nNumImages) { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBar_AddBitmap(m_hWnd, ModuleHelper::GetResourceInstance(), nBitmapID, nNumImages, 16, 16); } BOOL AddButtons(UINT uNumButtons, LPTBBUTTON lpButtons) { ATLASSERT(::IsWindow(m_hWnd)); return CommandBar_AddButtons(m_hWnd, uNumButtons, lpButtons); } BOOL AddToolTips(UINT uNumToolTips, LPTSTR lpToolTips) { ATLASSERT(::IsWindow(m_hWnd)); return CommandBar_AddToolTips(m_hWnd, uNumToolTips, lpToolTips); } BOOL InsertButton(int nButton, LPTBBUTTON lpButton) { ATLASSERT(::IsWindow(m_hWnd)); return CommandBar_InsertButton(m_hWnd, nButton, lpButton); } HWND InsertComboBox(int nWidth, UINT dwStyle, WORD wComboBoxID, WORD wButton) { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBar_InsertComboBox(m_hWnd, ModuleHelper::GetModuleInstance(), nWidth, dwStyle, wComboBoxID, wButton); } BOOL InsertMenubar(WORD wMenuID, WORD wButton) { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBar_InsertMenubar(m_hWnd, ModuleHelper::GetResourceInstance(), wMenuID, wButton); } BOOL InsertMenubarEx(ATL::_U_STRINGorID menu, WORD wButton) { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBar_InsertMenubarEx(m_hWnd, ModuleHelper::GetResourceInstance(), (LPTSTR)menu.m_lpstr, wButton); } BOOL IsCommandBarMessage(LPMSG lpMsg) { ATLASSERT(::IsWindow(m_hWnd)); return ::IsCommandBarMessage(m_hWnd, lpMsg); } }; #if defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) // PPC MenuBar typedef CCECommandBarCtrlT CMenuBarCtrl; #else typedef CCECommandBarCtrlT CCECommandBarCtrl; #endif // defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) /////////////////////////////////////////////////////////////////////////////// // CCECommandBandsCtrl template class CCECommandBandsCtrlT : public TBase { public: // Constructors CCECommandBandsCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CCECommandBandsCtrlT< TBase >& operator=(HWND hWnd) { m_hWnd = hWnd; return *this; } // Attributes BOOL IsVisible() const { return IsWindowVisible(); } #if (_WIN32_IE >= 0x0400) UINT GetHeight() const { ATLASSERT(::IsWindow(m_hWnd)); return CommandBands_Height(m_hWnd); } #endif // (_WIN32_IE >= 0x0400) HWND GetCommandBar(UINT uBand) const { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBands_GetCommandBar(m_hWnd, uBand); } BOOL GetRestoreInformation(UINT uBand, LPCOMMANDBANDSRESTOREINFO pcbr) const { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBands_GetRestoreInformation(m_hWnd, uBand, pcbr); } // Operations HWND Create(HWND hWndParent, UINT wID, DWORD dwStyles, HIMAGELIST hImageList = NULL) { m_hWnd = ::CommandBands_Create(ModuleHelper::GetModuleInstance(), hWndParent, wID, dwStyles, hImageList); ATLASSERT(::IsWindow(m_hWnd)); return m_hWnd; } BOOL AddAdornments(DWORD dwFlags = 0, LPREBARBANDINFO prbbi = NULL) { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBands_AddAdornments(m_hWnd, ModuleHelper::GetModuleInstance(), dwFlags, prbbi); } BOOL AddBands(UINT uBandCount, LPREBARBANDINFO prbbi) { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBands_AddBands(m_hWnd, ModuleHelper::GetModuleInstance(), uBandCount, prbbi); } BOOL Show(BOOL bShow = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return ::CommandBands_Show(m_hWnd, bShow); } }; typedef CCECommandBandsCtrlT CCECommandBandsCtrl; #endif // _WIN32_WCE }; // namespace WTL #endif // __ATLCTRLS_H__ ================================================ FILE: src/Setup/wtl90/atlctrlw.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLCTRLW_H__ #define __ATLCTRLW_H__ #pragma once #ifdef _WIN32_WCE #error atlctrlw.h is not supported on Windows CE #endif #ifndef __ATLAPP_H__ #error atlctrlw.h requires atlapp.h to be included first #endif #ifndef __ATLCTRLS_H__ #error atlctrlw.h requires atlctrls.h to be included first #endif #if (_WIN32_IE < 0x0400) #error atlctrlw.h requires _WIN32_IE >= 0x0400 #endif // Define _WTL_CMDBAR_VISTA_MENUS as 0 to exclude Vista menus support #if !defined(_WTL_CMDBAR_VISTA_MENUS) && (WINVER >= 0x0500) && (_WIN32_WINNT >= 0x0501) && (_WIN32_IE >= 0x0501) #define _WTL_CMDBAR_VISTA_MENUS 1 #endif #if _WTL_CMDBAR_VISTA_MENUS #if !((_WIN32_WINNT >= 0x0501) && (_WIN32_IE >= 0x0501)) #error _WTL_CMDBAR_VISTA_MENUS requires (_WIN32_WINNT >= 0x0501) && (_WIN32_IE >= 0x0501) #endif #endif // Note: Define _WTL_CMDBAR_VISTA_STD_MENUBAR to use Vista standard menubar look with Vista menus /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CCommandBarCtrlImpl // CCommandBarCtrl // CMDICommandBarCtrlImpl // CMDICommandBarCtrl namespace WTL { /////////////////////////////////////////////////////////////////////////////// // Command Bars // Window Styles: #define CBRWS_TOP CCS_TOP #define CBRWS_BOTTOM CCS_BOTTOM #define CBRWS_NORESIZE CCS_NORESIZE #define CBRWS_NOPARENTALIGN CCS_NOPARENTALIGN #define CBRWS_NODIVIDER CCS_NODIVIDER // Extended styles #define CBR_EX_TRANSPARENT 0x00000001L #define CBR_EX_SHAREMENU 0x00000002L #define CBR_EX_ALTFOCUSMODE 0x00000004L #define CBR_EX_TRACKALWAYS 0x00000008L #define CBR_EX_NOVISTAMENUS 0x00000010L // standard command bar styles #define ATL_SIMPLE_CMDBAR_PANE_STYLE \ (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CBRWS_NODIVIDER | CBRWS_NORESIZE | CBRWS_NOPARENTALIGN) // Messages - support chevrons for frame windows #define CBRM_GETCMDBAR (WM_USER + 301) // returns command bar HWND #define CBRM_GETMENU (WM_USER + 302) // returns loaded or attached menu #define CBRM_TRACKPOPUPMENU (WM_USER + 303) // displays a popup menu typedef struct tagCBRPOPUPMENU { int cbSize; HMENU hMenu; // popup menu do display UINT uFlags; // TPM_* flags for ::TrackPopupMenuEx int x; int y; LPTPMPARAMS lptpm; // ptr to TPMPARAMS for ::TrackPopupMenuEx } CBRPOPUPMENU, *LPCBRPOPUPMENU; // helper class template class CSimpleStack : public ATL::CSimpleArray< T > { public: BOOL Push(T t) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - STACK-PUSH (%8.8X) size = %i\n"), t, GetSize()); #endif return Add(t); } T Pop() { int nLast = GetSize() - 1; if(nLast < 0) return NULL; // must be able to convert to NULL T t = m_aT[nLast]; #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - STACK-POP (%8.8X) size = %i\n"), t, GetSize()); #endif if(!RemoveAt(nLast)) return NULL; return t; } T GetCurrent() { int nLast = GetSize() - 1; if(nLast < 0) return NULL; // must be able to convert to NULL return m_aT[nLast]; } }; /////////////////////////////////////////////////////////////////////////////// // CCommandBarCtrlBase - base class for the Command Bar implementation class CCommandBarCtrlBase : public CToolBarCtrl { public: struct _MsgHookData { HHOOK hMsgHook; DWORD dwUsage; _MsgHookData() : hMsgHook(NULL), dwUsage(0) { } }; typedef ATL::CSimpleMap CMsgHookMap; static CMsgHookMap* s_pmapMsgHook; static HHOOK s_hCreateHook; static bool s_bW2K; // For animation flag static CCommandBarCtrlBase* s_pCurrentBar; static bool s_bStaticInit; CSimpleStack m_stackMenuWnd; CSimpleStack m_stackMenuHandle; HWND m_hWndHook; DWORD m_dwMagic; CCommandBarCtrlBase() : m_hWndHook(NULL), m_dwMagic(1314) { // init static variables if(!s_bStaticInit) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CCommandBarCtrlBase::CCommandBarCtrlBase.\n")); ATLASSERT(FALSE); return; } if(!s_bStaticInit) { // Just in case... AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES); // Animation on Win2000 only s_bW2K = !AtlIsOldWindows(); // done s_bStaticInit = true; } lock.Unlock(); } } bool IsCommandBarBase() const { return m_dwMagic == 1314; } }; __declspec(selectany) CCommandBarCtrlBase::CMsgHookMap* CCommandBarCtrlBase::s_pmapMsgHook = NULL; __declspec(selectany) HHOOK CCommandBarCtrlBase::s_hCreateHook = NULL; __declspec(selectany) CCommandBarCtrlBase* CCommandBarCtrlBase::s_pCurrentBar = NULL; __declspec(selectany) bool CCommandBarCtrlBase::s_bW2K = false; __declspec(selectany) bool CCommandBarCtrlBase::s_bStaticInit = false; /////////////////////////////////////////////////////////////////////////////// // CCommandBarCtrl - ATL implementation of Command Bars template class ATL_NO_VTABLE CCommandBarCtrlImpl : public ATL::CWindowImpl< T, TBase, TWinTraits > { public: DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName()) // Declarations struct _MenuItemData // menu item data { DWORD dwMagic; LPTSTR lpstrText; UINT fType; UINT fState; int iButton; _MenuItemData() { dwMagic = 0x1313; } bool IsCmdBarMenuItem() { return (dwMagic == 0x1313); } }; struct _ToolBarData // toolbar resource data { WORD wVersion; WORD wWidth; WORD wHeight; WORD wItemCount; //WORD aItems[wItemCount] WORD* items() { return (WORD*)(this+1); } }; // Constants enum _CmdBarDrawConstants { s_kcxGap = 1, s_kcxTextMargin = 2, s_kcxButtonMargin = 3, s_kcyButtonMargin = 3 }; enum { _nMaxMenuItemTextLength = 100, _chChevronShortcut = _T('/') }; #ifndef DT_HIDEPREFIX enum { DT_HIDEPREFIX = 0x00100000 }; #endif // !DT_HIDEPREFIX // Data members HMENU m_hMenu; HIMAGELIST m_hImageList; ATL::CSimpleValArray m_arrCommand; DWORD m_dwExtendedStyle; // Command Bar specific extended styles ATL::CContainedWindow m_wndParent; bool m_bMenuActive:1; bool m_bAttachedMenu:1; bool m_bImagesVisible:1; bool m_bPopupItem:1; bool m_bContextMenu:1; bool m_bEscapePressed:1; bool m_bSkipMsg:1; bool m_bParentActive:1; bool m_bFlatMenus:1; bool m_bUseKeyboardCues:1; bool m_bShowKeyboardCues:1; bool m_bAllowKeyboardCues:1; bool m_bKeyboardInput:1; bool m_bAlphaImages:1; bool m_bLayoutRTL:1; bool m_bSkipPostDown:1; bool m_bVistaMenus:1; int m_nPopBtn; int m_nNextPopBtn; SIZE m_szBitmap; SIZE m_szButton; COLORREF m_clrMask; CFont m_fontMenu; // used internally, only to measure text UINT m_uSysKey; HWND m_hWndFocus; // Alternate focus mode int m_cxExtraSpacing; #if _WTL_CMDBAR_VISTA_MENUS ATL::CSimpleValArray m_arrVistaBitmap; // Bitmaps for Vista menus #endif // _WTL_CMDBAR_VISTA_MENUS // Constructor/destructor CCommandBarCtrlImpl() : m_hMenu(NULL), m_hImageList(NULL), m_wndParent(this, 1), m_bMenuActive(false), m_bAttachedMenu(false), m_nPopBtn(-1), m_nNextPopBtn(-1), m_bPopupItem(false), m_bImagesVisible(true), m_bSkipMsg(false), m_uSysKey(0), m_hWndFocus(NULL), m_bContextMenu(false), m_bEscapePressed(false), m_clrMask(RGB(192, 192, 192)), m_dwExtendedStyle(CBR_EX_TRANSPARENT | CBR_EX_SHAREMENU | CBR_EX_TRACKALWAYS), m_bParentActive(true), m_bFlatMenus(false), m_bUseKeyboardCues(false), m_bShowKeyboardCues(false), m_bAllowKeyboardCues(true), m_bKeyboardInput(false), m_cxExtraSpacing(0), m_bAlphaImages(false), m_bLayoutRTL(false), m_bSkipPostDown(false), m_bVistaMenus(false) { SetImageSize(16, 15); // default } ~CCommandBarCtrlImpl() { if(m_wndParent.IsWindow()) /*scary!*/ m_wndParent.UnsubclassWindow(); if(m_hMenu != NULL && (m_dwExtendedStyle & CBR_EX_SHAREMENU) == 0) ::DestroyMenu(m_hMenu); if(m_hImageList != NULL) ::ImageList_Destroy(m_hImageList); } // Attributes DWORD GetCommandBarExtendedStyle() const { return m_dwExtendedStyle; } DWORD SetCommandBarExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0) { DWORD dwPrevStyle = m_dwExtendedStyle; if(dwMask == 0) m_dwExtendedStyle = dwExtendedStyle; else m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask); return dwPrevStyle; } CMenuHandle GetMenu() const { ATLASSERT(::IsWindow(m_hWnd)); return m_hMenu; } COLORREF GetImageMaskColor() const { return m_clrMask; } COLORREF SetImageMaskColor(COLORREF clrMask) { COLORREF clrOld = m_clrMask; m_clrMask = clrMask; return clrOld; } bool GetImagesVisible() const { return m_bImagesVisible; } bool SetImagesVisible(bool bVisible) { bool bOld = m_bImagesVisible; m_bImagesVisible = bVisible; return bOld; } void GetImageSize(SIZE& size) const { size = m_szBitmap; } bool SetImageSize(SIZE& size) { return SetImageSize(size.cx, size.cy); } bool SetImageSize(int cx, int cy) { if(m_hImageList != NULL) { if(::ImageList_GetImageCount(m_hImageList) == 0) // empty { ::ImageList_Destroy(m_hImageList); m_hImageList = NULL; } else { return false; // can't set, image list exists } } if(cx == 0 || cy == 0) return false; m_szBitmap.cx = cx; m_szBitmap.cy = cy; m_szButton.cx = m_szBitmap.cx + 2 * s_kcxButtonMargin; m_szButton.cy = m_szBitmap.cy + 2 * s_kcyButtonMargin; return true; } bool GetAlphaImages() const { return m_bAlphaImages; } bool SetAlphaImages(bool bAlphaImages) { if(m_hImageList != NULL) { if(::ImageList_GetImageCount(m_hImageList) == 0) // empty { ::ImageList_Destroy(m_hImageList); m_hImageList = NULL; } else { return false; // can't set, image list exists } } m_bAlphaImages = bAlphaImages; return true; } HWND GetCmdBar() const { ATLASSERT(::IsWindow(m_hWnd)); return (HWND)::SendMessage(m_hWnd, CBRM_GETCMDBAR, 0, 0L); } // Methods HWND Create(HWND hWndParent, RECT& rcPos, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL) { // These styles are required for command bars dwStyle |= TBSTYLE_LIST | TBSTYLE_FLAT; #if (_MSC_VER >= 1300) return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcPos, szWindowName, dwStyle, dwExStyle, nID, lpCreateParam); #else // !(_MSC_VER >= 1300) typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass; return _baseClass::Create(hWndParent, rcPos, szWindowName, dwStyle, dwExStyle, nID, lpCreateParam); #endif // !(_MSC_VER >= 1300) } BOOL AttachToWindow(HWND hWnd) { ATLASSERT(m_hWnd == NULL); ATLASSERT(::IsWindow(hWnd)); BOOL bRet = SubclassWindow(hWnd); if(bRet) { m_bAttachedMenu = true; T* pT = static_cast(this); pT->GetSystemSettings(); } return bRet; } BOOL LoadMenu(ATL::_U_STRINGorID menu) { ATLASSERT(::IsWindow(m_hWnd)); if(m_bAttachedMenu) // doesn't work in this mode return FALSE; if(menu.m_lpstr == NULL) return FALSE; HMENU hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), menu.m_lpstr); if(hMenu == NULL) return FALSE; return AttachMenu(hMenu); } BOOL AttachMenu(HMENU hMenu) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hMenu == NULL || ::IsMenu(hMenu)); if(hMenu != NULL && !::IsMenu(hMenu)) return FALSE; #if _WTL_CMDBAR_VISTA_MENUS // remove Vista bitmaps if used if(m_bVistaMenus && (m_hMenu != NULL)) { T* pT = static_cast(this); pT->_RemoveVistaBitmapsFromMenu(); } #endif // _WTL_CMDBAR_VISTA_MENUS // destroy old menu, if needed, and set new one if(m_hMenu != NULL && (m_dwExtendedStyle & CBR_EX_SHAREMENU) == 0) ::DestroyMenu(m_hMenu); m_hMenu = hMenu; if(m_bAttachedMenu) // Nothing else in this mode return TRUE; // Build buttons according to menu SetRedraw(FALSE); // Clear all buttons int nCount = GetButtonCount(); for(int i = 0; i < nCount; i++) ATLVERIFY(DeleteButton(0) != FALSE); // Add buttons for each menu item if(m_hMenu != NULL) { int nItems = ::GetMenuItemCount(m_hMenu); T* pT = static_cast(this); pT; // avoid level 4 warning TCHAR szString[pT->_nMaxMenuItemTextLength] = { 0 }; for(int i = 0; i < nItems; i++) { CMenuItemInfo mii; mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_SUBMENU; mii.fType = MFT_STRING; mii.dwTypeData = szString; mii.cch = pT->_nMaxMenuItemTextLength; BOOL bRet = ::GetMenuItemInfo(m_hMenu, i, TRUE, &mii); ATLASSERT(bRet); // If we have more than the buffer, we assume we have bitmaps bits if(lstrlen(szString) > pT->_nMaxMenuItemTextLength - 1) { mii.fType = MFT_BITMAP; ::SetMenuItemInfo(m_hMenu, i, TRUE, &mii); szString[0] = 0; } // NOTE: Command Bar currently supports only drop-down menu items ATLASSERT(mii.hSubMenu != NULL); TBBUTTON btn = { 0 }; btn.iBitmap = 0; btn.idCommand = i; btn.fsState = (BYTE)(((mii.fState & MFS_DISABLED) == 0) ? TBSTATE_ENABLED : 0); btn.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_DROPDOWN; btn.dwData = 0; btn.iString = 0; bRet = InsertButton(-1, &btn); ATLASSERT(bRet); TBBUTTONINFO bi = { 0 }; bi.cbSize = sizeof(TBBUTTONINFO); bi.dwMask = TBIF_TEXT; bi.pszText = szString; bRet = SetButtonInfo(i, &bi); ATLASSERT(bRet); } } SetRedraw(TRUE); Invalidate(); UpdateWindow(); return TRUE; } BOOL LoadImages(ATL::_U_STRINGorID image) { return _LoadImagesHelper(image, false); } BOOL LoadMappedImages(UINT nIDImage, UINT nFlags = 0, LPCOLORMAP lpColorMap = NULL, int nMapSize = 0) { return _LoadImagesHelper(nIDImage, true, nFlags , lpColorMap, nMapSize); } BOOL _LoadImagesHelper(ATL::_U_STRINGorID image, bool bMapped, UINT nFlags = 0, LPCOLORMAP lpColorMap = NULL, int nMapSize = 0) { ATLASSERT(::IsWindow(m_hWnd)); HINSTANCE hInstance = ModuleHelper::GetResourceInstance(); HRSRC hRsrc = ::FindResource(hInstance, image.m_lpstr, (LPTSTR)RT_TOOLBAR); if(hRsrc == NULL) return FALSE; HGLOBAL hGlobal = ::LoadResource(hInstance, hRsrc); if(hGlobal == NULL) return FALSE; _ToolBarData* pData = (_ToolBarData*)::LockResource(hGlobal); if(pData == NULL) return FALSE; ATLASSERT(pData->wVersion == 1); WORD* pItems = pData->items(); int nItems = pData->wItemCount; // Set internal data SetImageSize(pData->wWidth, pData->wHeight); // Create image list if needed if(m_hImageList == NULL) { // Check if the bitmap is 32-bit (alpha channel) bitmap (valid for Windows XP only) T* pT = static_cast(this); m_bAlphaImages = AtlIsAlphaBitmapResource(image); if(!pT->CreateInternalImageList(pData->wItemCount)) return FALSE; } #if _WTL_CMDBAR_VISTA_MENUS int nOldImageCount = ::ImageList_GetImageCount(m_hImageList); #endif // _WTL_CMDBAR_VISTA_MENUS // Add bitmap to our image list CBitmap bmp; if(bMapped) { ATLASSERT(HIWORD(PtrToUlong(image.m_lpstr)) == 0); // if mapped, must be a numeric ID int nIDImage = (int)(short)LOWORD(PtrToUlong(image.m_lpstr)); bmp.LoadMappedBitmap(nIDImage, (WORD)nFlags, lpColorMap, nMapSize); } else { if(m_bAlphaImages) bmp = (HBITMAP)::LoadImage(ModuleHelper::GetResourceInstance(), image.m_lpstr, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE); else bmp.LoadBitmap(image.m_lpstr); } ATLASSERT(bmp.m_hBitmap != NULL); if(bmp.m_hBitmap == NULL) return FALSE; if(::ImageList_AddMasked(m_hImageList, bmp, m_clrMask) == -1) return FALSE; // Fill the array with command IDs for(int i = 0; i < nItems; i++) { if(pItems[i] != 0) m_arrCommand.Add(pItems[i]); } int nImageCount = ::ImageList_GetImageCount(m_hImageList); ATLASSERT(nImageCount == m_arrCommand.GetSize()); if(nImageCount != m_arrCommand.GetSize()) return FALSE; #if _WTL_CMDBAR_VISTA_MENUS if(RunTimeHelper::IsVista()) { T* pT = static_cast(this); pT->_AddVistaBitmapsFromImageList(nOldImageCount, nImageCount - nOldImageCount); ATLASSERT(nImageCount == m_arrVistaBitmap.GetSize()); } #endif // _WTL_CMDBAR_VISTA_MENUS return TRUE; } BOOL AddBitmap(ATL::_U_STRINGorID bitmap, int nCommandID) { ATLASSERT(::IsWindow(m_hWnd)); CBitmap bmp; bmp.LoadBitmap(bitmap.m_lpstr); if(bmp.m_hBitmap == NULL) return FALSE; return AddBitmap(bmp, nCommandID); } BOOL AddBitmap(HBITMAP hBitmap, UINT nCommandID) { ATLASSERT(::IsWindow(m_hWnd)); T* pT = static_cast(this); // Create image list if it doesn't exist if(m_hImageList == NULL) { if(!pT->CreateInternalImageList(1)) return FALSE; } // check bitmap size CBitmapHandle bmp = hBitmap; SIZE size = { 0, 0 }; bmp.GetSize(size); if(size.cx != m_szBitmap.cx || size.cy != m_szBitmap.cy) { ATLASSERT(FALSE); // must match size! return FALSE; } // add bitmap int nRet = ::ImageList_AddMasked(m_hImageList, hBitmap, m_clrMask); if(nRet == -1) return FALSE; BOOL bRet = m_arrCommand.Add((WORD)nCommandID); ATLASSERT(::ImageList_GetImageCount(m_hImageList) == m_arrCommand.GetSize()); #if _WTL_CMDBAR_VISTA_MENUS if(RunTimeHelper::IsVista()) { pT->_AddVistaBitmapFromImageList(m_arrCommand.GetSize() - 1); ATLASSERT(m_arrVistaBitmap.GetSize() == m_arrCommand.GetSize()); } #endif // _WTL_CMDBAR_VISTA_MENUS return bRet; } BOOL AddIcon(ATL::_U_STRINGorID icon, UINT nCommandID) { ATLASSERT(::IsWindow(m_hWnd)); HICON hIcon = ::LoadIcon(ModuleHelper::GetResourceInstance(), icon.m_lpstr); if(hIcon == NULL) return FALSE; return AddIcon(hIcon, nCommandID); } BOOL AddIcon(HICON hIcon, UINT nCommandID) { ATLASSERT(::IsWindow(m_hWnd)); T* pT = static_cast(this); // create image list if it doesn't exist if(m_hImageList == NULL) { if(!pT->CreateInternalImageList(1)) return FALSE; } int nRet = ::ImageList_AddIcon(m_hImageList, hIcon); if(nRet == -1) return FALSE; BOOL bRet = m_arrCommand.Add((WORD)nCommandID); ATLASSERT(::ImageList_GetImageCount(m_hImageList) == m_arrCommand.GetSize()); #if _WTL_CMDBAR_VISTA_MENUS if(RunTimeHelper::IsVista()) { pT->_AddVistaBitmapFromImageList(m_arrCommand.GetSize() - 1); ATLASSERT(m_arrVistaBitmap.GetSize() == m_arrCommand.GetSize()); } #endif // _WTL_CMDBAR_VISTA_MENUS return bRet; } BOOL ReplaceBitmap(ATL::_U_STRINGorID bitmap, int nCommandID) { ATLASSERT(::IsWindow(m_hWnd)); CBitmap bmp; bmp.LoadBitmap(bitmap.m_lpstr); if(bmp.m_hBitmap == NULL) return FALSE; return ReplaceBitmap(bmp, nCommandID); } BOOL ReplaceBitmap(HBITMAP hBitmap, UINT nCommandID) { ATLASSERT(::IsWindow(m_hWnd)); BOOL bRet = FALSE; for(int i = 0; i < m_arrCommand.GetSize(); i++) { if(m_arrCommand[i] == nCommandID) { bRet = ::ImageList_Remove(m_hImageList, i); if(bRet) { m_arrCommand.RemoveAt(i); #if _WTL_CMDBAR_VISTA_MENUS if(RunTimeHelper::IsVista()) { if(m_arrVistaBitmap[i] != NULL) ::DeleteObject(m_arrVistaBitmap[i]); m_arrVistaBitmap.RemoveAt(i); } #endif // _WTL_CMDBAR_VISTA_MENUS } break; } } if(bRet) bRet = AddBitmap(hBitmap, nCommandID); return bRet; } BOOL ReplaceIcon(ATL::_U_STRINGorID icon, UINT nCommandID) { ATLASSERT(::IsWindow(m_hWnd)); HICON hIcon = ::LoadIcon(ModuleHelper::GetResourceInstance(), icon.m_lpstr); if(hIcon == NULL) return FALSE; return ReplaceIcon(hIcon, nCommandID); } BOOL ReplaceIcon(HICON hIcon, UINT nCommandID) { ATLASSERT(::IsWindow(m_hWnd)); BOOL bRet = FALSE; for(int i = 0; i < m_arrCommand.GetSize(); i++) { if(m_arrCommand[i] == nCommandID) { bRet = (::ImageList_ReplaceIcon(m_hImageList, i, hIcon) != -1); #if _WTL_CMDBAR_VISTA_MENUS if(RunTimeHelper::IsVista() && bRet != FALSE) { T* pT = static_cast(this); pT->_ReplaceVistaBitmapFromImageList(i); } #endif // _WTL_CMDBAR_VISTA_MENUS break; } } return bRet; } BOOL RemoveImage(int nCommandID) { ATLASSERT(::IsWindow(m_hWnd)); BOOL bRet = FALSE; for(int i = 0; i < m_arrCommand.GetSize(); i++) { if(m_arrCommand[i] == nCommandID) { bRet = ::ImageList_Remove(m_hImageList, i); if(bRet) { m_arrCommand.RemoveAt(i); #if _WTL_CMDBAR_VISTA_MENUS if(RunTimeHelper::IsVista()) { if(m_arrVistaBitmap[i] != NULL) ::DeleteObject(m_arrVistaBitmap[i]); m_arrVistaBitmap.RemoveAt(i); } #endif // _WTL_CMDBAR_VISTA_MENUS } break; } } return bRet; } BOOL RemoveAllImages() { ATLASSERT(::IsWindow(m_hWnd)); ATLTRACE2(atlTraceUI, 0, _T("CmdBar - Removing all images\n")); BOOL bRet = ::ImageList_RemoveAll(m_hImageList); if(bRet) { m_arrCommand.RemoveAll(); #if _WTL_CMDBAR_VISTA_MENUS for(int i = 0; i < m_arrVistaBitmap.GetSize(); i++) { if(m_arrVistaBitmap[i] != NULL) ::DeleteObject(m_arrVistaBitmap[i]); } m_arrVistaBitmap.RemoveAll(); #endif // _WTL_CMDBAR_VISTA_MENUS } return bRet; } BOOL TrackPopupMenu(HMENU hMenu, UINT uFlags, int x, int y, LPTPMPARAMS lpParams = NULL) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(::IsMenu(hMenu)); if(!::IsMenu(hMenu)) return FALSE; m_bContextMenu = true; if(m_bUseKeyboardCues) m_bShowKeyboardCues = m_bKeyboardInput; T* pT = static_cast(this); return pT->DoTrackPopupMenu(hMenu, uFlags, x, y, lpParams); } BOOL SetMDIClient(HWND /*hWndMDIClient*/) { // Use CMDICommandBarCtrl for MDI support ATLASSERT(FALSE); return FALSE; } // Message map and handlers BEGIN_MSG_MAP(CCommandBarCtrlImpl) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) MESSAGE_HANDLER(WM_INITMENU, OnInitMenu) MESSAGE_HANDLER(WM_INITMENUPOPUP, OnInitMenuPopup) MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect) MESSAGE_HANDLER(GetAutoPopupMessage(), OnInternalAutoPopup) MESSAGE_HANDLER(GetGetBarMessage(), OnInternalGetBar) MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange) MESSAGE_HANDLER(WM_MENUCHAR, OnMenuChar) MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus) MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown) MESSAGE_HANDLER(WM_KEYUP, OnKeyUp) MESSAGE_HANDLER(WM_CHAR, OnChar) MESSAGE_HANDLER(WM_SYSKEYDOWN, OnSysKeyDown) MESSAGE_HANDLER(WM_SYSKEYUP, OnSysKeyUp) MESSAGE_HANDLER(WM_SYSCHAR, OnSysChar) // public API handlers - these stay to support chevrons in atlframe.h MESSAGE_HANDLER(CBRM_GETMENU, OnAPIGetMenu) MESSAGE_HANDLER(CBRM_TRACKPOPUPMENU, OnAPITrackPopupMenu) MESSAGE_HANDLER(CBRM_GETCMDBAR, OnAPIGetCmdBar) MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem) MESSAGE_HANDLER(WM_MEASUREITEM, OnMeasureItem) MESSAGE_HANDLER(WM_FORWARDMSG, OnForwardMsg) ALT_MSG_MAP(1) // Parent window messages NOTIFY_CODE_HANDLER(TBN_HOTITEMCHANGE, OnParentHotItemChange) NOTIFY_CODE_HANDLER(TBN_DROPDOWN, OnParentDropDown) MESSAGE_HANDLER(WM_INITMENUPOPUP, OnParentInitMenuPopup) MESSAGE_HANDLER(GetGetBarMessage(), OnParentInternalGetBar) MESSAGE_HANDLER(WM_SYSCOMMAND, OnParentSysCommand) MESSAGE_HANDLER(CBRM_GETMENU, OnParentAPIGetMenu) MESSAGE_HANDLER(WM_MENUCHAR, OnParentMenuChar) MESSAGE_HANDLER(CBRM_TRACKPOPUPMENU, OnParentAPITrackPopupMenu) MESSAGE_HANDLER(CBRM_GETCMDBAR, OnParentAPIGetCmdBar) MESSAGE_HANDLER(WM_SETTINGCHANGE, OnParentSettingChange) MESSAGE_HANDLER(WM_DRAWITEM, OnParentDrawItem) MESSAGE_HANDLER(WM_MEASUREITEM, OnParentMeasureItem) MESSAGE_HANDLER(WM_ACTIVATE, OnParentActivate) NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnParentCustomDraw) ALT_MSG_MAP(2) // MDI client window messages // Use CMDICommandBarCtrl for MDI support ALT_MSG_MAP(3) // Message hook messages MESSAGE_HANDLER(WM_MOUSEMOVE, OnHookMouseMove) MESSAGE_HANDLER(WM_SYSKEYDOWN, OnHookSysKeyDown) MESSAGE_HANDLER(WM_SYSKEYUP, OnHookSysKeyUp) MESSAGE_HANDLER(WM_SYSCHAR, OnHookSysChar) MESSAGE_HANDLER(WM_KEYDOWN, OnHookKeyDown) MESSAGE_HANDLER(WM_NEXTMENU, OnHookNextMenu) MESSAGE_HANDLER(WM_CHAR, OnHookChar) END_MSG_MAP() LRESULT OnForwardMsg(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/) { LPMSG pMsg = (LPMSG)lParam; if(pMsg->message >= WM_MOUSEFIRST && pMsg->message <= WM_MOUSELAST) m_bKeyboardInput = false; else if(pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST) m_bKeyboardInput = true; LRESULT lRet = 0; ProcessWindowMessage(pMsg->hwnd, pMsg->message, pMsg->wParam, pMsg->lParam, lRet, 3); return lRet; } LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { // Let the toolbar initialize itself LRESULT lRet = DefWindowProc(uMsg, wParam, lParam); // get and use system settings T* pT = static_cast(this); pT->GetSystemSettings(); // Parent init ATL::CWindow wndParent = GetParent(); ATL::CWindow wndTopLevelParent = wndParent.GetTopLevelParent(); m_wndParent.SubclassWindow(wndTopLevelParent); // Toolbar Init SetButtonStructSize(); SetImageList(NULL); // Create message hook if needed CWindowCreateCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CCommandBarCtrlImpl::OnCreate.\n")); ATLASSERT(FALSE); return -1; } if(s_pmapMsgHook == NULL) { ATLTRY(s_pmapMsgHook = new CMsgHookMap); ATLASSERT(s_pmapMsgHook != NULL); } if(s_pmapMsgHook != NULL) { DWORD dwThreadID = ::GetCurrentThreadId(); _MsgHookData* pData = s_pmapMsgHook->Lookup(dwThreadID); if(pData == NULL) { ATLTRY(pData = new _MsgHookData); ATLASSERT(pData != NULL); HHOOK hMsgHook = ::SetWindowsHookEx(WH_GETMESSAGE, MessageHookProc, ModuleHelper::GetModuleInstance(), dwThreadID); ATLASSERT(hMsgHook != NULL); if(pData != NULL && hMsgHook != NULL) { pData->hMsgHook = hMsgHook; pData->dwUsage = 1; BOOL bRet = s_pmapMsgHook->Add(dwThreadID, pData); bRet; ATLASSERT(bRet); } } else { (pData->dwUsage)++; } } lock.Unlock(); // Get layout #if (WINVER >= 0x0500) m_bLayoutRTL = ((GetExStyle() & WS_EX_LAYOUTRTL) != 0); #endif // (WINVER >= 0x0500) return lRet; } LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { LRESULT lRet = DefWindowProc(uMsg, wParam, lParam); #if _WTL_CMDBAR_VISTA_MENUS if(m_bVistaMenus && (m_hMenu != NULL)) { T* pT = static_cast(this); pT->_RemoveVistaBitmapsFromMenu(); } for(int i = 0; i < m_arrVistaBitmap.GetSize(); i++) { if(m_arrVistaBitmap[i] != NULL) ::DeleteObject(m_arrVistaBitmap[i]); } #endif // _WTL_CMDBAR_VISTA_MENUS if(m_bAttachedMenu) // nothing to do in this mode return lRet; CWindowCreateCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CCommandBarCtrlImpl::OnDestroy.\n")); ATLASSERT(FALSE); return lRet; } if(s_pmapMsgHook != NULL) { DWORD dwThreadID = ::GetCurrentThreadId(); _MsgHookData* pData = s_pmapMsgHook->Lookup(dwThreadID); if(pData != NULL) { (pData->dwUsage)--; if(pData->dwUsage == 0) { BOOL bRet = ::UnhookWindowsHookEx(pData->hMsgHook); ATLASSERT(bRet); bRet = s_pmapMsgHook->Remove(dwThreadID); ATLASSERT(bRet); if(bRet) delete pData; } if(s_pmapMsgHook->GetSize() == 0) { delete s_pmapMsgHook; s_pmapMsgHook = NULL; } } } lock.Unlock(); return lRet; } LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - OnKeyDown\n")); #endif bHandled = FALSE; // Simulate Alt+Space for the parent if(wParam == VK_SPACE) { m_wndParent.PostMessage(WM_SYSKEYDOWN, wParam, lParam | (1 << 29)); bHandled = TRUE; } #if (_WIN32_IE >= 0x0500) else if(wParam == VK_LEFT || wParam == VK_RIGHT) { WPARAM wpNext = m_bLayoutRTL ? VK_LEFT : VK_RIGHT; if(!m_bMenuActive) { T* pT = static_cast(this); int nBtn = GetHotItem(); int nNextBtn = (wParam == wpNext) ? pT->GetNextMenuItem(nBtn) : pT->GetPreviousMenuItem(nBtn); if(nNextBtn == -2) { SetHotItem(-1); if(pT->DisplayChevronMenu()) bHandled = TRUE; } } } #endif // (_WIN32_IE >= 0x0500) return 0; } LRESULT OnKeyUp(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - OnKeyUp\n")); #endif if(wParam != VK_SPACE) bHandled = FALSE; return 0; } LRESULT OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - OnChar\n")); #endif if(wParam != VK_SPACE) bHandled = FALSE; else return 0; // Security if(!m_wndParent.IsWindowEnabled() || ::GetFocus() != m_hWnd) return 0; // Handle mnemonic press when we have focus int nBtn = 0; if(wParam != VK_RETURN && !MapAccelerator((TCHAR)LOWORD(wParam), nBtn)) { #if (_WIN32_IE >= 0x0500) if((TCHAR)LOWORD(wParam) != _chChevronShortcut) #endif // (_WIN32_IE >= 0x0500) ::MessageBeep(0); } else { #if (_WIN32_IE >= 0x0500) RECT rcClient = { 0 }; GetClientRect(&rcClient); RECT rcBtn = { 0 }; GetItemRect(nBtn, &rcBtn); TBBUTTON tbb = { 0 }; GetButton(nBtn, &tbb); if((tbb.fsState & TBSTATE_ENABLED) != 0 && (tbb.fsState & TBSTATE_HIDDEN) == 0 && rcBtn.right <= rcClient.right) { #endif // (_WIN32_IE >= 0x0500) PostMessage(WM_KEYDOWN, VK_DOWN, 0L); if(wParam != VK_RETURN) SetHotItem(nBtn); #if (_WIN32_IE >= 0x0500) } else { ::MessageBeep(0); bHandled = TRUE; } #endif // (_WIN32_IE >= 0x0500) } return 0; } LRESULT OnSysKeyDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - OnSysKeyDown\n")); #endif bHandled = FALSE; return 0; } LRESULT OnSysKeyUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - OnSysKeyUp\n")); #endif bHandled = FALSE; return 0; } LRESULT OnSysChar(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - OnSysChar\n")); #endif bHandled = FALSE; return 0; } LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { if(m_bAttachedMenu || (m_dwExtendedStyle & CBR_EX_TRANSPARENT)) { bHandled = FALSE; return 0; } CDCHandle dc = (HDC)wParam; RECT rect = { 0 }; GetClientRect(&rect); dc.FillRect(&rect, COLOR_MENU); return 1; // don't do the default erase } LRESULT OnInitMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { int nIndex = GetHotItem(); SendMessage(WM_MENUSELECT, MAKEWPARAM(nIndex, MF_POPUP|MF_HILITE), (LPARAM)m_hMenu); bHandled = FALSE; return 1; } LRESULT OnInitMenuPopup(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if((BOOL)HIWORD(lParam)) // System menu, do nothing { bHandled = FALSE; return 1; } if(!(m_bAttachedMenu || m_bMenuActive)) // Not attached or ours, do nothing { bHandled = FALSE; return 1; } #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - OnInitMenuPopup\n")); #endif // forward to the parent or subclassed window, so it can handle update UI LRESULT lRet = 0; if(m_bAttachedMenu) lRet = DefWindowProc(uMsg, wParam, (lParam || m_bContextMenu) ? lParam : GetHotItem()); else lRet = m_wndParent.DefWindowProc(uMsg, wParam, (lParam || m_bContextMenu) ? lParam : GetHotItem()); #if _WTL_CMDBAR_VISTA_MENUS // If Vista menus are active, just set bitmaps and return if(m_bVistaMenus) { CMenuHandle menu = (HMENU)wParam; ATLASSERT(menu.m_hMenu != NULL); for(int i = 0; i < menu.GetMenuItemCount(); i++) { WORD nID = (WORD)menu.GetMenuItemID(i); int nIndex = m_arrCommand.Find(nID); CMenuItemInfo mii; mii.fMask = MIIM_BITMAP; mii.hbmpItem = (m_bImagesVisible && (nIndex != -1)) ? m_arrVistaBitmap[nIndex] : NULL; menu.SetMenuItemInfo(i, TRUE, &mii); } return lRet; } #endif // _WTL_CMDBAR_VISTA_MENUS // Convert menu items to ownerdraw, add our data if(m_bImagesVisible) { CMenuHandle menuPopup = (HMENU)wParam; ATLASSERT(menuPopup.m_hMenu != NULL); T* pT = static_cast(this); pT; // avoid level 4 warning TCHAR szString[pT->_nMaxMenuItemTextLength] = { 0 }; BOOL bRet = FALSE; for(int i = 0; i < menuPopup.GetMenuItemCount(); i++) { CMenuItemInfo mii; mii.cch = pT->_nMaxMenuItemTextLength; mii.fMask = MIIM_CHECKMARKS | MIIM_DATA | MIIM_ID | MIIM_STATE | MIIM_SUBMENU | MIIM_TYPE; mii.dwTypeData = szString; bRet = menuPopup.GetMenuItemInfo(i, TRUE, &mii); ATLASSERT(bRet); if(!(mii.fType & MFT_OWNERDRAW)) // Not already an ownerdraw item { mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_STATE; _MenuItemData* pMI = NULL; ATLTRY(pMI = new _MenuItemData); ATLASSERT(pMI != NULL); if(pMI != NULL) { pMI->fType = mii.fType; pMI->fState = mii.fState; mii.fType |= MFT_OWNERDRAW; pMI->iButton = -1; for(int j = 0; j < m_arrCommand.GetSize(); j++) { if(m_arrCommand[j] == mii.wID) { pMI->iButton = j; break; } } int cchLen = lstrlen(szString) + 1; pMI->lpstrText = NULL; ATLTRY(pMI->lpstrText = new TCHAR[cchLen]); ATLASSERT(pMI->lpstrText != NULL); if(pMI->lpstrText != NULL) SecureHelper::strcpy_x(pMI->lpstrText, cchLen, szString); mii.dwItemData = (ULONG_PTR)pMI; bRet = menuPopup.SetMenuItemInfo(i, TRUE, &mii); ATLASSERT(bRet); } } } // Add it to the list m_stackMenuHandle.Push(menuPopup.m_hMenu); } return lRet; } LRESULT OnMenuSelect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if(!m_bAttachedMenu) // Not attached, do nothing, forward to parent { m_bPopupItem = (lParam != NULL) && ((HMENU)lParam != m_hMenu) && (HIWORD(wParam) & MF_POPUP); if(m_wndParent.IsWindow()) m_wndParent.SendMessage(uMsg, wParam, lParam); bHandled = FALSE; return 1; } // Check if a menu is closing, do a cleanup if(HIWORD(wParam) == 0xFFFF && lParam == NULL) // Menu closing { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - OnMenuSelect - CLOSING!!!!\n")); #endif ATLASSERT(m_stackMenuWnd.GetSize() == 0); // Restore the menu items to the previous state for all menus that were converted if(m_bImagesVisible) { HMENU hMenu = NULL; while((hMenu = m_stackMenuHandle.Pop()) != NULL) { CMenuHandle menuPopup = hMenu; ATLASSERT(menuPopup.m_hMenu != NULL); // Restore state and delete menu item data BOOL bRet = FALSE; for(int i = 0; i < menuPopup.GetMenuItemCount(); i++) { CMenuItemInfo mii; mii.fMask = MIIM_DATA | MIIM_TYPE; bRet = menuPopup.GetMenuItemInfo(i, TRUE, &mii); ATLASSERT(bRet); _MenuItemData* pMI = (_MenuItemData*)mii.dwItemData; if(pMI != NULL && pMI->IsCmdBarMenuItem()) { mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_STATE; mii.fType = pMI->fType; mii.dwTypeData = pMI->lpstrText; mii.cch = lstrlen(pMI->lpstrText); mii.dwItemData = NULL; bRet = menuPopup.SetMenuItemInfo(i, TRUE, &mii); ATLASSERT(bRet); delete [] pMI->lpstrText; pMI->dwMagic = 0x6666; delete pMI; } } } } } bHandled = FALSE; return 1; } LRESULT OnInternalAutoPopup(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { int nIndex = (int)wParam; T* pT = static_cast(this); pT->DoPopupMenu(nIndex, false); return 0; } LRESULT OnInternalGetBar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { // Let's make sure we're not embedded in another process if((LPVOID)wParam != NULL) *((DWORD*)wParam) = GetCurrentProcessId(); if(IsWindowVisible()) return (LRESULT)static_cast(this); else return NULL; } LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { #ifndef SPI_GETKEYBOARDCUES const UINT SPI_SETKEYBOARDCUES = 0x100B; #endif // !SPI_GETKEYBOARDCUES #ifndef SPI_GETFLATMENU const UINT SPI_SETFLATMENU = 0x1023; #endif // !SPI_GETFLATMENU if(wParam == SPI_SETNONCLIENTMETRICS || wParam == SPI_SETKEYBOARDCUES || wParam == SPI_SETFLATMENU) { T* pT = static_cast(this); pT->GetSystemSettings(); } return 0; } LRESULT OnWindowPosChanging(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { LRESULT lRet = DefWindowProc(uMsg, wParam, lParam); LPWINDOWPOS lpWP = (LPWINDOWPOS)lParam; int cyMin = ::GetSystemMetrics(SM_CYMENU); if(lpWP->cy < cyMin) lpWP->cy = cyMin; return lRet; } LRESULT OnMenuChar(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - OnMenuChar\n")); #endif bHandled = TRUE; T* pT = static_cast(this); LRESULT lRet; if(m_bMenuActive && LOWORD(wParam) != 0x0D) lRet = 0; else lRet = MAKELRESULT(1, 1); if(m_bMenuActive && HIWORD(wParam) == MF_POPUP) { // Convert character to lower/uppercase and possibly Unicode, using current keyboard layout TCHAR ch = (TCHAR)LOWORD(wParam); CMenuHandle menu = (HMENU)lParam; int nCount = ::GetMenuItemCount(menu); int nRetCode = MNC_EXECUTE; BOOL bRet = FALSE; TCHAR szString[pT->_nMaxMenuItemTextLength] = { 0 }; WORD wMnem = 0; bool bFound = false; for(int i = 0; i < nCount; i++) { CMenuItemInfo mii; mii.cch = pT->_nMaxMenuItemTextLength; mii.fMask = MIIM_CHECKMARKS | MIIM_DATA | MIIM_ID | MIIM_STATE | MIIM_SUBMENU | MIIM_TYPE; mii.dwTypeData = szString; bRet = menu.GetMenuItemInfo(i, TRUE, &mii); if(!bRet || (mii.fType & MFT_SEPARATOR)) continue; _MenuItemData* pmd = (_MenuItemData*)mii.dwItemData; if(pmd != NULL && pmd->IsCmdBarMenuItem()) { LPTSTR p = pmd->lpstrText; if(p != NULL) { while(*p && *p != _T('&')) p = ::CharNext(p); if(p != NULL && *p) { DWORD dwP = MAKELONG(*(++p), 0); DWORD dwC = MAKELONG(ch, 0); if(::CharLower((LPTSTR)ULongToPtr(dwP)) == ::CharLower((LPTSTR)ULongToPtr(dwC))) { if(!bFound) { wMnem = (WORD)i; bFound = true; } else { nRetCode = MNC_SELECT; break; } } } } } } if(bFound) { if(nRetCode == MNC_EXECUTE) { PostMessage(TB_SETHOTITEM, (WPARAM)-1, 0L); pT->GiveFocusBack(); } bHandled = TRUE; lRet = MAKELRESULT(wMnem, nRetCode); } } else if(!m_bMenuActive) { int nBtn = 0; if(!MapAccelerator((TCHAR)LOWORD(wParam), nBtn)) { bHandled = FALSE; PostMessage(TB_SETHOTITEM, (WPARAM)-1, 0L); pT->GiveFocusBack(); #if (_WIN32_IE >= 0x0500) // check if we should display chevron menu if((TCHAR)LOWORD(wParam) == pT->_chChevronShortcut) { if(pT->DisplayChevronMenu()) bHandled = TRUE; } #endif // (_WIN32_IE >= 0x0500) } else if(m_wndParent.IsWindowEnabled()) { #if (_WIN32_IE >= 0x0500) RECT rcClient = { 0 }; GetClientRect(&rcClient); RECT rcBtn = { 0 }; GetItemRect(nBtn, &rcBtn); TBBUTTON tbb = { 0 }; GetButton(nBtn, &tbb); if((tbb.fsState & TBSTATE_ENABLED) != 0 && (tbb.fsState & TBSTATE_HIDDEN) == 0 && rcBtn.right <= rcClient.right) { #endif // (_WIN32_IE >= 0x0500) if(m_bUseKeyboardCues && !m_bShowKeyboardCues) { m_bAllowKeyboardCues = true; ShowKeyboardCues(true); } pT->TakeFocus(); PostMessage(WM_KEYDOWN, VK_DOWN, 0L); SetHotItem(nBtn); #if (_WIN32_IE >= 0x0500) } else { ::MessageBeep(0); } #endif // (_WIN32_IE >= 0x0500) } } return lRet; } LRESULT OnKillFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(m_bUseKeyboardCues && m_bShowKeyboardCues) ShowKeyboardCues(false); bHandled = FALSE; return 1; } LRESULT OnDrawItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { LPDRAWITEMSTRUCT lpDrawItemStruct = (LPDRAWITEMSTRUCT)lParam; _MenuItemData* pmd = (_MenuItemData*)lpDrawItemStruct->itemData; if(lpDrawItemStruct->CtlType == ODT_MENU && pmd != NULL && pmd->IsCmdBarMenuItem()) { T* pT = static_cast(this); pT->DrawItem(lpDrawItemStruct); } else { bHandled = FALSE; } return (LRESULT)TRUE; } LRESULT OnMeasureItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { LPMEASUREITEMSTRUCT lpMeasureItemStruct = (LPMEASUREITEMSTRUCT)lParam; _MenuItemData* pmd = (_MenuItemData*)lpMeasureItemStruct->itemData; if(lpMeasureItemStruct->CtlType == ODT_MENU && pmd != NULL && pmd->IsCmdBarMenuItem()) { T* pT = static_cast(this); pT->MeasureItem(lpMeasureItemStruct); } else { bHandled = FALSE; } return (LRESULT)TRUE; } // API message handlers LRESULT OnAPIGetMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return (LRESULT)m_hMenu; } LRESULT OnAPITrackPopupMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/) { if(lParam == NULL) return FALSE; LPCBRPOPUPMENU lpCBRPopupMenu = (LPCBRPOPUPMENU)lParam; if(lpCBRPopupMenu->cbSize != sizeof(CBRPOPUPMENU)) return FALSE; T* pT = static_cast(this); return pT->TrackPopupMenu(lpCBRPopupMenu->hMenu, lpCBRPopupMenu->uFlags, lpCBRPopupMenu->x, lpCBRPopupMenu->y, lpCBRPopupMenu->lptpm); } LRESULT OnAPIGetCmdBar(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return (LRESULT)m_hWnd; } // Parent window message handlers LRESULT OnParentHotItemChange(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled) { LPNMTBHOTITEM lpNMHT = (LPNMTBHOTITEM)pnmh; // Check if this comes from us if(pnmh->hwndFrom != m_hWnd) { bHandled = FALSE; return 0; } bool bBlockTracking = false; if((m_dwExtendedStyle & CBR_EX_TRACKALWAYS) == 0) { DWORD dwProcessID; ::GetWindowThreadProcessId(::GetActiveWindow(), &dwProcessID); bBlockTracking = (::GetCurrentProcessId() != dwProcessID); } if((!m_wndParent.IsWindowEnabled() || bBlockTracking) && (lpNMHT->dwFlags & HICF_MOUSE)) { return 1; } else { #ifndef HICF_LMOUSE const DWORD HICF_LMOUSE = 0x00000080; // left mouse button selected #endif bHandled = FALSE; // Send WM_MENUSELECT to the app if it needs to display a status text if(!(lpNMHT->dwFlags & HICF_MOUSE) && !(lpNMHT->dwFlags & HICF_ACCELERATOR) && !(lpNMHT->dwFlags & HICF_LMOUSE)) { if(lpNMHT->dwFlags & HICF_ENTERING) m_wndParent.SendMessage(WM_MENUSELECT, 0, (LPARAM)m_hMenu); if(lpNMHT->dwFlags & HICF_LEAVING) m_wndParent.SendMessage(WM_MENUSELECT, MAKEWPARAM(0, 0xFFFF), NULL); } return 0; } } LRESULT OnParentDropDown(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled) { // Check if this comes from us if(pnmh->hwndFrom != m_hWnd) { bHandled = FALSE; return 1; } T* pT = static_cast(this); if(::GetFocus() != m_hWnd) pT->TakeFocus(); LPNMTOOLBAR pNMToolBar = (LPNMTOOLBAR)pnmh; int nIndex = CommandToIndex(pNMToolBar->iItem); m_bContextMenu = false; m_bEscapePressed = false; pT->DoPopupMenu(nIndex, true); return TBDDRET_DEFAULT; } LRESULT OnParentInitMenuPopup(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { return OnInitMenuPopup(uMsg, wParam, lParam, bHandled); } LRESULT OnParentInternalGetBar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { return OnInternalGetBar(uMsg, wParam, lParam, bHandled); } LRESULT OnParentSysCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { bHandled = FALSE; if((m_uSysKey == VK_MENU || (m_uSysKey == VK_F10 && !(::GetKeyState(VK_SHIFT) & 0x80)) || m_uSysKey == VK_SPACE) && wParam == SC_KEYMENU) { T* pT = static_cast(this); if(::GetFocus() == m_hWnd) { pT->GiveFocusBack(); // exit menu "loop" PostMessage(TB_SETHOTITEM, (WPARAM)-1, 0L); } else if(m_uSysKey != VK_SPACE && !m_bSkipMsg) { if(m_bUseKeyboardCues && !m_bShowKeyboardCues && m_bAllowKeyboardCues) ShowKeyboardCues(true); pT->TakeFocus(); // enter menu "loop" bHandled = TRUE; } else if(m_uSysKey != VK_SPACE) { bHandled = TRUE; } } m_bSkipMsg = false; return 0; } LRESULT OnParentAPIGetMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { return OnAPIGetMenu(uMsg, wParam, lParam, bHandled); } LRESULT OnParentMenuChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { return OnMenuChar(uMsg, wParam, lParam, bHandled); } LRESULT OnParentAPITrackPopupMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { return OnAPITrackPopupMenu(uMsg, wParam, lParam, bHandled); } LRESULT OnParentAPIGetCmdBar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { return OnAPIGetCmdBar(uMsg, wParam, lParam, bHandled); } LRESULT OnParentSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { OnSettingChange(uMsg, wParam, lParam, bHandled); bHandled = FALSE; return 1; } LRESULT OnParentDrawItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { return OnDrawItem(uMsg, wParam, lParam, bHandled); } LRESULT OnParentMeasureItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { return OnMeasureItem(uMsg, wParam, lParam, bHandled); } LRESULT OnParentActivate(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { m_bParentActive = (LOWORD(wParam) != WA_INACTIVE); if(!m_bParentActive && m_bUseKeyboardCues && m_bShowKeyboardCues) { ShowKeyboardCues(false); // this will repaint our window } else { Invalidate(); UpdateWindow(); } bHandled = FALSE; return 1; } LRESULT OnParentCustomDraw(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled) { LRESULT lRet = CDRF_DODEFAULT; bHandled = FALSE; if(pnmh->hwndFrom == m_hWnd) { LPNMTBCUSTOMDRAW lpTBCustomDraw = (LPNMTBCUSTOMDRAW)pnmh; if(lpTBCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT) { lRet = CDRF_NOTIFYITEMDRAW; bHandled = TRUE; } else if(lpTBCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) { #if _WTL_CMDBAR_VISTA_MENUS && defined(_WTL_CMDBAR_VISTA_STD_MENUBAR) if(m_bVistaMenus) { ::SetRectEmpty(&lpTBCustomDraw->rcText); lRet = CDRF_NOTIFYPOSTPAINT; bHandled = TRUE; } else #endif // _WTL_CMDBAR_VISTA_MENUS && defined(_WTL_CMDBAR_VISTA_STD_MENUBAR) { if(m_bFlatMenus) { #ifndef COLOR_MENUHILIGHT const int COLOR_MENUHILIGHT = 29; #endif // !COLOR_MENUHILIGHT bool bDisabled = ((lpTBCustomDraw->nmcd.uItemState & CDIS_DISABLED) == CDIS_DISABLED); if(!bDisabled && ((lpTBCustomDraw->nmcd.uItemState & CDIS_HOT) == CDIS_HOT || (lpTBCustomDraw->nmcd.uItemState & CDIS_SELECTED) == CDIS_SELECTED)) { ::FillRect(lpTBCustomDraw->nmcd.hdc, &lpTBCustomDraw->nmcd.rc, ::GetSysColorBrush(COLOR_MENUHILIGHT)); ::FrameRect(lpTBCustomDraw->nmcd.hdc, &lpTBCustomDraw->nmcd.rc, ::GetSysColorBrush(COLOR_HIGHLIGHT)); lpTBCustomDraw->clrText = ::GetSysColor(m_bParentActive ? COLOR_HIGHLIGHTTEXT : COLOR_GRAYTEXT); } else if(bDisabled || !m_bParentActive) { lpTBCustomDraw->clrText = ::GetSysColor(COLOR_GRAYTEXT); } _ParentCustomDrawHelper(lpTBCustomDraw); lRet = CDRF_SKIPDEFAULT; bHandled = TRUE; } else if(!m_bParentActive) { lpTBCustomDraw->clrText = ::GetSysColor(COLOR_GRAYTEXT); bHandled = TRUE; } } } #if _WTL_CMDBAR_VISTA_MENUS && defined(_WTL_CMDBAR_VISTA_STD_MENUBAR) else if (lpTBCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPOSTPAINT) { bool bDisabled = ((lpTBCustomDraw->nmcd.uItemState & CDIS_DISABLED) == CDIS_DISABLED); if(bDisabled || !m_bParentActive) lpTBCustomDraw->clrText = ::GetSysColor(COLOR_GRAYTEXT); _ParentCustomDrawHelper(lpTBCustomDraw); lRet = CDRF_SKIPDEFAULT; bHandled = TRUE; } #endif // _WTL_CMDBAR_VISTA_MENUS && defined(_WTL_CMDBAR_VISTA_STD_MENUBAR) } return lRet; } void _ParentCustomDrawHelper(LPNMTBCUSTOMDRAW lpTBCustomDraw) { CDCHandle dc = lpTBCustomDraw->nmcd.hdc; dc.SetTextColor(lpTBCustomDraw->clrText); dc.SetBkMode(lpTBCustomDraw->nStringBkMode); HFONT hFont = GetFont(); HFONT hFontOld = NULL; if(hFont != NULL) hFontOld = dc.SelectFont(hFont); const int cchText = 200; TCHAR szText[cchText] = { 0 }; TBBUTTONINFO tbbi = { 0 }; tbbi.cbSize = sizeof(TBBUTTONINFO); tbbi.dwMask = TBIF_TEXT; tbbi.pszText = szText; tbbi.cchText = cchText; GetButtonInfo((int)lpTBCustomDraw->nmcd.dwItemSpec, &tbbi); dc.DrawText(szText, -1, &lpTBCustomDraw->nmcd.rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER | (m_bShowKeyboardCues ? 0 : DT_HIDEPREFIX)); if(hFont != NULL) dc.SelectFont(hFontOld); } // Message hook handlers LRESULT OnHookMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { static POINT s_point = { -1, -1 }; DWORD dwPoint = ::GetMessagePos(); POINT point = { GET_X_LPARAM(dwPoint), GET_Y_LPARAM(dwPoint) }; bHandled = FALSE; if(m_bMenuActive) { if(::WindowFromPoint(point) == m_hWnd) { ScreenToClient(&point); int nHit = HitTest(&point); if((point.x != s_point.x || point.y != s_point.y) && nHit >= 0 && nHit < ::GetMenuItemCount(m_hMenu) && nHit != m_nPopBtn && m_nPopBtn != -1) { TBBUTTON tbb = { 0 }; GetButton(nHit, &tbb); if((tbb.fsState & TBSTATE_ENABLED) != 0) { m_nNextPopBtn = nHit | 0xFFFF0000; HWND hWndMenu = m_stackMenuWnd.GetCurrent(); ATLASSERT(hWndMenu != NULL); // this one is needed to close a menu if mouse button was down ::PostMessage(hWndMenu, WM_LBUTTONUP, 0, MAKELPARAM(point.x, point.y)); // this one closes a popup menu ::PostMessage(hWndMenu, WM_KEYDOWN, VK_ESCAPE, 0L); bHandled = TRUE; } } } } else { ScreenToClient(&point); } s_point = point; return 0; } LRESULT OnHookSysKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { bHandled = FALSE; #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - Hook WM_SYSKEYDOWN (0x%2.2X)\n"), wParam); #endif if(wParam == VK_MENU && m_bParentActive && m_bUseKeyboardCues && !m_bShowKeyboardCues && m_bAllowKeyboardCues) ShowKeyboardCues(true); if(wParam != VK_SPACE && !m_bMenuActive && ::GetFocus() == m_hWnd) { m_bAllowKeyboardCues = false; PostMessage(TB_SETHOTITEM, (WPARAM)-1, 0L); T* pT = static_cast(this); pT->GiveFocusBack(); m_bSkipMsg = true; } else { if(wParam == VK_SPACE && m_bUseKeyboardCues && m_bShowKeyboardCues) { m_bAllowKeyboardCues = true; ShowKeyboardCues(false); } m_uSysKey = (UINT)wParam; } return 0; } LRESULT OnHookSysKeyUp(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { if(!m_bAllowKeyboardCues) m_bAllowKeyboardCues = true; bHandled = FALSE; wParam; #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - Hook WM_SYSKEYUP (0x%2.2X)\n"), wParam); #endif return 0; } LRESULT OnHookSysChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { bHandled = FALSE; #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - Hook WM_SYSCHAR (0x%2.2X)\n"), wParam); #endif if(!m_bMenuActive && m_hWndHook != m_hWnd && wParam != VK_SPACE) bHandled = TRUE; return 0; } LRESULT OnHookKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - Hook WM_KEYDOWN (0x%2.2X)\n"), wParam); #endif bHandled = FALSE; T* pT = static_cast(this); if(wParam == VK_ESCAPE && m_stackMenuWnd.GetSize() <= 1) { if(m_bMenuActive && !m_bContextMenu) { int nHot = GetHotItem(); if(nHot == -1) nHot = m_nPopBtn; if(nHot == -1) nHot = 0; SetHotItem(nHot); bHandled = TRUE; pT->TakeFocus(); m_bEscapePressed = true; // To keep focus m_bSkipPostDown = false; } else if(::GetFocus() == m_hWnd && m_wndParent.IsWindow()) { SetHotItem(-1); pT->GiveFocusBack(); bHandled = TRUE; } } else if(wParam == VK_RETURN || wParam == VK_UP || wParam == VK_DOWN) { if(!m_bMenuActive && ::GetFocus() == m_hWnd && m_wndParent.IsWindow()) { int nHot = GetHotItem(); if(nHot != -1) { if(wParam != VK_RETURN) { if(!m_bSkipPostDown) { // IE4 only: WM_KEYDOWN doesn't generate TBN_DROPDOWN, we need to simulate a mouse click #if (_WIN32_IE < 0x0500) DWORD dwMajor = 0, dwMinor = 0; ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor); if(dwMajor <= 4 || (dwMajor == 5 && dwMinor < 80)) { RECT rect = { 0 }; GetItemRect(nHot, &rect); PostMessage(WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(rect.left, rect.top)); } #endif // (_WIN32_IE < 0x0500) PostMessage(WM_KEYDOWN, VK_DOWN, 0L); m_bSkipPostDown = true; } else { ATLTRACE2(atlTraceUI, 0, _T("CmdBar - skipping posting another VK_DOWN\n")); m_bSkipPostDown = false; } } } else { ATLTRACE2(atlTraceUI, 0, _T("CmdBar - Can't find hot button\n")); } } if(wParam == VK_RETURN && m_bMenuActive) { PostMessage(TB_SETHOTITEM, (WPARAM)-1, 0L); m_nNextPopBtn = -1; pT->GiveFocusBack(); } } else if(wParam == VK_LEFT || wParam == VK_RIGHT) { WPARAM wpNext = m_bLayoutRTL ? VK_LEFT : VK_RIGHT; WPARAM wpPrev = m_bLayoutRTL ? VK_RIGHT : VK_LEFT; if(m_bMenuActive && !m_bContextMenu && !(wParam == wpNext && m_bPopupItem)) { bool bAction = false; if(wParam == wpPrev && s_pCurrentBar->m_stackMenuWnd.GetSize() == 1) { m_nNextPopBtn = pT->GetPreviousMenuItem(m_nPopBtn); if(m_nNextPopBtn != -1) bAction = true; } else if(wParam == wpNext) { m_nNextPopBtn = pT->GetNextMenuItem(m_nPopBtn); if(m_nNextPopBtn != -1) bAction = true; } HWND hWndMenu = m_stackMenuWnd.GetCurrent(); ATLASSERT(hWndMenu != NULL); // Close the popup menu if(bAction) { ::PostMessage(hWndMenu, WM_KEYDOWN, VK_ESCAPE, 0L); if(wParam == wpNext) { int cItem = m_stackMenuWnd.GetSize() - 1; while(cItem >= 0) { hWndMenu = m_stackMenuWnd[cItem]; if(hWndMenu != NULL) ::PostMessage(hWndMenu, WM_KEYDOWN, VK_ESCAPE, 0L); cItem--; } } #if (_WIN32_IE >= 0x0500) if(m_nNextPopBtn == -2) { m_nNextPopBtn = -1; pT->DisplayChevronMenu(); } #endif // (_WIN32_IE >= 0x0500) bHandled = TRUE; } } } return 0; } LRESULT OnHookNextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - Hook WM_NEXTMENU\n")); #endif bHandled = FALSE; return 1; } LRESULT OnHookChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - Hook WM_CHAR (0x%2.2X)\n"), wParam); #endif bHandled = (wParam == VK_ESCAPE); return 0; } // Implementation - ownerdraw overrideables and helpers void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { T* pT = static_cast(this); if(m_bFlatMenus) pT->DrawItemFlat(lpDrawItemStruct); else pT->DrawItem3D(lpDrawItemStruct); } void DrawItem3D(LPDRAWITEMSTRUCT lpDrawItemStruct) { _MenuItemData* pmd = (_MenuItemData*)lpDrawItemStruct->itemData; CDCHandle dc = lpDrawItemStruct->hDC; const RECT& rcItem = lpDrawItemStruct->rcItem; T* pT = static_cast(this); if(pmd->fType & MFT_SEPARATOR) { // draw separator RECT rc = rcItem; rc.top += (rc.bottom - rc.top) / 2; // vertical center dc.DrawEdge(&rc, EDGE_ETCHED, BF_TOP); // draw separator line } else // not a separator { BOOL bDisabled = lpDrawItemStruct->itemState & ODS_GRAYED; BOOL bSelected = lpDrawItemStruct->itemState & ODS_SELECTED; BOOL bChecked = lpDrawItemStruct->itemState & ODS_CHECKED; BOOL bHasImage = FALSE; if(LOWORD(lpDrawItemStruct->itemID) == (WORD)-1) bSelected = FALSE; RECT rcButn = { rcItem.left, rcItem.top, rcItem.left + m_szButton.cx, rcItem.top + m_szButton.cy }; // button rect ::OffsetRect(&rcButn, 0, ((rcItem.bottom - rcItem.top) - (rcButn.bottom - rcButn.top)) / 2); // center vertically int iButton = pmd->iButton; if(iButton >= 0) { bHasImage = TRUE; // calc drawing point SIZE sz = { rcButn.right - rcButn.left - m_szBitmap.cx, rcButn.bottom - rcButn.top - m_szBitmap.cy }; sz.cx /= 2; sz.cy /= 2; POINT point = { rcButn.left + sz.cx, rcButn.top + sz.cy }; // fill background depending on state if(!bChecked || (bSelected && !bDisabled)) { if(!bDisabled) dc.FillRect(&rcButn, (bChecked && !bSelected) ? COLOR_3DLIGHT : COLOR_MENU); else dc.FillRect(&rcButn, COLOR_MENU); } else { COLORREF crTxt = dc.SetTextColor(::GetSysColor(COLOR_BTNFACE)); COLORREF crBk = dc.SetBkColor(::GetSysColor(COLOR_BTNHILIGHT)); CBrush hbr(CDCHandle::GetHalftoneBrush()); dc.SetBrushOrg(rcButn.left, rcButn.top); dc.FillRect(&rcButn, hbr); dc.SetTextColor(crTxt); dc.SetBkColor(crBk); } // draw disabled or normal if(!bDisabled) { // draw pushed-in or popped-out edge if(bSelected || bChecked) { RECT rc2 = rcButn; dc.DrawEdge(&rc2, bChecked ? BDR_SUNKENOUTER : BDR_RAISEDINNER, BF_RECT); } // draw the image ::ImageList_Draw(m_hImageList, iButton, dc, point.x, point.y, ILD_TRANSPARENT); } else { HBRUSH hBrushBackground = bChecked ? NULL : ::GetSysColorBrush(COLOR_MENU); pT->DrawBitmapDisabled(dc, iButton, point, hBrushBackground); } } else { // no image - look for custom checked/unchecked bitmaps CMenuItemInfo info; info.fMask = MIIM_CHECKMARKS | MIIM_TYPE; ::GetMenuItemInfo((HMENU)lpDrawItemStruct->hwndItem, lpDrawItemStruct->itemID, MF_BYCOMMAND, &info); if(bChecked || info.hbmpUnchecked != NULL) { BOOL bRadio = ((info.fType & MFT_RADIOCHECK) != 0); bHasImage = pT->DrawCheckmark(dc, rcButn, bSelected, bDisabled, bRadio, bChecked ? info.hbmpChecked : info.hbmpUnchecked); } } // draw item text int cxButn = m_szButton.cx; COLORREF colorBG = ::GetSysColor(bSelected ? COLOR_HIGHLIGHT : COLOR_MENU); if(bSelected || lpDrawItemStruct->itemAction == ODA_SELECT) { RECT rcBG = rcItem; if(bHasImage) rcBG.left += cxButn + s_kcxGap; dc.FillRect(&rcBG, bSelected ? COLOR_HIGHLIGHT : COLOR_MENU); } // calc text rectangle and colors RECT rcText = rcItem; rcText.left += cxButn + s_kcxGap + s_kcxTextMargin; rcText.right -= cxButn; dc.SetBkMode(TRANSPARENT); COLORREF colorText = ::GetSysColor(bDisabled ? (bSelected ? COLOR_GRAYTEXT : COLOR_3DSHADOW) : (bSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)); // font already selected by Windows if(bDisabled && (!bSelected || colorText == colorBG)) { // disabled - draw shadow text shifted down and right 1 pixel (unles selected) RECT rcDisabled = rcText; ::OffsetRect(&rcDisabled, 1, 1); pT->DrawMenuText(dc, rcDisabled, pmd->lpstrText, ::GetSysColor(COLOR_3DHILIGHT)); } pT->DrawMenuText(dc, rcText, pmd->lpstrText, colorText); // finally! } } void DrawItemFlat(LPDRAWITEMSTRUCT lpDrawItemStruct) { _MenuItemData* pmd = (_MenuItemData*)lpDrawItemStruct->itemData; CDCHandle dc = lpDrawItemStruct->hDC; const RECT& rcItem = lpDrawItemStruct->rcItem; T* pT = static_cast(this); #ifndef COLOR_MENUHILIGHT const int COLOR_MENUHILIGHT = 29; #endif // !COLOR_MENUHILIGHT BOOL bDisabled = lpDrawItemStruct->itemState & ODS_GRAYED; BOOL bSelected = lpDrawItemStruct->itemState & ODS_SELECTED; BOOL bChecked = lpDrawItemStruct->itemState & ODS_CHECKED; // paint background if(bSelected || lpDrawItemStruct->itemAction == ODA_SELECT) { if(bSelected) { dc.FillRect(&rcItem, ::GetSysColorBrush(COLOR_MENUHILIGHT)); dc.FrameRect(&rcItem, ::GetSysColorBrush(COLOR_HIGHLIGHT)); } else { dc.FillRect(&rcItem, ::GetSysColorBrush(COLOR_MENU)); } } if(pmd->fType & MFT_SEPARATOR) { // draw separator RECT rc = rcItem; rc.top += (rc.bottom - rc.top) / 2; // vertical center dc.DrawEdge(&rc, EDGE_ETCHED, BF_TOP); // draw separator line } else // not a separator { if(LOWORD(lpDrawItemStruct->itemID) == (WORD)-1) bSelected = FALSE; RECT rcButn = { rcItem.left, rcItem.top, rcItem.left + m_szButton.cx, rcItem.top + m_szButton.cy }; // button rect ::OffsetRect(&rcButn, 0, ((rcItem.bottom - rcItem.top) - (rcButn.bottom - rcButn.top)) / 2); // center vertically // draw background and border for checked items if(bChecked) { RECT rcCheck = rcButn; ::InflateRect(&rcCheck, -1, -1); if(bSelected) dc.FillRect(&rcCheck, ::GetSysColorBrush(COLOR_MENU)); dc.FrameRect(&rcCheck, ::GetSysColorBrush(COLOR_HIGHLIGHT)); } int iButton = pmd->iButton; if(iButton >= 0) { // calc drawing point SIZE sz = { rcButn.right - rcButn.left - m_szBitmap.cx, rcButn.bottom - rcButn.top - m_szBitmap.cy }; sz.cx /= 2; sz.cy /= 2; POINT point = { rcButn.left + sz.cx, rcButn.top + sz.cy }; // draw disabled or normal if(!bDisabled) { ::ImageList_Draw(m_hImageList, iButton, dc, point.x, point.y, ILD_TRANSPARENT); } else { HBRUSH hBrushBackground = ::GetSysColorBrush((bSelected && !(bDisabled && bChecked)) ? COLOR_MENUHILIGHT : COLOR_MENU); HBRUSH hBrushDisabledImage = ::GetSysColorBrush(COLOR_3DSHADOW); pT->DrawBitmapDisabled(dc, iButton, point, hBrushBackground, hBrushBackground, hBrushDisabledImage); } } else { // no image - look for custom checked/unchecked bitmaps CMenuItemInfo info; info.fMask = MIIM_CHECKMARKS | MIIM_TYPE; ::GetMenuItemInfo((HMENU)lpDrawItemStruct->hwndItem, lpDrawItemStruct->itemID, MF_BYCOMMAND, &info); if(bChecked || info.hbmpUnchecked != NULL) { BOOL bRadio = ((info.fType & MFT_RADIOCHECK) != 0); pT->DrawCheckmark(dc, rcButn, bSelected, bDisabled, bRadio, bChecked ? info.hbmpChecked : info.hbmpUnchecked); } } // draw item text int cxButn = m_szButton.cx; // calc text rectangle and colors RECT rcText = rcItem; rcText.left += cxButn + s_kcxGap + s_kcxTextMargin; rcText.right -= cxButn; dc.SetBkMode(TRANSPARENT); COLORREF colorText = ::GetSysColor(bDisabled ? (bSelected ? COLOR_GRAYTEXT : COLOR_3DSHADOW) : (bSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)); pT->DrawMenuText(dc, rcText, pmd->lpstrText, colorText); // finally! } } void DrawMenuText(CDCHandle& dc, RECT& rc, LPCTSTR lpstrText, COLORREF color) { int nTab = -1; for(int i = 0; i < lstrlen(lpstrText); i++) { if(lpstrText[i] == _T('\t')) { nTab = i; break; } } dc.SetTextColor(color); dc.DrawText(lpstrText, nTab, &rc, DT_SINGLELINE | DT_LEFT | DT_VCENTER | (m_bShowKeyboardCues ? 0 : DT_HIDEPREFIX)); if(nTab != -1) dc.DrawText(&lpstrText[nTab + 1], -1, &rc, DT_SINGLELINE | DT_RIGHT | DT_VCENTER | (m_bShowKeyboardCues ? 0 : DT_HIDEPREFIX)); } void DrawBitmapDisabled(CDCHandle& dc, int nImage, POINT point, HBRUSH hBrushBackground = ::GetSysColorBrush(COLOR_3DFACE), HBRUSH hBrush3DEffect = ::GetSysColorBrush(COLOR_3DHILIGHT), HBRUSH hBrushDisabledImage = ::GetSysColorBrush(COLOR_3DSHADOW)) { #if (_WIN32_WINNT >= 0x0501) && (_WIN32_IE >= 0x0501) if(m_bAlphaImages) { IMAGELISTDRAWPARAMS ildp = { 0 }; ildp.cbSize = sizeof(IMAGELISTDRAWPARAMS); ildp.himl = m_hImageList; ildp.i = nImage; ildp.hdcDst = dc; ildp.x = point.x; ildp.y = point.y; ildp.cx = 0; ildp.cy = 0; ildp.xBitmap = 0; ildp.yBitmap = 0; ildp.fStyle = ILD_TRANSPARENT; ildp.fState = ILS_SATURATE; ildp.Frame = 0; ::ImageList_DrawIndirect(&ildp); } else #endif // (_WIN32_WINNT >= 0x0501) && (_WIN32_IE >= 0x0501) { // create memory DC CDC dcMem; dcMem.CreateCompatibleDC(dc); // create mono or color bitmap CBitmap bmp; bmp.CreateCompatibleBitmap(dc, m_szBitmap.cx, m_szBitmap.cy); ATLASSERT(bmp.m_hBitmap != NULL); // draw image into memory DC--fill BG white first HBITMAP hBmpOld = dcMem.SelectBitmap(bmp); dcMem.PatBlt(0, 0, m_szBitmap.cx, m_szBitmap.cy, WHITENESS); // If white is the text color, we can't use the normal painting since // it would blend with the WHITENESS, but the mask is OK UINT uDrawStyle = (::GetSysColor(COLOR_BTNTEXT) == RGB(255, 255, 255)) ? ILD_MASK : ILD_NORMAL; ::ImageList_Draw(m_hImageList, nImage, dcMem, 0, 0, uDrawStyle); dc.DitherBlt(point.x, point.y, m_szBitmap.cx, m_szBitmap.cy, dcMem, NULL, 0, 0, hBrushBackground, hBrush3DEffect, hBrushDisabledImage); dcMem.SelectBitmap(hBmpOld); // restore } } // old name BOOL Draw3DCheckmark(CDCHandle& dc, const RECT& rc, BOOL bSelected, BOOL bDisabled, BOOL bRadio, HBITMAP hBmpCheck) { return DrawCheckmark(dc, rc, bSelected, bDisabled, bRadio, hBmpCheck); } BOOL DrawCheckmark(CDCHandle& dc, const RECT& rc, BOOL bSelected, BOOL bDisabled, BOOL bRadio, HBITMAP hBmpCheck) { // get checkmark bitmap, if none, use Windows standard SIZE size = { 0, 0 }; CBitmapHandle bmp = hBmpCheck; if(hBmpCheck != NULL) { bmp.GetSize(size); } else { size.cx = ::GetSystemMetrics(SM_CXMENUCHECK); size.cy = ::GetSystemMetrics(SM_CYMENUCHECK); bmp.CreateCompatibleBitmap(dc, size.cx, size.cy); ATLASSERT(bmp.m_hBitmap != NULL); } // center bitmap in caller's rectangle RECT rcDest = rc; if((rc.right - rc.left) > size.cx) { rcDest.left = rc.left + (rc.right - rc.left - size.cx) / 2; rcDest.right = rcDest.left + size.cx; } if((rc.bottom - rc.top) > size.cy) { rcDest.top = rc.top + (rc.bottom - rc.top - size.cy) / 2; rcDest.bottom = rcDest.top + size.cy; } // paint background if(!m_bFlatMenus) { if(bSelected && !bDisabled) { dc.FillRect(&rcDest, COLOR_MENU); } else { COLORREF clrTextOld = dc.SetTextColor(::GetSysColor(COLOR_BTNFACE)); COLORREF clrBkOld = dc.SetBkColor(::GetSysColor(COLOR_BTNHILIGHT)); CBrush hbr(CDCHandle::GetHalftoneBrush()); dc.SetBrushOrg(rcDest.left, rcDest.top); dc.FillRect(&rcDest, hbr); dc.SetTextColor(clrTextOld); dc.SetBkColor(clrBkOld); } } // create source image CDC dcSource; dcSource.CreateCompatibleDC(dc); HBITMAP hBmpOld = dcSource.SelectBitmap(bmp); // set colors const COLORREF clrBlack = RGB(0, 0, 0); const COLORREF clrWhite = RGB(255, 255, 255); COLORREF clrTextOld = dc.SetTextColor(clrBlack); COLORREF clrBkOld = dc.SetBkColor(clrWhite); // create mask CDC dcMask; dcMask.CreateCompatibleDC(dc); CBitmap bmpMask; bmpMask.CreateBitmap(size.cx, size.cy, 1, 1, NULL); HBITMAP hBmpOld1 = dcMask.SelectBitmap(bmpMask); // draw the checkmark transparently int cx = rcDest.right - rcDest.left; int cy = rcDest.bottom - rcDest.top; if(hBmpCheck != NULL) { // build mask based on transparent color dcSource.SetBkColor(m_clrMask); dcMask.SetBkColor(clrBlack); dcMask.SetTextColor(clrWhite); dcMask.BitBlt(0, 0, size.cx, size.cy, dcSource, 0, 0, SRCCOPY); // draw bitmap using the mask dc.BitBlt(rcDest.left, rcDest.top, cx, cy, dcSource, 0, 0, SRCINVERT); dc.BitBlt(rcDest.left, rcDest.top, cx, cy, dcMask, 0, 0, SRCAND); dc.BitBlt(rcDest.left, rcDest.top, cx, cy, dcSource, 0, 0, SRCINVERT); } else { const DWORD ROP_DSno = 0x00BB0226L; const DWORD ROP_DSa = 0x008800C6L; const DWORD ROP_DSo = 0x00EE0086L; const DWORD ROP_DSna = 0x00220326L; // draw mask RECT rcSource = { 0, 0, __min(size.cx, rc.right - rc.left), __min(size.cy, rc.bottom - rc.top) }; dcMask.DrawFrameControl(&rcSource, DFC_MENU, bRadio ? DFCS_MENUBULLET : DFCS_MENUCHECK); // draw shadow if disabled if(!m_bFlatMenus && bDisabled) { // offset by one pixel int x = rcDest.left + 1; int y = rcDest.top + 1; // paint source bitmap const int nColor = COLOR_3DHILIGHT; dcSource.FillRect(&rcSource, nColor); // draw checkmark - special case black and white colors COLORREF clrCheck = ::GetSysColor(nColor); if(clrCheck == clrWhite) { dc.BitBlt(x, y, cx, cy, dcMask, 0, 0, ROP_DSno); dc.BitBlt(x, y, cx, cy, dcSource, 0, 0, ROP_DSa); } else { if(clrCheck != clrBlack) { ATLASSERT(dcSource.GetTextColor() == clrBlack); ATLASSERT(dcSource.GetBkColor() == clrWhite); dcSource.BitBlt(0, 0, size.cx, size.cy, dcMask, 0, 0, ROP_DSna); } dc.BitBlt(x, y, cx, cy, dcMask, 0, 0, ROP_DSa); dc.BitBlt(x, y, cx, cy, dcSource, 0, 0, ROP_DSo); } } // paint source bitmap const int nColor = bDisabled ? COLOR_BTNSHADOW : COLOR_MENUTEXT; dcSource.FillRect(&rcSource, nColor); // draw checkmark - special case black and white colors COLORREF clrCheck = ::GetSysColor(nColor); if(clrCheck == clrWhite) { dc.BitBlt(rcDest.left, rcDest.top, cx, cy, dcMask, 0, 0, ROP_DSno); dc.BitBlt(rcDest.left, rcDest.top, cx, cy, dcSource, 0, 0, ROP_DSa); } else { if(clrCheck != clrBlack) { ATLASSERT(dcSource.GetTextColor() == clrBlack); ATLASSERT(dcSource.GetBkColor() == clrWhite); dcSource.BitBlt(0, 0, size.cx, size.cy, dcMask, 0, 0, ROP_DSna); } dc.BitBlt(rcDest.left, rcDest.top, cx, cy, dcMask, 0, 0, ROP_DSa); dc.BitBlt(rcDest.left, rcDest.top, cx, cy, dcSource, 0, 0, ROP_DSo); } } // restore all dc.SetTextColor(clrTextOld); dc.SetBkColor(clrBkOld); dcSource.SelectBitmap(hBmpOld); dcMask.SelectBitmap(hBmpOld1); if(hBmpCheck == NULL) bmp.DeleteObject(); // draw pushed-in hilight if(!m_bFlatMenus && !bDisabled) { if(rc.right - rc.left > size.cx) ::InflateRect(&rcDest, 1,1); // inflate checkmark by one pixel all around dc.DrawEdge(&rcDest, BDR_SUNKENOUTER, BF_RECT); } return TRUE; } void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) { _MenuItemData* pmd = (_MenuItemData*)lpMeasureItemStruct->itemData; if(pmd->fType & MFT_SEPARATOR) // separator - use half system height and zero width { lpMeasureItemStruct->itemHeight = ::GetSystemMetrics(SM_CYMENU) / 2; lpMeasureItemStruct->itemWidth = 0; } else { // compute size of text - use DrawText with DT_CALCRECT CWindowDC dc(NULL); CFont fontBold; HFONT hOldFont = NULL; if(pmd->fState & MFS_DEFAULT) { // need bold version of font LOGFONT lf = { 0 }; m_fontMenu.GetLogFont(lf); lf.lfWeight += 200; fontBold.CreateFontIndirect(&lf); ATLASSERT(fontBold.m_hFont != NULL); hOldFont = dc.SelectFont(fontBold); } else { hOldFont = dc.SelectFont(m_fontMenu); } RECT rcText = { 0 }; dc.DrawText(pmd->lpstrText, -1, &rcText, DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_CALCRECT); int cx = rcText.right - rcText.left; dc.SelectFont(hOldFont); LOGFONT lf = { 0 }; m_fontMenu.GetLogFont(lf); int cy = lf.lfHeight; if(cy < 0) cy = -cy; const int cyMargin = 8; cy += cyMargin; // height of item is the bigger of these two lpMeasureItemStruct->itemHeight = __max(cy, (int)m_szButton.cy); // width is width of text plus a bunch of stuff cx += 2 * s_kcxTextMargin; // L/R margin for readability cx += s_kcxGap; // space between button and menu text cx += 2 * m_szButton.cx; // button width (L=button; R=empty margin) cx += m_cxExtraSpacing; // extra between item text and accelerator keys // Windows adds 1 to returned value cx -= ::GetSystemMetrics(SM_CXMENUCHECK) - 1; lpMeasureItemStruct->itemWidth = cx; // done deal } } // Implementation - Hook procs static LRESULT CALLBACK CreateHookProc(int nCode, WPARAM wParam, LPARAM lParam) { const int cchClassName = 7; TCHAR szClassName[cchClassName] = { 0 }; if(nCode == HCBT_CREATEWND) { HWND hWndMenu = (HWND)wParam; #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - HCBT_CREATEWND (HWND = %8.8X)\n"), hWndMenu); #endif ::GetClassName(hWndMenu, szClassName, cchClassName); if(!lstrcmp(_T("#32768"), szClassName)) s_pCurrentBar->m_stackMenuWnd.Push(hWndMenu); } else if(nCode == HCBT_DESTROYWND) { HWND hWndMenu = (HWND)wParam; #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - HCBT_DESTROYWND (HWND = %8.8X)\n"), hWndMenu); #endif ::GetClassName(hWndMenu, szClassName, cchClassName); if(!lstrcmp(_T("#32768"), szClassName)) { ATLASSERT(hWndMenu == s_pCurrentBar->m_stackMenuWnd.GetCurrent()); s_pCurrentBar->m_stackMenuWnd.Pop(); } } return ::CallNextHookEx(s_hCreateHook, nCode, wParam, lParam); } static LRESULT CALLBACK MessageHookProc(int nCode, WPARAM wParam, LPARAM lParam) { LPMSG pMsg = (LPMSG)lParam; if(nCode == HC_ACTION && wParam == PM_REMOVE && pMsg->message != GetGetBarMessage() && pMsg->message != WM_FORWARDMSG) { CCommandBarCtrlBase* pCmdBar = NULL; HWND hWnd = pMsg->hwnd; DWORD dwPID = 0; while(pCmdBar == NULL && hWnd != NULL) { pCmdBar = (CCommandBarCtrlBase*)::SendMessage(hWnd, GetGetBarMessage(), (WPARAM)&dwPID, 0L); hWnd = ::GetParent(hWnd); } if(pCmdBar != NULL && dwPID == GetCurrentProcessId()) { pCmdBar->m_hWndHook = pMsg->hwnd; ATLASSERT(pCmdBar->IsCommandBarBase()); if(::IsWindow(pCmdBar->m_hWnd)) pCmdBar->SendMessage(WM_FORWARDMSG, 0, (LPARAM)pMsg); else ATLTRACE2(atlTraceUI, 0, _T("CmdBar - Hook skipping message, can't find command bar!\n")); } } LRESULT lRet = 0; ATLASSERT(s_pmapMsgHook != NULL); if(s_pmapMsgHook != NULL) { DWORD dwThreadID = ::GetCurrentThreadId(); _MsgHookData* pData = s_pmapMsgHook->Lookup(dwThreadID); if(pData != NULL) { lRet = ::CallNextHookEx(pData->hMsgHook, nCode, wParam, lParam); } } return lRet; } // Implementation void DoPopupMenu(int nIndex, bool bAnimate) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - DoPopupMenu, bAnimate = %s\n"), bAnimate ? "true" : "false"); #endif // Menu animation flags #ifndef TPM_VERPOSANIMATION const UINT TPM_VERPOSANIMATION = 0x1000L; #endif #ifndef TPM_NOANIMATION const UINT TPM_NOANIMATION = 0x4000L; #endif T* pT = static_cast(this); // get popup menu and it's position RECT rect = { 0 }; GetItemRect(nIndex, &rect); POINT pt = { rect.left, rect.bottom }; MapWindowPoints(NULL, &pt, 1); MapWindowPoints(NULL, &rect); TPMPARAMS TPMParams = { 0 }; TPMParams.cbSize = sizeof(TPMPARAMS); TPMParams.rcExclude = rect; HMENU hMenuPopup = ::GetSubMenu(m_hMenu, nIndex); ATLASSERT(hMenuPopup != NULL); // get button ID TBBUTTON tbb = { 0 }; GetButton(nIndex, &tbb); int nCmdID = tbb.idCommand; m_nPopBtn = nIndex; // remember current button's index // press button and display popup menu PressButton(nCmdID, TRUE); SetHotItem(nCmdID); pT->DoTrackPopupMenu(hMenuPopup, TPM_LEFTBUTTON | TPM_VERTICAL | TPM_LEFTALIGN | TPM_TOPALIGN | (s_bW2K ? (bAnimate ? TPM_VERPOSANIMATION : TPM_NOANIMATION) : 0), pt.x, pt.y, &TPMParams); PressButton(nCmdID, FALSE); if(::GetFocus() != m_hWnd) SetHotItem(-1); m_nPopBtn = -1; // restore // eat next message if click is on the same button MSG msg = { 0 }; if(::PeekMessage(&msg, m_hWnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_NOREMOVE) && ::PtInRect(&rect, msg.pt)) ::PeekMessage(&msg, m_hWnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE); // check if another popup menu should be displayed if(m_nNextPopBtn != -1) { PostMessage(GetAutoPopupMessage(), m_nNextPopBtn & 0xFFFF); if(!(m_nNextPopBtn & 0xFFFF0000) && !m_bPopupItem) PostMessage(WM_KEYDOWN, VK_DOWN, 0); m_nNextPopBtn = -1; } else { m_bContextMenu = false; // If user didn't hit escape, give focus back if(!m_bEscapePressed) { if(m_bUseKeyboardCues && m_bShowKeyboardCues) m_bAllowKeyboardCues = false; pT->GiveFocusBack(); } else { SetHotItem(nCmdID); SetAnchorHighlight(TRUE); } } } BOOL DoTrackPopupMenu(HMENU hMenu, UINT uFlags, int x, int y, LPTPMPARAMS lpParams = NULL) { CMenuHandle menuPopup = hMenu; CWindowCreateCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CCommandBarCtrlImpl::DoTrackPopupMenu.\n")); ATLASSERT(FALSE); return FALSE; } ATLASSERT(s_hCreateHook == NULL); s_pCurrentBar = static_cast(this); s_hCreateHook = ::SetWindowsHookEx(WH_CBT, CreateHookProc, ModuleHelper::GetModuleInstance(), GetCurrentThreadId()); ATLASSERT(s_hCreateHook != NULL); m_bPopupItem = false; m_bMenuActive = true; BOOL bTrackRet = menuPopup.TrackPopupMenuEx(uFlags, x, y, m_hWnd, lpParams); m_bMenuActive = false; ::UnhookWindowsHookEx(s_hCreateHook); s_hCreateHook = NULL; s_pCurrentBar = NULL; lock.Unlock(); // cleanup - convert menus back to original state #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - TrackPopupMenu - cleanup\n")); #endif ATLASSERT(m_stackMenuWnd.GetSize() == 0); UpdateWindow(); ATL::CWindow wndTL = GetTopLevelParent(); wndTL.UpdateWindow(); // restore the menu items to the previous state for all menus that were converted if(m_bImagesVisible) { HMENU hMenuSav = NULL; while((hMenuSav = m_stackMenuHandle.Pop()) != NULL) { menuPopup = hMenuSav; BOOL bRet = FALSE; // restore state and delete menu item data for(int i = 0; i < menuPopup.GetMenuItemCount(); i++) { CMenuItemInfo mii; mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID; bRet = menuPopup.GetMenuItemInfo(i, TRUE, &mii); ATLASSERT(bRet); _MenuItemData* pMI = (_MenuItemData*)mii.dwItemData; if(pMI != NULL && pMI->IsCmdBarMenuItem()) { mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_STATE; mii.fType = pMI->fType; mii.fState = pMI->fState; mii.dwTypeData = pMI->lpstrText; mii.cch = lstrlen(pMI->lpstrText); mii.dwItemData = NULL; bRet = menuPopup.SetMenuItemInfo(i, TRUE, &mii); // this one triggers WM_MEASUREITEM menuPopup.ModifyMenu(i, MF_BYPOSITION | mii.fType | mii.fState, mii.wID, pMI->lpstrText); ATLASSERT(bRet); delete [] pMI->lpstrText; delete pMI; } } } } return bTrackRet; } int GetPreviousMenuItem(int nBtn) const { if(nBtn == -1) return -1; #if (_WIN32_IE >= 0x0500) RECT rcClient = { 0 }; GetClientRect(&rcClient); #endif // (_WIN32_IE >= 0x0500) int nNextBtn; for(nNextBtn = nBtn - 1; nNextBtn != nBtn; nNextBtn--) { if(nNextBtn < 0) nNextBtn = ::GetMenuItemCount(m_hMenu) - 1; TBBUTTON tbb = { 0 }; GetButton(nNextBtn, &tbb); #if (_WIN32_IE >= 0x0500) RECT rcBtn = { 0 }; GetItemRect(nNextBtn, &rcBtn); if(rcBtn.right > rcClient.right) { nNextBtn = -2; // chevron break; } #endif // (_WIN32_IE >= 0x0500) if((tbb.fsState & TBSTATE_ENABLED) != 0 && (tbb.fsState & TBSTATE_HIDDEN) == 0) break; } return (nNextBtn != nBtn) ? nNextBtn : -1; } int GetNextMenuItem(int nBtn) const { if(nBtn == -1) return -1; #if (_WIN32_IE >= 0x0500) RECT rcClient = { 0 }; GetClientRect(&rcClient); #endif // (_WIN32_IE >= 0x0500) int nNextBtn = 0; int nCount = ::GetMenuItemCount(m_hMenu); for(nNextBtn = nBtn + 1; nNextBtn != nBtn; nNextBtn++) { if(nNextBtn >= nCount) nNextBtn = 0; TBBUTTON tbb = { 0 }; GetButton(nNextBtn, &tbb); #if (_WIN32_IE >= 0x0500) RECT rcBtn = { 0 }; GetItemRect(nNextBtn, &rcBtn); if(rcBtn.right > rcClient.right) { nNextBtn = -2; // chevron break; } #endif // (_WIN32_IE >= 0x0500) if((tbb.fsState & TBSTATE_ENABLED) != 0 && (tbb.fsState & TBSTATE_HIDDEN) == 0) break; } return (nNextBtn != nBtn) ? nNextBtn : -1; } #if (_WIN32_IE >= 0x0500) bool DisplayChevronMenu() { // assume we are in a rebar HWND hWndReBar = GetParent(); int nCount = (int)::SendMessage(hWndReBar, RB_GETBANDCOUNT, 0, 0L); bool bRet = false; for(int i = 0; i < nCount; i++) { REBARBANDINFO rbbi = { RunTimeHelper::SizeOf_REBARBANDINFO(), RBBIM_CHILD | RBBIM_STYLE }; BOOL bRetBandInfo = (BOOL)::SendMessage(hWndReBar, RB_GETBANDINFO, i, (LPARAM)&rbbi); if(bRetBandInfo && rbbi.hwndChild == m_hWnd) { if((rbbi.fStyle & RBBS_USECHEVRON) != 0) { ::PostMessage(hWndReBar, RB_PUSHCHEVRON, i, 0L); PostMessage(WM_KEYDOWN, VK_DOWN, 0L); bRet = true; } break; } } return bRet; } #endif // (_WIN32_IE >= 0x0500) void GetSystemSettings() { // refresh our font NONCLIENTMETRICS info = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() }; BOOL bRet = ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0); ATLASSERT(bRet); if(bRet) { LOGFONT logfont = { 0 }; if(m_fontMenu.m_hFont != NULL) m_fontMenu.GetLogFont(logfont); if(logfont.lfHeight != info.lfMenuFont.lfHeight || logfont.lfWidth != info.lfMenuFont.lfWidth || logfont.lfEscapement != info.lfMenuFont.lfEscapement || logfont.lfOrientation != info.lfMenuFont.lfOrientation || logfont.lfWeight != info.lfMenuFont.lfWeight || logfont.lfItalic != info.lfMenuFont.lfItalic || logfont.lfUnderline != info.lfMenuFont.lfUnderline || logfont.lfStrikeOut != info.lfMenuFont.lfStrikeOut || logfont.lfCharSet != info.lfMenuFont.lfCharSet || logfont.lfOutPrecision != info.lfMenuFont.lfOutPrecision || logfont.lfClipPrecision != info.lfMenuFont.lfClipPrecision || logfont.lfQuality != info.lfMenuFont.lfQuality || logfont.lfPitchAndFamily != info.lfMenuFont.lfPitchAndFamily || lstrcmp(logfont.lfFaceName, info.lfMenuFont.lfFaceName) != 0) { HFONT hFontMenu = ::CreateFontIndirect(&info.lfMenuFont); ATLASSERT(hFontMenu != NULL); if(hFontMenu != NULL) { if(m_fontMenu.m_hFont != NULL) m_fontMenu.DeleteObject(); m_fontMenu.Attach(hFontMenu); SetFont(m_fontMenu); AddStrings(_T("NS\0")); // for proper item height AutoSize(); } } } // check if we need extra spacing for menu item text CWindowDC dc(m_hWnd); HFONT hFontOld = dc.SelectFont(m_fontMenu); RECT rcText = { 0 }; dc.DrawText(_T("\t"), -1, &rcText, DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_CALCRECT); if((rcText.right - rcText.left) < 4) { ::SetRectEmpty(&rcText); dc.DrawText(_T("x"), -1, &rcText, DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_CALCRECT); m_cxExtraSpacing = rcText.right - rcText.left; } else { m_cxExtraSpacing = 0; } dc.SelectFont(hFontOld); // get Windows version #ifndef _versionhelpers_H_INCLUDED_ OSVERSIONINFO ovi = { sizeof(OSVERSIONINFO) }; ::GetVersionEx(&ovi); #endif // !_versionhelpers_H_INCLUDED_ // query keyboard cues mode (Windows 2000 or later) #ifdef _versionhelpers_H_INCLUDED_ if(::IsWindowsVersionOrGreater(5, 0, 0)) #else // !_versionhelpers_H_INCLUDED_ if (ovi.dwMajorVersion >= 5) #endif // _versionhelpers_H_INCLUDED_ { #ifndef SPI_GETKEYBOARDCUES const UINT SPI_GETKEYBOARDCUES = 0x100A; #endif // !SPI_GETKEYBOARDCUES BOOL bRetVal = TRUE; bRet = ::SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &bRetVal, 0); m_bUseKeyboardCues = (bRet && !bRetVal); m_bAllowKeyboardCues = true; ShowKeyboardCues(!m_bUseKeyboardCues); } // query flat menu mode (Windows XP or later) #ifdef _versionhelpers_H_INCLUDED_ if(::IsWindowsXPOrGreater()) #else // !_versionhelpers_H_INCLUDED_ if ((ovi.dwMajorVersion == 5 && ovi.dwMinorVersion >= 1) || (ovi.dwMajorVersion > 5)) #endif // _versionhelpers_H_INCLUDED_ { #ifndef SPI_GETFLATMENU const UINT SPI_GETFLATMENU = 0x1022; #endif // !SPI_GETFLATMENU BOOL bRetVal = FALSE; bRet = ::SystemParametersInfo(SPI_GETFLATMENU, 0, &bRetVal, 0); m_bFlatMenus = (bRet && bRetVal); } #if _WTL_CMDBAR_VISTA_MENUS // check if we should use Vista menus bool bVistaMenus = (((m_dwExtendedStyle & CBR_EX_NOVISTAMENUS) == 0) && RunTimeHelper::IsVista() && RunTimeHelper::IsThemeAvailable()); if(!bVistaMenus && m_bVistaMenus && (m_hMenu != NULL) && (m_arrCommand.GetSize() > 0)) { T* pT = static_cast(this); pT->_RemoveVistaBitmapsFromMenu(); } m_bVistaMenus = bVistaMenus; #endif // _WTL_CMDBAR_VISTA_MENUS #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("CmdBar - GetSystemSettings:\n m_bFlatMenus = %s\n m_bUseKeyboardCues = %s m_bVistaMenus = %s\n"), m_bFlatMenus ? "true" : "false", m_bUseKeyboardCues ? "true" : "false", m_bVistaMenus ? "true" : "false"); #endif } // Implementation - alternate focus mode support void TakeFocus() { if((m_dwExtendedStyle & CBR_EX_ALTFOCUSMODE) && m_hWndFocus == NULL) m_hWndFocus = ::GetFocus(); SetFocus(); } void GiveFocusBack() { if(m_bParentActive) { if((m_dwExtendedStyle & CBR_EX_ALTFOCUSMODE) && ::IsWindow(m_hWndFocus)) ::SetFocus(m_hWndFocus); else if(!(m_dwExtendedStyle & CBR_EX_ALTFOCUSMODE) && m_wndParent.IsWindow()) m_wndParent.SetFocus(); } m_hWndFocus = NULL; SetAnchorHighlight(FALSE); if(m_bUseKeyboardCues && m_bShowKeyboardCues) ShowKeyboardCues(false); m_bSkipPostDown = false; } void ShowKeyboardCues(bool bShow) { m_bShowKeyboardCues = bShow; SetDrawTextFlags(DT_HIDEPREFIX, m_bShowKeyboardCues ? 0 : DT_HIDEPREFIX); Invalidate(); UpdateWindow(); } // Implementation - internal message helpers static UINT GetAutoPopupMessage() { static UINT uAutoPopupMessage = 0; if(uAutoPopupMessage == 0) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CCommandBarCtrlImpl::GetAutoPopupMessage.\n")); ATLASSERT(FALSE); return 0; } if(uAutoPopupMessage == 0) uAutoPopupMessage = ::RegisterWindowMessage(_T("WTL_CmdBar_InternalAutoPopupMsg")); lock.Unlock(); } ATLASSERT(uAutoPopupMessage != 0); return uAutoPopupMessage; } static UINT GetGetBarMessage() { static UINT uGetBarMessage = 0; if(uGetBarMessage == 0) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CCommandBarCtrlImpl::GetGetBarMessage.\n")); ATLASSERT(FALSE); return 0; } if(uGetBarMessage == 0) uGetBarMessage = ::RegisterWindowMessage(_T("WTL_CmdBar_InternalGetBarMsg")); lock.Unlock(); } ATLASSERT(uGetBarMessage != 0); return uGetBarMessage; } // Implementation bool CreateInternalImageList(int cImages) { UINT uFlags = (m_bAlphaImages ? ILC_COLOR32 : ILC_COLOR24) | ILC_MASK; m_hImageList = ::ImageList_Create(m_szBitmap.cx, m_szBitmap.cy, uFlags, cImages, 1); ATLASSERT(m_hImageList != NULL); return (m_hImageList != NULL); } // Implementation - support for Vista menus #if _WTL_CMDBAR_VISTA_MENUS void _AddVistaBitmapsFromImageList(int nStartIndex, int nCount) { // Create display compatible memory DC CClientDC dc(NULL); CDC dcMem; dcMem.CreateCompatibleDC(dc); HBITMAP hBitmapSave = dcMem.GetCurrentBitmap(); T* pT = static_cast(this); // Create bitmaps for all menu items for(int i = 0; i < nCount; i++) { HBITMAP hBitmap = pT->_CreateVistaBitmapHelper(nStartIndex + i, dc, dcMem); dcMem.SelectBitmap(hBitmapSave); m_arrVistaBitmap.Add(hBitmap); } } void _AddVistaBitmapFromImageList(int nIndex) { // Create display compatible memory DC CClientDC dc(NULL); CDC dcMem; dcMem.CreateCompatibleDC(dc); HBITMAP hBitmapSave = dcMem.GetCurrentBitmap(); // Create bitmap for menu item T* pT = static_cast(this); HBITMAP hBitmap = pT->_CreateVistaBitmapHelper(nIndex, dc, dcMem); // Select saved bitmap back and add bitmap to the array dcMem.SelectBitmap(hBitmapSave); m_arrVistaBitmap.Add(hBitmap); } void _ReplaceVistaBitmapFromImageList(int nIndex) { // Delete existing bitmap if(m_arrVistaBitmap[nIndex] != NULL) ::DeleteObject(m_arrVistaBitmap[nIndex]); // Create display compatible memory DC CClientDC dc(NULL); CDC dcMem; dcMem.CreateCompatibleDC(dc); HBITMAP hBitmapSave = dcMem.GetCurrentBitmap(); // Create bitmap for menu item T* pT = static_cast(this); HBITMAP hBitmap = pT->_CreateVistaBitmapHelper(nIndex, dc, dcMem); // Select saved bitmap back and replace bitmap in the array dcMem.SelectBitmap(hBitmapSave); m_arrVistaBitmap.SetAtIndex(nIndex, hBitmap); } HBITMAP _CreateVistaBitmapHelper(int nIndex, HDC hDCSource, HDC hDCTarget) { // Create 32-bit bitmap BITMAPINFO bi = { 0 }; bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth = m_szBitmap.cx; bi.bmiHeader.biHeight = m_szBitmap.cy; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB; bi.bmiHeader.biSizeImage = 0; bi.bmiHeader.biXPelsPerMeter = 0; bi.bmiHeader.biYPelsPerMeter = 0; bi.bmiHeader.biClrUsed = 0; bi.bmiHeader.biClrImportant = 0; HBITMAP hBitmap = ::CreateDIBSection(hDCSource, &bi, DIB_RGB_COLORS, NULL, NULL, 0); ATLASSERT(hBitmap != NULL); // Select bitmap into target DC and draw from image list to it if(hBitmap != NULL) { ::SelectObject(hDCTarget, hBitmap); IMAGELISTDRAWPARAMS ildp = { 0 }; ildp.cbSize = sizeof(IMAGELISTDRAWPARAMS); ildp.himl = m_hImageList; ildp.i = nIndex; ildp.hdcDst = hDCTarget; ildp.x = 0; ildp.y = 0; ildp.cx = 0; ildp.cy = 0; ildp.xBitmap = 0; ildp.yBitmap = 0; ildp.fStyle = ILD_TRANSPARENT; ildp.fState = ILS_ALPHA; ildp.Frame = 255; ::ImageList_DrawIndirect(&ildp); } return hBitmap; } void _RemoveVistaBitmapsFromMenu() { CMenuHandle menu = m_hMenu; for(int i = 0; i < m_arrCommand.GetSize(); i++) { CMenuItemInfo mii; mii.fMask = MIIM_BITMAP; mii.hbmpItem = NULL; menu.SetMenuItemInfo(m_arrCommand[i], FALSE, &mii); } } #endif // _WTL_CMDBAR_VISTA_MENUS }; class CCommandBarCtrl : public CCommandBarCtrlImpl { public: DECLARE_WND_SUPERCLASS(_T("WTL_CommandBar"), GetWndClassName()) }; /////////////////////////////////////////////////////////////////////////////// // CMDICommandBarCtrl - ATL implementation of Command Bars for MDI apps template class ATL_NO_VTABLE CMDICommandBarCtrlImpl : public CCommandBarCtrlImpl< T, TBase, TWinTraits> { public: // Data members ATL::CContainedWindow m_wndMDIClient; bool m_bChildMaximized; HWND m_hWndChildMaximized; HICON m_hIconChildMaximized; int m_nBtnPressed; int m_nBtnWasPressed; int m_cxyOffset; // offset between nonclient elements int m_cxIconWidth; // small icon width int m_cyIconHeight; // small icon height int m_cxBtnWidth; // nonclient button width int m_cyBtnHeight; // nonclient button height int m_cxLeft; // left nonclient area width int m_cxRight; // right nonclient area width // Theme declarations and data members #ifndef _WTL_NO_AUTO_THEME #ifndef _UXTHEME_H_ typedef HANDLE HTHEME; #endif // !_UXTHEME_H_ typedef HTHEME (STDAPICALLTYPE *PFN_OpenThemeData)(HWND hwnd, LPCWSTR pszClassList); typedef HRESULT (STDAPICALLTYPE *PFN_CloseThemeData)(HTHEME hTheme); typedef HRESULT (STDAPICALLTYPE *PFN_DrawThemeBackground)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, OPTIONAL const RECT *pClipRect); typedef HRESULT (STDAPICALLTYPE *PFN_DrawThemeParentBackground)(HWND hwnd, HDC hdc, OPTIONAL RECT* prc); HMODULE m_hThemeDLL; HTHEME m_hTheme; PFN_DrawThemeBackground m_pfnDrawThemeBackground; PFN_DrawThemeParentBackground m_pfnDrawThemeParentBackground; #endif // !_WTL_NO_AUTO_THEME // Constructor/destructor CMDICommandBarCtrlImpl() : m_wndMDIClient(this, 2), m_bChildMaximized(false), m_hWndChildMaximized(NULL), m_hIconChildMaximized(NULL), m_nBtnPressed(-1), m_nBtnWasPressed(-1), #ifndef _WTL_NO_AUTO_THEME m_hThemeDLL(NULL), m_hTheme(NULL), m_pfnDrawThemeBackground(NULL), m_pfnDrawThemeParentBackground(NULL), #endif // !_WTL_NO_AUTO_THEME m_cxyOffset(2), m_cxIconWidth(16), m_cyIconHeight(16), m_cxBtnWidth(16), m_cyBtnHeight(14), m_cxLeft(20), m_cxRight(55) { } ~CMDICommandBarCtrlImpl() { if(m_wndMDIClient.IsWindow()) /*scary!*/ m_wndMDIClient.UnsubclassWindow(); } // Operations BOOL SetMDIClient(HWND hWndMDIClient) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(::IsWindow(hWndMDIClient)); if(!::IsWindow(hWndMDIClient)) return FALSE; #ifdef _DEBUG // BLOCK: Test if the passed window is MDICLIENT { LPCTSTR lpszMDIClientClass = _T("MDICLIENT"); const int nNameLen = 9 + 1; // "MDICLIENT" + NULL TCHAR szClassName[nNameLen] = { 0 }; ::GetClassName(hWndMDIClient, szClassName, nNameLen); ATLASSERT(lstrcmpi(szClassName, lpszMDIClientClass) == 0); } #endif // _DEBUG if(m_wndMDIClient.IsWindow()) /*scary!*/ m_wndMDIClient.UnsubclassWindow(); return m_wndMDIClient.SubclassWindow(hWndMDIClient); } // Message maps typedef CCommandBarCtrlImpl< T, TBase, TWinTraits > _baseClass; BEGIN_MSG_MAP(CMDICommandBarCtrlImpl) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) #ifndef _WTL_NO_AUTO_THEME MESSAGE_HANDLER(_GetThemeChangedMsg(), OnThemeChanged) #endif // !_WTL_NO_AUTO_THEME MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_NCCALCSIZE, OnNcCalcSize) MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint) MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest) MESSAGE_HANDLER(WM_NCLBUTTONDOWN, OnNcLButtonDown) MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp) MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnNcLButtonDblClk) MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged) CHAIN_MSG_MAP(_baseClass) ALT_MSG_MAP(1) // Parent window messages MESSAGE_HANDLER(WM_ACTIVATE, OnParentActivate) CHAIN_MSG_MAP_ALT(_baseClass, 1) ALT_MSG_MAP(2) // MDI client window messages MESSAGE_HANDLER(WM_MDISETMENU, OnMDISetMenu) // no chaining needed since this was moved from the base class here ALT_MSG_MAP(3) // Message hook messages MESSAGE_RANGE_HANDLER(0, 0xFFFF, OnAllHookMessages) CHAIN_MSG_MAP_ALT(_baseClass, 3) END_MSG_MAP() // Additional MDI message handlers LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { LRESULT lRet = _baseClass::OnCreate(uMsg, wParam, lParam, bHandled); if(lRet == (LRESULT)-1) return lRet; #ifndef _WTL_NO_AUTO_THEME // this will fail if theming is not supported m_hThemeDLL = ::LoadLibrary(_T("uxtheme.dll")); if(m_hThemeDLL != NULL) { m_pfnDrawThemeBackground = (PFN_DrawThemeBackground)::GetProcAddress(m_hThemeDLL, "DrawThemeBackground"); ATLASSERT(m_pfnDrawThemeBackground != NULL); if(m_pfnDrawThemeBackground != NULL) { T* pT = static_cast(this); pT->_OpenThemeData(); } else { ::FreeLibrary(m_hThemeDLL); m_hThemeDLL = NULL; } m_pfnDrawThemeParentBackground = (PFN_DrawThemeParentBackground)::GetProcAddress(m_hThemeDLL, "DrawThemeParentBackground"); ATLASSERT(m_pfnDrawThemeParentBackground != NULL); } #endif // !_WTL_NO_AUTO_THEME return lRet; } LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { LRESULT lRet = _baseClass::OnDestroy(uMsg, wParam, lParam, bHandled); #ifndef _WTL_NO_AUTO_THEME if(m_hThemeDLL != NULL) { T* pT = static_cast(this); pT->_CloseThemeData(); ::FreeLibrary(m_hThemeDLL); m_hThemeDLL = NULL; } #endif // !_WTL_NO_AUTO_THEME return lRet; } #ifndef _WTL_NO_AUTO_THEME LRESULT OnThemeChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { if(m_hThemeDLL != NULL) { T* pT = static_cast(this); pT->_CloseThemeData(); pT->_OpenThemeData(); } return 0; } #endif // !_WTL_NO_AUTO_THEME LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { LRESULT lRet = DefWindowProc(uMsg, wParam, lParam); T* pT = static_cast(this); pT->_AdjustBtnSize(GET_Y_LPARAM(lParam)); return lRet; } LRESULT OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { LRESULT lRet = DefWindowProc(uMsg, wParam, lParam); if(m_bChildMaximized && (BOOL)wParam) { LPNCCALCSIZE_PARAMS lpParams = (LPNCCALCSIZE_PARAMS)lParam; if(m_bLayoutRTL) { lpParams->rgrc[0].left += m_cxRight; lpParams->rgrc[0].right -= m_cxLeft; } else { lpParams->rgrc[0].left += m_cxLeft; lpParams->rgrc[0].right -= m_cxRight; } } return lRet; } LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { LRESULT lRet = DefWindowProc(uMsg, wParam, lParam); if(!m_bChildMaximized) return lRet; ATLASSERT(m_hWndChildMaximized != NULL && m_hIconChildMaximized != NULL); // get DC and window rectangle CWindowDC dc(m_hWnd); RECT rect = { 0 }; GetWindowRect(&rect); int cxWidth = rect.right - rect.left; int cyHeight = rect.bottom - rect.top; // paint left side nonclient background and draw icon ::SetRect(&rect, 0, 0, m_cxLeft, cyHeight); #ifndef _WTL_NO_AUTO_THEME if(m_hTheme != NULL) { if(m_pfnDrawThemeParentBackground != NULL) m_pfnDrawThemeParentBackground(m_hWnd, dc, &rect); else dc.FillRect(&rect, COLOR_WINDOW); } else #endif // !_WTL_NO_AUTO_THEME { if((m_dwExtendedStyle & CBR_EX_TRANSPARENT) != 0) dc.FillRect(&rect, COLOR_3DFACE); else dc.FillRect(&rect, COLOR_MENU); } RECT rcIcon = { 0 }; T* pT = static_cast(this); pT->_CalcIconRect(cxWidth, cyHeight, rcIcon); dc.DrawIconEx(rcIcon.left, rcIcon.top, m_hIconChildMaximized, m_cxIconWidth, m_cyIconHeight); // paint right side nonclient background ::SetRect(&rect, cxWidth - m_cxRight, 0, cxWidth, cyHeight); #ifndef _WTL_NO_AUTO_THEME if(m_hTheme != NULL) { if(m_pfnDrawThemeParentBackground != NULL) { // this is to account for the left non-client area POINT ptOrg = { 0, 0 }; dc.GetViewportOrg(&ptOrg); dc.SetViewportOrg(ptOrg.x + m_cxLeft, ptOrg.y); ::OffsetRect(&rect, -m_cxLeft, 0); m_pfnDrawThemeParentBackground(m_hWnd, dc, &rect); // restore dc.SetViewportOrg(ptOrg); ::OffsetRect(&rect, m_cxLeft, 0); } else { dc.FillRect(&rect, COLOR_3DFACE); } } else #endif // !_WTL_NO_AUTO_THEME { if((m_dwExtendedStyle & CBR_EX_TRANSPARENT) != 0) dc.FillRect(&rect, COLOR_3DFACE); else dc.FillRect(&rect, COLOR_MENU); } // draw buttons RECT arrRect[3] = { 0 }; pT->_CalcBtnRects(cxWidth, cyHeight, arrRect); pT->_DrawMDIButton(dc, arrRect, -1); // draw all buttons return lRet; } LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { LRESULT lRet = DefWindowProc(uMsg, wParam, lParam); if(m_bChildMaximized) { RECT rect = { 0 }; GetWindowRect(&rect); POINT pt = { GET_X_LPARAM(lParam) - rect.left, GET_Y_LPARAM(lParam) - rect.top }; if(m_bLayoutRTL) { if((pt.x < m_cxRight) || (pt.x > ((rect.right - rect.left) - m_cxLeft))) lRet = HTBORDER; } else { if((pt.x < m_cxLeft) || (pt.x > ((rect.right - rect.left) - m_cxRight))) lRet = HTBORDER; } } return lRet; } LRESULT OnNcLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { if(!m_bChildMaximized) { bHandled = FALSE; return 1; } ATLASSERT(_DebugCheckChild()); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; RECT rect = { 0 }; GetWindowRect(&rect); pt.x -= rect.left; pt.y -= rect.top; RECT rcIcon = { 0 }; T* pT = static_cast(this); pT->_CalcIconRect(rect.right - rect.left, rect.bottom - rect.top, rcIcon, m_bLayoutRTL); RECT arrRect[3] = { 0 }; pT->_CalcBtnRects(rect.right - rect.left, rect.bottom - rect.top, arrRect, m_bLayoutRTL); if(::PtInRect(&rcIcon, pt)) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("MDI CmdBar - LButtonDown: icon\n")); #endif #ifndef TPM_VERPOSANIMATION const UINT TPM_VERPOSANIMATION = 0x1000L; // Menu animation flag #endif CMenuHandle menu = ::GetSystemMenu(m_hWndChildMaximized, FALSE); UINT uRet = (UINT)menu.TrackPopupMenu(TPM_LEFTBUTTON | TPM_VERTICAL | TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | (s_bW2K ? TPM_VERPOSANIMATION : 0), m_bLayoutRTL ? rect.right : rect.left, rect.bottom, m_hWndChildMaximized); // eat next message if click is on the same button ::OffsetRect(&rcIcon, rect.left, rect.top); MSG msg = { 0 }; if(::PeekMessage(&msg, m_hWnd, WM_NCLBUTTONDOWN, WM_NCLBUTTONDOWN, PM_NOREMOVE) && ::PtInRect(&rcIcon, msg.pt)) ::PeekMessage(&msg, m_hWnd, WM_NCLBUTTONDOWN, WM_NCLBUTTONDOWN, PM_REMOVE); if(uRet != 0) ::SendMessage(m_hWndChildMaximized, WM_SYSCOMMAND, uRet, 0L); } else if(::PtInRect(&arrRect[0], pt)) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("MDI CmdBar - LButtonDown: close button\n")); #endif m_nBtnWasPressed = m_nBtnPressed = 0; } else if(::PtInRect(&arrRect[1], pt)) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("MDI CmdBar - LButtonDown: restore button\n")); #endif m_nBtnWasPressed = m_nBtnPressed = 1; } else if(::PtInRect(&arrRect[2], pt)) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("MDI CmdBar - LButtonDown: minimize button\n")); #endif m_nBtnWasPressed = m_nBtnPressed = 2; } else { bHandled = FALSE; } // draw the button state if it was pressed if(m_nBtnPressed != -1) { SetCapture(); CWindowDC dc(m_hWnd); pT->_CalcBtnRects(rect.right - rect.left, rect.bottom - rect.top, arrRect); pT->_DrawMDIButton(dc, arrRect, m_nBtnPressed); } return 0; } LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { if(!m_bChildMaximized || ::GetCapture() != m_hWnd || m_nBtnWasPressed == -1) { bHandled = FALSE; return 1; } POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; ClientToScreen(&pt); RECT rect = { 0 }; GetWindowRect(&rect); pt.x -= rect.left; pt.y -= rect.top; RECT arrRect[3] = { 0 }; T* pT = static_cast(this); pT->_CalcBtnRects(rect.right - rect.left, rect.bottom - rect.top, arrRect, m_bLayoutRTL); int nOldBtnPressed = m_nBtnPressed; m_nBtnPressed = ::PtInRect(&arrRect[m_nBtnWasPressed], pt) ? m_nBtnWasPressed : -1; if(nOldBtnPressed != m_nBtnPressed) { CWindowDC dc(m_hWnd); pT->_CalcBtnRects(rect.right - rect.left, rect.bottom - rect.top, arrRect); pT->_DrawMDIButton(dc, arrRect, (m_nBtnPressed != -1) ? m_nBtnPressed : nOldBtnPressed); } return 0; } LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { if(!m_bChildMaximized || ::GetCapture() != m_hWnd || m_nBtnWasPressed == -1) { bHandled = FALSE; return 1; } ATLASSERT(_DebugCheckChild()); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; ClientToScreen(&pt); RECT rect = { 0 }; GetWindowRect(&rect); pt.x -= rect.left; pt.y -= rect.top; int nBtn = m_nBtnWasPressed; ReleaseCapture(); RECT arrRect[3] = { 0 }; T* pT = static_cast(this); pT->_CalcBtnRects(rect.right - rect.left, rect.bottom - rect.top, arrRect, m_bLayoutRTL); if(::PtInRect(&arrRect[nBtn], pt)) { switch(nBtn) { case 0: // close #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("MDI CmdBar - LButtonUp: close button\n")); #endif ::SendMessage(m_hWndChildMaximized, WM_SYSCOMMAND, SC_CLOSE, 0L); break; case 1: // restore #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("MDI CmdBar - LButtonUp: restore button\n")); #endif ::SendMessage(m_hWndChildMaximized, WM_SYSCOMMAND, SC_RESTORE, 0L); break; case 2: // minimize #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("MDI CmdBar - LButtonUp: minimize button\n")); #endif ::SendMessage(m_hWndChildMaximized, WM_SYSCOMMAND, SC_MINIMIZE, 0L); break; default: break; } } return 0; } LRESULT OnNcLButtonDblClk(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { if(!m_bChildMaximized || m_nBtnWasPressed != -1) { bHandled = FALSE; return 1; } ATLASSERT(_DebugCheckChild()); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; RECT rect = { 0 }; GetWindowRect(&rect); pt.x -= rect.left; pt.y -= rect.top; RECT rcIcon = { 0 }; T* pT = static_cast(this); pT->_CalcIconRect(rect.right - rect.left, rect.bottom - rect.top, rcIcon, m_bLayoutRTL); RECT arrRect[3] = { 0 }; pT->_CalcBtnRects(rect.right - rect.left, rect.bottom - rect.top, arrRect, m_bLayoutRTL); if(::PtInRect(&rcIcon, pt)) { CMenuHandle menu = ::GetSystemMenu(m_hWndChildMaximized, FALSE); UINT uDefID = menu.GetMenuDefaultItem(); if(uDefID == (UINT)-1) uDefID = SC_CLOSE; ::SendMessage(m_hWndChildMaximized, WM_SYSCOMMAND, uDefID, 0L); } return 0; } LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(m_bChildMaximized) { if(m_nBtnPressed != -1) { ATLASSERT(m_nBtnPressed == m_nBtnWasPressed); // must be m_nBtnPressed = -1; RECT rect = { 0 }; GetWindowRect(&rect); RECT arrRect[3] = { 0 }; T* pT = static_cast(this); pT->_CalcBtnRects(rect.right - rect.left, rect.bottom - rect.top, arrRect); CWindowDC dc(m_hWnd); pT->_DrawMDIButton(dc, arrRect, m_nBtnWasPressed); } m_nBtnWasPressed = -1; } else { bHandled = FALSE; } return 0; } // Parent window message handlers LRESULT OnParentActivate(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { m_bParentActive = (LOWORD(wParam) != WA_INACTIVE); RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); bHandled = FALSE; return 1; } // MDI client window message handlers LRESULT OnMDISetMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { m_wndMDIClient.DefWindowProc(uMsg, NULL, lParam); HMENU hOldMenu = GetMenu(); BOOL bRet = AttachMenu((HMENU)wParam); bRet; // avoid level 4 warning ATLASSERT(bRet); #if (_WIN32_IE >= 0x0400) T* pT = static_cast(this); pT->UpdateRebarBandIdealSize(); #endif // (_WIN32_IE >= 0x0400) return (LRESULT)hOldMenu; } // All messages from the message hook LRESULT OnAllHookMessages(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { T* pT = static_cast(this); pT->_ProcessAllHookMessages(uMsg, wParam, lParam); bHandled = FALSE; return 1; } // Overrideables // override this to provide different ideal size void UpdateRebarBandIdealSize() { // assuming we are in a rebar, change ideal size to our size // we hope that if we are not in a rebar, nCount will be 0 int nCount = (int)::SendMessage(GetParent(), RB_GETBANDCOUNT, 0, 0L); for(int i = 0; i < nCount; i++) { REBARBANDINFO rbi = { RunTimeHelper::SizeOf_REBARBANDINFO(), RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE }; ::SendMessage(GetParent(), RB_GETBANDINFO, i, (LPARAM)&rbi); if(rbi.hwndChild == m_hWnd) { rbi.fMask = RBBIM_IDEALSIZE; rbi.cxIdeal = m_bChildMaximized ? m_cxLeft + m_cxRight : 0; int nBtnCount = GetButtonCount(); if(nBtnCount > 0) { RECT rect = { 0 }; GetItemRect(nBtnCount - 1, &rect); rbi.cxIdeal += rect.right; } ::SendMessage(GetParent(), RB_SETBANDINFO, i, (LPARAM)&rbi); break; } } } // all hook messages - check for the maximized MDI child window change void _ProcessAllHookMessages(UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/) { if(uMsg == WM_MDIGETACTIVE || uMsg == WM_MDISETMENU) return; BOOL bMaximized = FALSE; HWND hWndChild = (HWND)::SendMessage(m_wndMDIClient, WM_MDIGETACTIVE, 0, (LPARAM)&bMaximized); bool bMaxOld = m_bChildMaximized; m_bChildMaximized = (hWndChild != NULL && bMaximized); HICON hIconOld = m_hIconChildMaximized; if(m_bChildMaximized) { if(m_hWndChildMaximized != hWndChild) { ATL::CWindow wnd = m_hWndChildMaximized = hWndChild; m_hIconChildMaximized = wnd.GetIcon(FALSE); if(m_hIconChildMaximized == NULL) { m_hIconChildMaximized = wnd.GetIcon(TRUE); if(m_hIconChildMaximized == NULL) { // no icon set with WM_SETICON, get the class one // need conditional code because types don't match in winuser.h #ifdef _WIN64 m_hIconChildMaximized = (HICON)::GetClassLongPtr(wnd, GCLP_HICONSM); #else m_hIconChildMaximized = (HICON)LongToHandle(::GetClassLongPtr(wnd, GCLP_HICONSM)); #endif } } } } else { m_hWndChildMaximized = NULL; m_hIconChildMaximized = NULL; } if(bMaxOld != m_bChildMaximized) { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("MDI CmdBar - All messages hook change: m_bChildMaximized = %s\n"), m_bChildMaximized ? "true" : "false"); #endif // assuming we are in a rebar, change our size to accomodate new state // we hope that if we are not in a rebar, nCount will be 0 int nCount = (int)::SendMessage(GetParent(), RB_GETBANDCOUNT, 0, 0L); int cxDiff = (m_bChildMaximized ? 1 : -1) * (m_cxLeft + m_cxRight); for(int i = 0; i < nCount; i++) { #if (_WIN32_IE >= 0x0500) REBARBANDINFO rbi = { RunTimeHelper::SizeOf_REBARBANDINFO(), RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_STYLE }; ::SendMessage(GetParent(), RB_GETBANDINFO, i, (LPARAM)&rbi); if(rbi.hwndChild == m_hWnd) { if((rbi.fStyle & RBBS_USECHEVRON) != 0) { rbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE; rbi.cxMinChild += cxDiff; rbi.cxIdeal += cxDiff; ::SendMessage(GetParent(), RB_SETBANDINFO, i, (LPARAM)&rbi); } break; } #elif (_WIN32_IE >= 0x0400) REBARBANDINFO rbi = { RunTimeHelper::SizeOf_REBARBANDINFO(), RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE }; ::SendMessage(GetParent(), RB_GETBANDINFO, i, (LPARAM)&rbi); if(rbi.hwndChild == m_hWnd) { rbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE; rbi.cxMinChild += cxDiff; rbi.cxIdeal += cxDiff; ::SendMessage(GetParent(), RB_SETBANDINFO, i, (LPARAM)&rbi); break; } #else // (_WIN32_IE < 0x0400) REBARBANDINFO rbi = { RunTimeHelper::SizeOf_REBARBANDINFO(), RBBIM_CHILD | RBBIM_CHILDSIZE }; ::SendMessage(GetParent(), RB_GETBANDINFO, i, (LPARAM)&rbi); if(rbi.hwndChild == m_hWnd) { rbi.fMask = RBBIM_CHILDSIZE; rbi.cxMinChild += cxDiff; ::SendMessage(GetParent(), RB_SETBANDINFO, i, (LPARAM)&rbi); break; } #endif // (_WIN32_IE < 0x0400) } } if(bMaxOld != m_bChildMaximized || hIconOld != m_hIconChildMaximized) { // force size change and redraw everything RECT rect = { 0 }; GetWindowRect(&rect); ::MapWindowPoints(NULL, GetParent(), (LPPOINT)&rect, 2); SetRedraw(FALSE); SetWindowPos(NULL, 0, 0, 1, 1, SWP_NOZORDER | SWP_NOMOVE); SetWindowPos(NULL, &rect, SWP_NOZORDER | SWP_NOMOVE); SetRedraw(TRUE); RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW); } } // Implementation void GetSystemSettings() { #ifdef _CMDBAR_EXTRA_TRACE ATLTRACE2(atlTraceUI, 0, _T("MDI CmdBar - GetSystemSettings\n")); #endif _baseClass::GetSystemSettings(); NONCLIENTMETRICS info = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() }; BOOL bRet = ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0); ATLASSERT(bRet); if(bRet) { m_cxIconWidth = ::GetSystemMetrics(SM_CXSMICON); m_cyIconHeight = ::GetSystemMetrics(SM_CYSMICON); m_cxLeft = m_cxIconWidth; #ifndef _WTL_NO_AUTO_THEME if(m_hTheme != NULL) { m_cxBtnWidth = info.iCaptionWidth - 2 * m_cxyOffset; m_cyBtnHeight = info.iCaptionHeight - 2 * m_cxyOffset; m_cxRight = 3 * m_cxBtnWidth; } else #endif // !_WTL_NO_AUTO_THEME { m_cxBtnWidth = info.iCaptionWidth - m_cxyOffset; m_cyBtnHeight = info.iCaptionHeight - 2 * m_cxyOffset; m_cxRight = 3 * m_cxBtnWidth + m_cxyOffset; } } RECT rect = { 0 }; GetClientRect(&rect); T* pT = static_cast(this); pT->_AdjustBtnSize(rect.bottom); } void _AdjustBtnSize(int cyHeight) { if(cyHeight > 1 && m_cyBtnHeight > cyHeight) { #ifndef _WTL_NO_AUTO_THEME if(m_hTheme != NULL) { m_cyBtnHeight = cyHeight; m_cxBtnWidth = cyHeight; m_cxRight = 3 * m_cxBtnWidth; } else #endif // !_WTL_NO_AUTO_THEME { m_cyBtnHeight = cyHeight; m_cxBtnWidth = cyHeight + m_cxyOffset; m_cxRight = 3 * m_cxBtnWidth + m_cxyOffset; } } } void _CalcIconRect(int cxWidth, int cyHeight, RECT& rect, bool bInvertX = false) const { int xStart = (m_cxLeft - m_cxIconWidth) / 2; if(xStart < 0) xStart = 0; int yStart = (cyHeight - m_cyIconHeight) / 2; if(yStart < 0) yStart = 0; if(bInvertX) ::SetRect(&rect, cxWidth - (xStart + m_cxBtnWidth), yStart, cxWidth - xStart, yStart + m_cyBtnHeight); else ::SetRect(&rect, xStart, yStart, xStart + m_cxBtnWidth, yStart + m_cyBtnHeight); } void _CalcBtnRects(int cxWidth, int cyHeight, RECT arrRect[3], bool bInvertX = false) const { int yStart = (cyHeight - m_cyBtnHeight) / 2; if(yStart < 0) yStart = 0; RECT rcBtn = { cxWidth - m_cxBtnWidth, yStart, cxWidth, yStart + m_cyBtnHeight }; int nDirection = -1; if(bInvertX) { ::SetRect(&rcBtn, 0, yStart, m_cxBtnWidth, yStart + m_cyBtnHeight); nDirection = 1; } arrRect[0] = rcBtn; #ifndef _WTL_NO_AUTO_THEME if(m_hTheme != NULL) ::OffsetRect(&rcBtn, nDirection * m_cxBtnWidth, 0); else #endif // !_WTL_NO_AUTO_THEME ::OffsetRect(&rcBtn, nDirection * (m_cxBtnWidth + m_cxyOffset), 0); arrRect[1] = rcBtn; ::OffsetRect(&rcBtn, nDirection * m_cxBtnWidth, 0); arrRect[2] = rcBtn; } void _DrawMDIButton(CWindowDC& dc, LPRECT pRects, int nBtn) { #ifndef _WTL_NO_AUTO_THEME if(m_hTheme != NULL) { #ifndef TMSCHEMA_H const int WP_MDICLOSEBUTTON = 20; const int CBS_NORMAL = 1; const int CBS_PUSHED = 3; const int CBS_DISABLED = 4; const int WP_MDIRESTOREBUTTON = 22; const int RBS_NORMAL = 1; const int RBS_PUSHED = 3; const int RBS_DISABLED = 4; const int WP_MDIMINBUTTON = 16; const int MINBS_NORMAL = 1; const int MINBS_PUSHED = 3; const int MINBS_DISABLED = 4; #endif // TMSCHEMA_H if(nBtn == -1 || nBtn == 0) m_pfnDrawThemeBackground(m_hTheme, dc, WP_MDICLOSEBUTTON, m_bParentActive ? ((m_nBtnPressed == 0) ? CBS_PUSHED : CBS_NORMAL) : CBS_DISABLED, &pRects[0], NULL); if(nBtn == -1 || nBtn == 1) m_pfnDrawThemeBackground(m_hTheme, dc, WP_MDIRESTOREBUTTON, m_bParentActive ? ((m_nBtnPressed == 1) ? RBS_PUSHED : RBS_NORMAL) : RBS_DISABLED, &pRects[1], NULL); if(nBtn == -1 || nBtn == 2) m_pfnDrawThemeBackground(m_hTheme, dc, WP_MDIMINBUTTON, m_bParentActive ? ((m_nBtnPressed == 2) ? MINBS_PUSHED : MINBS_NORMAL) : MINBS_DISABLED, &pRects[2], NULL); } else #endif // !_WTL_NO_AUTO_THEME { if(nBtn == -1 || nBtn == 0) dc.DrawFrameControl(&pRects[0], DFC_CAPTION, DFCS_CAPTIONCLOSE | ((m_nBtnPressed == 0) ? DFCS_PUSHED : 0)); if(nBtn == -1 || nBtn == 1) dc.DrawFrameControl(&pRects[1], DFC_CAPTION, DFCS_CAPTIONRESTORE | ((m_nBtnPressed == 1) ? DFCS_PUSHED : 0)); if(nBtn == -1 || nBtn == 2) dc.DrawFrameControl(&pRects[2], DFC_CAPTION, DFCS_CAPTIONMIN | ((m_nBtnPressed == 2) ? DFCS_PUSHED : 0)); } } #ifndef _WTL_NO_AUTO_THEME static UINT _GetThemeChangedMsg() { #ifndef WM_THEMECHANGED static const UINT WM_THEMECHANGED = 0x031A; #endif // !WM_THEMECHANGED return WM_THEMECHANGED; } void _OpenThemeData() { ATLASSERT(m_hThemeDLL != NULL); PFN_OpenThemeData pfnOpenThemeData = (PFN_OpenThemeData)::GetProcAddress(m_hThemeDLL, "OpenThemeData"); ATLASSERT(pfnOpenThemeData != NULL); if(pfnOpenThemeData != NULL) m_hTheme = pfnOpenThemeData(m_hWnd, L"Window"); } void _CloseThemeData() { ATLASSERT(m_hThemeDLL != NULL); if(m_hTheme == NULL) return; // nothing to do PFN_CloseThemeData pfnCloseThemeData = (PFN_CloseThemeData)::GetProcAddress(m_hThemeDLL, "CloseThemeData"); ATLASSERT(pfnCloseThemeData != NULL); if(pfnCloseThemeData != NULL) { pfnCloseThemeData(m_hTheme); m_hTheme = NULL; } } #endif // !_WTL_NO_AUTO_THEME bool _DebugCheckChild() { #ifdef _DEBUG BOOL bMaximized = FALSE; HWND hWndChild = (HWND)::SendMessage(m_wndMDIClient, WM_MDIGETACTIVE, 0, (LPARAM)&bMaximized); return (bMaximized && hWndChild == m_hWndChildMaximized); #else // !_DEBUG return true; #endif // !_DEBUG } }; class CMDICommandBarCtrl : public CMDICommandBarCtrlImpl { public: DECLARE_WND_SUPERCLASS(_T("WTL_MDICommandBar"), GetWndClassName()) }; }; // namespace WTL #endif // __ATLCTRLW_H__ ================================================ FILE: src/Setup/wtl90/atlctrlx.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLCTRLX_H__ #define __ATLCTRLX_H__ #pragma once #ifndef __ATLAPP_H__ #error atlctrlx.h requires atlapp.h to be included first #endif #ifndef __ATLCTRLS_H__ #error atlctrlx.h requires atlctrls.h to be included first #endif #ifndef WM_UPDATEUISTATE #define WM_UPDATEUISTATE 0x0128 #endif // !WM_UPDATEUISTATE /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CBitmapButtonImpl // CBitmapButton // CCheckListViewCtrlImpl // CCheckListViewCtrl // CHyperLinkImpl // CHyperLink // CWaitCursor // CCustomWaitCursor // CMultiPaneStatusBarCtrlImpl // CMultiPaneStatusBarCtrl // CPaneContainerImpl // CPaneContainer // CSortListViewImpl // CSortListViewCtrlImpl // CSortListViewCtrl // CTabViewImpl // CTabView namespace WTL { /////////////////////////////////////////////////////////////////////////////// // CBitmapButton - bitmap button implementation #ifndef _WIN32_WCE // bitmap button extended styles #define BMPBTN_HOVER 0x00000001 #define BMPBTN_AUTO3D_SINGLE 0x00000002 #define BMPBTN_AUTO3D_DOUBLE 0x00000004 #define BMPBTN_AUTOSIZE 0x00000008 #define BMPBTN_SHAREIMAGELISTS 0x00000010 #define BMPBTN_AUTOFIRE 0x00000020 #define BMPBTN_CHECK 0x00000040 #define BMPBTN_AUTOCHECK 0x00000080 // Note: BMPBTN_CHECK/BMPBTN_AUTOCHECK disables BN_DOUBLECLICKED, // BMPBTN_AUTOFIRE doesn't work with BMPBTN_CHECK/BMPBTN_AUTOCHECK template class ATL_NO_VTABLE CBitmapButtonImpl : public ATL::CWindowImpl< T, TBase, TWinTraits > { public: DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName()) enum { _nImageNormal = 0, _nImagePushed, _nImageFocusOrHover, _nImageDisabled, _nImageCount = 4, }; enum { ID_TIMER_FIRST = 1000, ID_TIMER_REPEAT = 1001 }; // Bitmap button specific extended styles DWORD m_dwExtendedStyle; CImageList m_ImageList; int m_nImage[_nImageCount]; CToolTipCtrl m_tip; LPTSTR m_lpstrToolTipText; // Internal states unsigned m_fMouseOver:1; unsigned m_fFocus:1; unsigned m_fPressed:1; unsigned m_fChecked:1; // Constructor/Destructor CBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) : m_dwExtendedStyle(dwExtendedStyle), m_ImageList(hImageList), m_lpstrToolTipText(NULL), m_fMouseOver(0), m_fFocus(0), m_fPressed(0), m_fChecked(0) { m_nImage[_nImageNormal] = -1; m_nImage[_nImagePushed] = -1; m_nImage[_nImageFocusOrHover] = -1; m_nImage[_nImageDisabled] = -1; #ifdef _DEBUG if(((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0) && IsCheckMode()) ATLTRACE2(atlTraceUI, 0, _T("CBitmapButtonImpl - Check mode and BMPBTN_AUTOFIRE cannot be used together, BMPBTN_AUTOFIRE will be ignored.\n")); #endif // _DEBUG } ~CBitmapButtonImpl() { if((m_dwExtendedStyle & BMPBTN_SHAREIMAGELISTS) == 0) m_ImageList.Destroy(); delete [] m_lpstrToolTipText; } // overridden to provide proper initialization BOOL SubclassWindow(HWND hWnd) { #if (_MSC_VER >= 1300) BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); #else // !(_MSC_VER >= 1300) typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass; BOOL bRet = _baseClass::SubclassWindow(hWnd); #endif // !(_MSC_VER >= 1300) if(bRet != FALSE) { T* pT = static_cast(this); pT->Init(); } return bRet; } // Attributes DWORD GetBitmapButtonExtendedStyle() const { return m_dwExtendedStyle; } DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0) { DWORD dwPrevStyle = m_dwExtendedStyle; if(dwMask == 0) m_dwExtendedStyle = dwExtendedStyle; else m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask); #ifdef _DEBUG if(((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0) && IsCheckMode()) ATLTRACE2(atlTraceUI, 0, _T("CBitmapButtonImpl - Check mode and BMPBTN_AUTOFIRE cannot be used together, BMPBTN_AUTOFIRE will be ignored.\n")); #endif // _DEBUG return dwPrevStyle; } HIMAGELIST GetImageList() const { return m_ImageList; } HIMAGELIST SetImageList(HIMAGELIST hImageList) { HIMAGELIST hImageListPrev = m_ImageList; m_ImageList = hImageList; if((m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0 && ::IsWindow(m_hWnd)) SizeToImage(); return hImageListPrev; } int GetToolTipTextLength() const { return (m_lpstrToolTipText == NULL) ? -1 : lstrlen(m_lpstrToolTipText); } bool GetToolTipText(LPTSTR lpstrText, int nLength) const { ATLASSERT(lpstrText != NULL); if(m_lpstrToolTipText == NULL) return false; errno_t nRet = SecureHelper::strncpy_x(lpstrText, nLength, m_lpstrToolTipText, _TRUNCATE); return (nRet == 0 || nRet == STRUNCATE); } bool SetToolTipText(LPCTSTR lpstrText) { if(m_lpstrToolTipText != NULL) { delete [] m_lpstrToolTipText; m_lpstrToolTipText = NULL; } if(lpstrText == NULL) { if(m_tip.IsWindow()) m_tip.Activate(FALSE); return true; } int cchLen = lstrlen(lpstrText) + 1; ATLTRY(m_lpstrToolTipText = new TCHAR[cchLen]); if(m_lpstrToolTipText == NULL) return false; SecureHelper::strcpy_x(m_lpstrToolTipText, cchLen, lpstrText); if(m_tip.IsWindow()) { m_tip.Activate(TRUE); m_tip.AddTool(m_hWnd, m_lpstrToolTipText); } return true; } bool GetCheck() const { return (m_fChecked == 1); } void SetCheck(bool bCheck, bool bUpdate = true) { m_fChecked = bCheck ? 1 : 0; if(bUpdate) { Invalidate(); UpdateWindow(); } } // Operations void SetImages(int nNormal, int nPushed = -1, int nFocusOrHover = -1, int nDisabled = -1) { if(nNormal != -1) m_nImage[_nImageNormal] = nNormal; if(nPushed != -1) m_nImage[_nImagePushed] = nPushed; if(nFocusOrHover != -1) m_nImage[_nImageFocusOrHover] = nFocusOrHover; if(nDisabled != -1) m_nImage[_nImageDisabled] = nDisabled; } BOOL SizeToImage() { ATLASSERT(::IsWindow(m_hWnd) && m_ImageList.m_hImageList != NULL); int cx = 0; int cy = 0; if(!m_ImageList.GetIconSize(cx, cy)) return FALSE; return ResizeClient(cx, cy); } // Overrideables void DoPaint(CDCHandle dc) { ATLASSERT(m_ImageList.m_hImageList != NULL); // image list must be set ATLASSERT(m_nImage[0] != -1); // main bitmap must be set // set bitmap according to the current button state bool bHover = IsHoverMode(); bool bPressed = (m_fPressed == 1) || (IsCheckMode() && (m_fChecked == 1)); int nImage = -1; if(!IsWindowEnabled()) nImage = m_nImage[_nImageDisabled]; else if(bPressed) nImage = m_nImage[_nImagePushed]; else if((!bHover && (m_fFocus == 1)) || (bHover && (m_fMouseOver == 1))) nImage = m_nImage[_nImageFocusOrHover]; // if none is set, use default one if(nImage == -1) nImage = m_nImage[_nImageNormal]; // draw the button image bool bAuto3D = (m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0; int xyPos = (bPressed && bAuto3D && (m_nImage[_nImagePushed] == -1)) ? 1 : 0; m_ImageList.Draw(dc, nImage, xyPos, xyPos, ILD_NORMAL); // draw 3D border if required if(bAuto3D) { RECT rect = { 0 }; GetClientRect(&rect); if(bPressed) dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_SUNKENOUTER : EDGE_SUNKEN, BF_RECT); else if(!bHover || (m_fMouseOver == 1)) dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_RAISEDINNER : EDGE_RAISED, BF_RECT); if(!bHover && (m_fFocus == 1)) { ::InflateRect(&rect, -2 * ::GetSystemMetrics(SM_CXEDGE), -2 * ::GetSystemMetrics(SM_CYEDGE)); dc.DrawFocusRect(&rect); } } } // Message map and handlers BEGIN_MSG_MAP(CBitmapButtonImpl) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) MESSAGE_HANDLER(WM_PAINT, OnPaint) MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint) MESSAGE_HANDLER(WM_SETFOCUS, OnFocus) MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus) MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown) MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClk) MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp) MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged) MESSAGE_HANDLER(WM_ENABLE, OnEnable) MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave) MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown) MESSAGE_HANDLER(WM_KEYUP, OnKeyUp) MESSAGE_HANDLER(WM_TIMER, OnTimer) MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState) END_MSG_MAP() LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); pT->Init(); bHandled = FALSE; return 1; } LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(m_tip.IsWindow()) { m_tip.DestroyWindow(); m_tip.m_hWnd = NULL; } bHandled = FALSE; return 1; } LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { MSG msg = { m_hWnd, uMsg, wParam, lParam }; if(m_tip.IsWindow()) m_tip.RelayEvent(&msg); bHandled = FALSE; return 1; } LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return 1; // no background needed } LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); if(wParam != NULL) { pT->DoPaint((HDC)wParam); } else { CPaintDC dc(m_hWnd); pT->DoPaint(dc.m_hDC); } return 0; } LRESULT OnFocus(UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { m_fFocus = (uMsg == WM_SETFOCUS) ? 1 : 0; Invalidate(); UpdateWindow(); bHandled = FALSE; return 1; } LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { LRESULT lRet = 0; if(IsHoverMode()) SetCapture(); else lRet = DefWindowProc(uMsg, wParam, lParam); if(::GetCapture() == m_hWnd) { m_fPressed = 1; Invalidate(); UpdateWindow(); } if(((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0) && !IsCheckMode()) { int nElapse = 250; int nDelay = 0; if(::SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &nDelay, 0)) nElapse += nDelay * 250; // all milli-seconds SetTimer(ID_TIMER_FIRST, nElapse); } return lRet; } LRESULT OnLButtonDblClk(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { LRESULT lRet = 0; if(!IsHoverMode() && !IsCheckMode()) lRet = DefWindowProc(uMsg, wParam, lParam); if(::GetCapture() != m_hWnd) SetCapture(); if(m_fPressed == 0) { m_fPressed = 1; Invalidate(); UpdateWindow(); } return lRet; } LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { if(((m_dwExtendedStyle & BMPBTN_AUTOCHECK) != 0) && (m_fPressed == 1)) SetCheck(!GetCheck(), false); LRESULT lRet = 0; if(!IsHoverMode() && !IsCheckMode()) lRet = DefWindowProc(uMsg, wParam, lParam); if(::GetCapture() == m_hWnd) { if((IsHoverMode() || IsCheckMode()) && (m_fPressed == 1)) ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd); ::ReleaseCapture(); } return lRet; } LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(m_fPressed == 1) { m_fPressed = 0; Invalidate(); UpdateWindow(); } bHandled = FALSE; return 1; } LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { Invalidate(); UpdateWindow(); bHandled = FALSE; return 1; } LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { if(::GetCapture() == m_hWnd) { POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; ClientToScreen(&ptCursor); RECT rect = { 0 }; GetWindowRect(&rect); unsigned int uPressed = ::PtInRect(&rect, ptCursor) ? 1 : 0; if(m_fPressed != uPressed) { m_fPressed = uPressed; Invalidate(); UpdateWindow(); } } else if(IsHoverMode() && m_fMouseOver == 0) { m_fMouseOver = 1; Invalidate(); UpdateWindow(); StartTrackMouseLeave(); } bHandled = FALSE; return 1; } LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { if(m_fMouseOver == 1) { m_fMouseOver = 0; Invalidate(); UpdateWindow(); } return 0; } LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { if(wParam == VK_SPACE && IsHoverMode()) return 0; // ignore if in hover mode if(wParam == VK_SPACE && m_fPressed == 0) { m_fPressed = 1; Invalidate(); UpdateWindow(); } bHandled = FALSE; return 1; } LRESULT OnKeyUp(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { if(wParam == VK_SPACE && IsHoverMode()) return 0; // ignore if in hover mode if(wParam == VK_SPACE && m_fPressed == 1) { m_fPressed = 0; if((m_dwExtendedStyle & BMPBTN_AUTOCHECK) != 0) SetCheck(!GetCheck(), false); Invalidate(); UpdateWindow(); } bHandled = FALSE; return 1; } LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { ATLASSERT((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0); switch(wParam) // timer ID { case ID_TIMER_FIRST: KillTimer(ID_TIMER_FIRST); if(m_fPressed == 1) { ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd); int nElapse = 250; int nRepeat = 40; if(::SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &nRepeat, 0)) nElapse = 10000 / (10 * nRepeat + 25); // milli-seconds, approximated SetTimer(ID_TIMER_REPEAT, nElapse); } break; case ID_TIMER_REPEAT: if(m_fPressed == 1) ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd); else if(::GetCapture() != m_hWnd) KillTimer(ID_TIMER_REPEAT); break; default: // not our timer break; } return 0; } LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { // If the control is subclassed or superclassed, this message can cause // repainting without WM_PAINT. We don't use this state, so just do nothing. return 0; } // Implementation void Init() { // We need this style to prevent Windows from painting the button ModifyStyle(0, BS_OWNERDRAW); // create a tool tip m_tip.Create(m_hWnd); ATLASSERT(m_tip.IsWindow()); if(m_tip.IsWindow() && m_lpstrToolTipText != NULL) { m_tip.Activate(TRUE); m_tip.AddTool(m_hWnd, m_lpstrToolTipText); } if(m_ImageList.m_hImageList != NULL && (m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0) SizeToImage(); } BOOL StartTrackMouseLeave() { TRACKMOUSEEVENT tme = { 0 }; tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; tme.hwndTrack = m_hWnd; return _TrackMouseEvent(&tme); } bool IsHoverMode() const { return ((m_dwExtendedStyle & BMPBTN_HOVER) != 0); } bool IsCheckMode() const { return ((m_dwExtendedStyle & (BMPBTN_CHECK | BMPBTN_AUTOCHECK)) != 0); } }; class CBitmapButton : public CBitmapButtonImpl { public: DECLARE_WND_SUPERCLASS(_T("WTL_BitmapButton"), GetWndClassName()) CBitmapButton(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) : CBitmapButtonImpl(dwExtendedStyle, hImageList) { } }; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CCheckListCtrlView - list view control with check boxes template class CCheckListViewCtrlImplTraits { public: static DWORD GetWndStyle(DWORD dwStyle) { return (dwStyle == 0) ? t_dwStyle : dwStyle; } static DWORD GetWndExStyle(DWORD dwExStyle) { return (dwExStyle == 0) ? t_dwExStyle : dwExStyle; } static DWORD GetExtendedLVStyle() { return t_dwExListViewStyle; } }; typedef CCheckListViewCtrlImplTraits CCheckListViewCtrlTraits; template class ATL_NO_VTABLE CCheckListViewCtrlImpl : public ATL::CWindowImpl { public: DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName()) // Attributes static DWORD GetExtendedLVStyle() { return TWinTraits::GetExtendedLVStyle(); } // Operations BOOL SubclassWindow(HWND hWnd) { #if (_MSC_VER >= 1300) BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); #else // !(_MSC_VER >= 1300) typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass; BOOL bRet = _baseClass::SubclassWindow(hWnd); #endif // !(_MSC_VER >= 1300) if(bRet != FALSE) { T* pT = static_cast(this); pT->Init(); } return bRet; } void CheckSelectedItems(int nCurrItem) { // first check if this item is selected LVITEM lvi = { 0 }; lvi.iItem = nCurrItem; lvi.iSubItem = 0; lvi.mask = LVIF_STATE; lvi.stateMask = LVIS_SELECTED; GetItem(&lvi); // if item is not selected, don't do anything if(!(lvi.state & LVIS_SELECTED)) return; // new check state will be reverse of the current state, BOOL bCheck = !GetCheckState(nCurrItem); int nItem = -1; int nOldItem = -1; while((nItem = GetNextItem(nOldItem, LVNI_SELECTED)) != -1) { if(nItem != nCurrItem) SetCheckState(nItem, bCheck); nOldItem = nItem; } } // Implementation void Init() { T* pT = static_cast(this); pT; // avoid level 4 warning ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0); SetExtendedListViewStyle(pT->GetExtendedLVStyle()); } // Message map and handlers BEGIN_MSG_MAP(CCheckListViewCtrlImpl) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown) MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDown) MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown) END_MSG_MAP() LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { // first let list view control initialize everything LRESULT lRet = DefWindowProc(uMsg, wParam, lParam); if(lRet == 0) { T* pT = static_cast(this); pT->Init(); } return lRet; } LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { POINT ptMsg = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; LVHITTESTINFO lvh = { 0 }; lvh.pt = ptMsg; if(HitTest(&lvh) != -1 && lvh.flags == LVHT_ONITEMSTATEICON && ::GetKeyState(VK_CONTROL) >= 0) { T* pT = static_cast(this); pT->CheckSelectedItems(lvh.iItem); } bHandled = FALSE; return 1; } LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { if(wParam == VK_SPACE) { int nCurrItem = GetNextItem(-1, LVNI_FOCUSED); if(nCurrItem != -1 && ::GetKeyState(VK_CONTROL) >= 0) { T* pT = static_cast(this); pT->CheckSelectedItems(nCurrItem); } } bHandled = FALSE; return 1; } }; class CCheckListViewCtrl : public CCheckListViewCtrlImpl { public: DECLARE_WND_SUPERCLASS(_T("WTL_CheckListView"), GetWndClassName()) }; /////////////////////////////////////////////////////////////////////////////// // CHyperLink - hyper link control implementation #if (WINVER < 0x0500) && !defined(_WIN32_WCE) __declspec(selectany) struct { enum { cxWidth = 32, cyHeight = 32 }; int xHotSpot; int yHotSpot; unsigned char arrANDPlane[cxWidth * cyHeight / 8]; unsigned char arrXORPlane[cxWidth * cyHeight / 8]; } _AtlHyperLink_CursorData = { 5, 0, { 0xF9, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0x80, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, { 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x06, 0xD8, 0x00, 0x00, 0x06, 0xDA, 0x00, 0x00, 0x06, 0xDB, 0x00, 0x00, 0x67, 0xFB, 0x00, 0x00, 0x77, 0xFF, 0x00, 0x00, 0x37, 0xFF, 0x00, 0x00, 0x17, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; #endif // (WINVER < 0x0500) && !defined(_WIN32_WCE) #define HLINK_UNDERLINED 0x00000000 #define HLINK_NOTUNDERLINED 0x00000001 #define HLINK_UNDERLINEHOVER 0x00000002 #define HLINK_COMMANDBUTTON 0x00000004 #define HLINK_NOTIFYBUTTON 0x0000000C #define HLINK_USETAGS 0x00000010 #define HLINK_USETAGSBOLD 0x00000030 #define HLINK_NOTOOLTIP 0x00000040 #define HLINK_AUTOCREATELINKFONT 0x00000080 #define HLINK_SINGLELINE 0x00000100 // Notes: // - HLINK_USETAGS and HLINK_USETAGSBOLD are always left-aligned // - When HLINK_USETAGSBOLD is used, the underlined styles will be ignored template class ATL_NO_VTABLE CHyperLinkImpl : public ATL::CWindowImpl< T, TBase, TWinTraits > { public: LPTSTR m_lpstrLabel; LPTSTR m_lpstrHyperLink; HCURSOR m_hCursor; HFONT m_hFontLink; HFONT m_hFontNormal; RECT m_rcLink; #ifndef _WIN32_WCE CToolTipCtrl m_tip; #endif // !_WIN32_WCE COLORREF m_clrLink; COLORREF m_clrVisited; DWORD m_dwExtendedStyle; // Hyper Link specific extended styles bool m_bPaintLabel:1; bool m_bVisited:1; bool m_bHover:1; bool m_bInternalLinkFont:1; bool m_bInternalNormalFont:1; // Constructor/Destructor CHyperLinkImpl(DWORD dwExtendedStyle = HLINK_UNDERLINED) : m_lpstrLabel(NULL), m_lpstrHyperLink(NULL), m_hCursor(NULL), m_hFontLink(NULL), m_hFontNormal(NULL), m_clrLink(RGB(0, 0, 255)), m_clrVisited(RGB(128, 0, 128)), m_dwExtendedStyle(dwExtendedStyle), m_bPaintLabel(true), m_bVisited(false), m_bHover(false), m_bInternalLinkFont(false), m_bInternalNormalFont(false) { ::SetRectEmpty(&m_rcLink); } ~CHyperLinkImpl() { delete [] m_lpstrLabel; delete [] m_lpstrHyperLink; #if (WINVER < 0x0500) && !defined(_WIN32_WCE) // It was created, not loaded, so we have to destroy it if(m_hCursor != NULL) ::DestroyCursor(m_hCursor); #endif // (WINVER < 0x0500) && !defined(_WIN32_WCE) } // Attributes DWORD GetHyperLinkExtendedStyle() const { return m_dwExtendedStyle; } DWORD SetHyperLinkExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0) { DWORD dwPrevStyle = m_dwExtendedStyle; if(dwMask == 0) m_dwExtendedStyle = dwExtendedStyle; else m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask); return dwPrevStyle; } bool GetLabel(LPTSTR lpstrBuffer, int nLength) const { if(m_lpstrLabel == NULL) return false; ATLASSERT(lpstrBuffer != NULL); if(nLength <= lstrlen(m_lpstrLabel)) return false; SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrLabel); return true; } bool SetLabel(LPCTSTR lpstrLabel) { delete [] m_lpstrLabel; m_lpstrLabel = NULL; int cchLen = lstrlen(lpstrLabel) + 1; ATLTRY(m_lpstrLabel = new TCHAR[cchLen]); if(m_lpstrLabel == NULL) return false; SecureHelper::strcpy_x(m_lpstrLabel, cchLen, lpstrLabel); T* pT = static_cast(this); pT->CalcLabelRect(); if(m_hWnd != NULL) SetWindowText(lpstrLabel); // Set this for accessibility return true; } bool GetHyperLink(LPTSTR lpstrBuffer, int nLength) const { if(m_lpstrHyperLink == NULL) return false; ATLASSERT(lpstrBuffer != NULL); if(nLength <= lstrlen(m_lpstrHyperLink)) return false; SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrHyperLink); return true; } bool SetHyperLink(LPCTSTR lpstrLink) { delete [] m_lpstrHyperLink; m_lpstrHyperLink = NULL; int cchLen = lstrlen(lpstrLink) + 1; ATLTRY(m_lpstrHyperLink = new TCHAR[cchLen]); if(m_lpstrHyperLink == NULL) return false; SecureHelper::strcpy_x(m_lpstrHyperLink, cchLen, lpstrLink); if(m_lpstrLabel == NULL) { T* pT = static_cast(this); pT->CalcLabelRect(); } #ifndef _WIN32_WCE if(m_tip.IsWindow()) { m_tip.Activate(TRUE); m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1); } #endif // !_WIN32_WCE return true; } HFONT GetLinkFont() const { return m_hFontLink; } void SetLinkFont(HFONT hFont) { if(m_bInternalLinkFont) { ::DeleteObject(m_hFontLink); m_bInternalLinkFont = false; } m_hFontLink = hFont; T* pT = static_cast(this); pT->CalcLabelRect(); } int GetIdealHeight() const { ATLASSERT(::IsWindow(m_hWnd)); if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL) return -1; if(!m_bPaintLabel) return -1; UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK; CClientDC dc(m_hWnd); RECT rect = { 0 }; GetClientRect(&rect); HFONT hFontOld = dc.SelectFont(m_hFontNormal); RECT rcText = rect; dc.DrawText(_T("NS"), -1, &rcText, DT_LEFT | uFormat | DT_CALCRECT); dc.SelectFont(m_hFontLink); RECT rcLink = rect; dc.DrawText(_T("NS"), -1, &rcLink, DT_LEFT | uFormat | DT_CALCRECT); dc.SelectFont(hFontOld); return __max(rcText.bottom - rcText.top, rcLink.bottom - rcLink.top); } bool GetIdealSize(SIZE& size) const { int cx = 0, cy = 0; bool bRet = GetIdealSize(cx, cy); if(bRet) { size.cx = cx; size.cy = cy; } return bRet; } bool GetIdealSize(int& cx, int& cy) const { ATLASSERT(::IsWindow(m_hWnd)); if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL) return false; if(!m_bPaintLabel) return false; CClientDC dc(m_hWnd); RECT rcClient = { 0 }; GetClientRect(&rcClient); RECT rcAll = rcClient; if(IsUsingTags()) { // find tags and label parts LPTSTR lpstrLeft = NULL; int cchLeft = 0; LPTSTR lpstrLink = NULL; int cchLink = 0; LPTSTR lpstrRight = NULL; int cchRight = 0; const T* pT = static_cast(this); pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight); // get label part rects UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK; HFONT hFontOld = dc.SelectFont(m_hFontNormal); RECT rcLeft = rcClient; dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | uFormat | DT_CALCRECT); dc.SelectFont(m_hFontLink); RECT rcLink = { rcLeft.right, rcLeft.top, rcClient.right, rcClient.bottom }; dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | uFormat | DT_CALCRECT); dc.SelectFont(m_hFontNormal); RECT rcRight = { rcLink.right, rcLink.top, rcClient.right, rcClient.bottom }; dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | uFormat | DT_CALCRECT); dc.SelectFont(hFontOld); int cyMax = __max(rcLeft.bottom, __max(rcLink.bottom, rcRight.bottom)); ::SetRect(&rcAll, rcLeft.left, rcLeft.top, rcRight.right, cyMax); } else { HFONT hOldFont = NULL; if(m_hFontLink != NULL) hOldFont = dc.SelectFont(m_hFontLink); LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink; DWORD dwStyle = GetStyle(); UINT uFormat = DT_LEFT; if (dwStyle & SS_CENTER) uFormat = DT_CENTER; else if (dwStyle & SS_RIGHT) uFormat = DT_RIGHT; uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK; dc.DrawText(lpstrText, -1, &rcAll, uFormat | DT_CALCRECT); if(m_hFontLink != NULL) dc.SelectFont(hOldFont); if (dwStyle & SS_CENTER) { int dx = (rcClient.right - rcAll.right) / 2; ::OffsetRect(&rcAll, dx, 0); } else if (dwStyle & SS_RIGHT) { int dx = rcClient.right - rcAll.right; ::OffsetRect(&rcAll, dx, 0); } } cx = rcAll.right - rcAll.left; cy = rcAll.bottom - rcAll.top; return true; } // for command buttons only bool GetToolTipText(LPTSTR lpstrBuffer, int nLength) const { ATLASSERT(IsCommandButton()); return GetHyperLink(lpstrBuffer, nLength); } bool SetToolTipText(LPCTSTR lpstrToolTipText) { ATLASSERT(IsCommandButton()); return SetHyperLink(lpstrToolTipText); } // Operations BOOL SubclassWindow(HWND hWnd) { ATLASSERT(m_hWnd == NULL); ATLASSERT(::IsWindow(hWnd)); if(m_hFontNormal == NULL) m_hFontNormal = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0L); #if (_MSC_VER >= 1300) BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); #else // !(_MSC_VER >= 1300) typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass; BOOL bRet = _baseClass::SubclassWindow(hWnd); #endif // !(_MSC_VER >= 1300) if(bRet != FALSE) { T* pT = static_cast(this); pT->Init(); } return bRet; } bool Navigate() { ATLASSERT(::IsWindow(m_hWnd)); bool bRet = true; if(IsNotifyButton()) { NMHDR nmhdr = { m_hWnd, GetDlgCtrlID(), NM_CLICK }; ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr); } else if(IsCommandButton()) { ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd); } else { ATLASSERT(m_lpstrHyperLink != NULL); #ifndef _WIN32_WCE DWORD_PTR dwRet = (DWORD_PTR)::ShellExecute(0, _T("open"), m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL); bRet = (dwRet > 32); #else // CE specific SHELLEXECUTEINFO shExeInfo = { sizeof(SHELLEXECUTEINFO), 0, 0, L"open", m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL, 0, 0, 0, 0, 0, 0, 0 }; ::ShellExecuteEx(&shExeInfo); DWORD_PTR dwRet = (DWORD_PTR)shExeInfo.hInstApp; bRet = (dwRet == 0) || (dwRet > 32); #endif // _WIN32_WCE ATLASSERT(bRet); if(bRet) { m_bVisited = true; Invalidate(); } } return bRet; } void CreateLinkFontFromNormal() { if(m_bInternalLinkFont) { ::DeleteObject(m_hFontLink); m_bInternalLinkFont = false; } CFontHandle font = (m_hFontNormal != NULL) ? m_hFontNormal : (HFONT)::GetStockObject(SYSTEM_FONT); LOGFONT lf = { 0 }; font.GetLogFont(&lf); if(IsUsingTagsBold()) lf.lfWeight = FW_BOLD; else if(!IsNotUnderlined()) lf.lfUnderline = TRUE; m_hFontLink = ::CreateFontIndirect(&lf); m_bInternalLinkFont = true; ATLASSERT(m_hFontLink != NULL); } // Message map and handlers BEGIN_MSG_MAP(CHyperLinkImpl) MESSAGE_HANDLER(WM_CREATE, OnCreate) #ifndef _WIN32_WCE MESSAGE_HANDLER(WM_DESTROY, OnDestroy) MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage) #endif // !_WIN32_WCE MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) MESSAGE_HANDLER(WM_PAINT, OnPaint) #ifndef _WIN32_WCE MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint) #endif // !_WIN32_WCE MESSAGE_HANDLER(WM_SETFOCUS, OnFocus) MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus) MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) #ifndef _WIN32_WCE MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave) #endif // !_WIN32_WCE MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown) MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp) MESSAGE_HANDLER(WM_CHAR, OnChar) MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode) MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor) MESSAGE_HANDLER(WM_ENABLE, OnEnable) MESSAGE_HANDLER(WM_GETFONT, OnGetFont) MESSAGE_HANDLER(WM_SETFONT, OnSetFont) MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState) MESSAGE_HANDLER(WM_SIZE, OnSize) END_MSG_MAP() LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->Init(); return 0; } #ifndef _WIN32_WCE LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(m_tip.IsWindow()) { m_tip.DestroyWindow(); m_tip.m_hWnd = NULL; } if(m_bInternalLinkFont) { ::DeleteObject(m_hFontLink); m_hFontLink = NULL; m_bInternalLinkFont = false; } if(m_bInternalNormalFont) { ::DeleteObject(m_hFontNormal); m_hFontNormal = NULL; m_bInternalNormalFont = false; } bHandled = FALSE; return 1; } LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { MSG msg = { m_hWnd, uMsg, wParam, lParam }; if(m_tip.IsWindow() && IsUsingToolTip()) m_tip.RelayEvent(&msg); bHandled = FALSE; return 1; } #endif // !_WIN32_WCE LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return 1; // no background painting needed (we do it all during WM_PAINT) } LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { if(!m_bPaintLabel) { bHandled = FALSE; return 1; } T* pT = static_cast(this); if(wParam != NULL) { pT->DoEraseBackground((HDC)wParam); pT->DoPaint((HDC)wParam); } else { CPaintDC dc(m_hWnd); pT->DoEraseBackground(dc.m_hDC); pT->DoPaint(dc.m_hDC); } return 0; } LRESULT OnFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(m_bPaintLabel) Invalidate(); else bHandled = FALSE; return 0; } LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; if((m_lpstrHyperLink != NULL || IsCommandButton()) && ::PtInRect(&m_rcLink, pt)) { ::SetCursor(m_hCursor); if(IsUnderlineHover()) { if(!m_bHover) { m_bHover = true; InvalidateRect(&m_rcLink); UpdateWindow(); #ifndef _WIN32_WCE StartTrackMouseLeave(); #endif // !_WIN32_WCE } } } else { if(IsUnderlineHover()) { if(m_bHover) { m_bHover = false; InvalidateRect(&m_rcLink); UpdateWindow(); } } bHandled = FALSE; } return 0; } #ifndef _WIN32_WCE LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { if(IsUnderlineHover() && m_bHover) { m_bHover = false; InvalidateRect(&m_rcLink); UpdateWindow(); } return 0; } #endif // !_WIN32_WCE LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/) { POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; if(::PtInRect(&m_rcLink, pt)) { SetFocus(); SetCapture(); } return 0; } LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/) { if(GetCapture() == m_hWnd) { ReleaseCapture(); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; if(::PtInRect(&m_rcLink, pt)) { T* pT = static_cast(this); pT->Navigate(); } } return 0; } LRESULT OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { if(wParam == VK_RETURN || wParam == VK_SPACE) { T* pT = static_cast(this); pT->Navigate(); } return 0; } LRESULT OnGetDlgCode(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return DLGC_WANTCHARS; } LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { POINT pt = { 0, 0 }; GetCursorPos(&pt); ScreenToClient(&pt); if((m_lpstrHyperLink != NULL || IsCommandButton()) && ::PtInRect(&m_rcLink, pt)) { return TRUE; } bHandled = FALSE; return FALSE; } LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { Invalidate(); UpdateWindow(); return 0; } LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return (LRESULT)m_hFontNormal; } LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { if(m_bInternalNormalFont) { ::DeleteObject(m_hFontNormal); m_bInternalNormalFont = false; } bool bCreateLinkFont = m_bInternalLinkFont; m_hFontNormal = (HFONT)wParam; if(bCreateLinkFont || IsAutoCreateLinkFont()) CreateLinkFontFromNormal(); T* pT = static_cast(this); pT->CalcLabelRect(); if((BOOL)lParam) { Invalidate(); UpdateWindow(); } return 0; } LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { // If the control is subclassed or superclassed, this message can cause // repainting without WM_PAINT. We don't use this state, so just do nothing. return 0; } LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->CalcLabelRect(); pT->Invalidate(); return 0; } // Implementation void Init() { ATLASSERT(::IsWindow(m_hWnd)); // Check if we should paint a label const int cchBuff = 8; TCHAR szBuffer[cchBuff] = { 0 }; if(::GetClassName(m_hWnd, szBuffer, cchBuff)) { if(lstrcmpi(szBuffer, _T("static")) == 0) { ModifyStyle(0, SS_NOTIFY); // we need this DWORD dwStyle = GetStyle() & 0x000000FF; #ifndef _WIN32_WCE if(dwStyle == SS_ICON || dwStyle == SS_BLACKRECT || dwStyle == SS_GRAYRECT || dwStyle == SS_WHITERECT || dwStyle == SS_BLACKFRAME || dwStyle == SS_GRAYFRAME || dwStyle == SS_WHITEFRAME || dwStyle == SS_OWNERDRAW || dwStyle == SS_BITMAP || dwStyle == SS_ENHMETAFILE) #else // CE specific if(dwStyle == SS_ICON || dwStyle == SS_BITMAP) #endif // _WIN32_WCE m_bPaintLabel = false; } } // create or load a cursor #if (WINVER >= 0x0500) || defined(_WIN32_WCE) m_hCursor = ::LoadCursor(NULL, IDC_HAND); #else m_hCursor = ::CreateCursor(ModuleHelper::GetModuleInstance(), _AtlHyperLink_CursorData.xHotSpot, _AtlHyperLink_CursorData.yHotSpot, _AtlHyperLink_CursorData.cxWidth, _AtlHyperLink_CursorData.cyHeight, _AtlHyperLink_CursorData.arrANDPlane, _AtlHyperLink_CursorData.arrXORPlane); #endif ATLASSERT(m_hCursor != NULL); // set fonts if(m_bPaintLabel) { if(m_hFontNormal == NULL) { m_hFontNormal = AtlCreateControlFont(); m_bInternalNormalFont = true; } if(m_hFontLink == NULL) CreateLinkFontFromNormal(); } #ifndef _WIN32_WCE // create a tool tip m_tip.Create(m_hWnd); ATLASSERT(m_tip.IsWindow()); #endif // !_WIN32_WCE // set label (defaults to window text) if(m_lpstrLabel == NULL) { int nLen = GetWindowTextLength(); if(nLen > 0) { ATLTRY(m_lpstrLabel = new TCHAR[nLen + 1]); if(m_lpstrLabel != NULL) ATLVERIFY(GetWindowText(m_lpstrLabel, nLen + 1) > 0); } } T* pT = static_cast(this); pT->CalcLabelRect(); // set hyperlink (defaults to label), or just activate tool tip if already set if(m_lpstrHyperLink == NULL && !IsCommandButton()) { if(m_lpstrLabel != NULL) SetHyperLink(m_lpstrLabel); } #ifndef _WIN32_WCE else { m_tip.Activate(TRUE); m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1); } #endif // !_WIN32_WCE // set link colors if(m_bPaintLabel) { CRegKeyEx rk; LONG lRet = rk.Open(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Internet Explorer\\Settings")); if(lRet == ERROR_SUCCESS) { const int cchValue = 12; TCHAR szValue[cchValue] = { 0 }; ULONG ulCount = cchValue; lRet = rk.QueryStringValue(_T("Anchor Color"), szValue, &ulCount); if(lRet == ERROR_SUCCESS) { COLORREF clr = pT->_ParseColorString(szValue); ATLASSERT(clr != CLR_INVALID); if(clr != CLR_INVALID) m_clrLink = clr; } ulCount = cchValue; lRet = rk.QueryStringValue(_T("Anchor Color Visited"), szValue, &ulCount); if(lRet == ERROR_SUCCESS) { COLORREF clr = pT->_ParseColorString(szValue); ATLASSERT(clr != CLR_INVALID); if(clr != CLR_INVALID) m_clrVisited = clr; } } } } static COLORREF _ParseColorString(LPTSTR lpstr) { int c[3] = { -1, -1, -1 }; LPTSTR p = NULL; for(int i = 0; i < 2; i++) { for(p = lpstr; *p != _T('\0'); p = ::CharNext(p)) { if(*p == _T(',')) { *p = _T('\0'); c[i] = MinCrtHelper::_atoi(lpstr); lpstr = &p[1]; break; } } if(c[i] == -1) return CLR_INVALID; } if(*lpstr == _T('\0')) return CLR_INVALID; c[2] = MinCrtHelper::_atoi(lpstr); return RGB(c[0], c[1], c[2]); } bool CalcLabelRect() { if(!::IsWindow(m_hWnd)) return false; if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL) return false; CClientDC dc(m_hWnd); RECT rcClient = { 0 }; GetClientRect(&rcClient); m_rcLink = rcClient; if(!m_bPaintLabel) return true; if(IsUsingTags()) { // find tags and label parts LPTSTR lpstrLeft = NULL; int cchLeft = 0; LPTSTR lpstrLink = NULL; int cchLink = 0; LPTSTR lpstrRight = NULL; int cchRight = 0; T* pT = static_cast(this); pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight); ATLASSERT(lpstrLink != NULL); ATLASSERT(cchLink > 0); // get label part rects HFONT hFontOld = dc.SelectFont(m_hFontNormal); UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK; RECT rcLeft = rcClient; if(lpstrLeft != NULL) dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | uFormat | DT_CALCRECT); dc.SelectFont(m_hFontLink); RECT rcLink = rcClient; if(lpstrLeft != NULL) rcLink.left = rcLeft.right; dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | uFormat | DT_CALCRECT); dc.SelectFont(hFontOld); m_rcLink = rcLink; } else { HFONT hOldFont = NULL; if(m_hFontLink != NULL) hOldFont = dc.SelectFont(m_hFontLink); LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink; DWORD dwStyle = GetStyle(); UINT uFormat = DT_LEFT; if (dwStyle & SS_CENTER) uFormat = DT_CENTER; else if (dwStyle & SS_RIGHT) uFormat = DT_RIGHT; uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK; dc.DrawText(lpstrText, -1, &m_rcLink, uFormat | DT_CALCRECT); if(m_hFontLink != NULL) dc.SelectFont(hOldFont); if (dwStyle & SS_CENTER) { int dx = (rcClient.right - m_rcLink.right) / 2; ::OffsetRect(&m_rcLink, dx, 0); } else if (dwStyle & SS_RIGHT) { int dx = rcClient.right - m_rcLink.right; ::OffsetRect(&m_rcLink, dx, 0); } } return true; } void CalcLabelParts(LPTSTR& lpstrLeft, int& cchLeft, LPTSTR& lpstrLink, int& cchLink, LPTSTR& lpstrRight, int& cchRight) const { lpstrLeft = NULL; cchLeft = 0; lpstrLink = NULL; cchLink = 0; lpstrRight = NULL; cchRight = 0; LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink; int cchText = lstrlen(lpstrText); bool bOutsideLink = true; for(int i = 0; i < cchText; i++) { if(lpstrText[i] != _T('<')) continue; if(bOutsideLink) { if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 3, _T(""), 3) == CSTR_EQUAL) { if(i > 0) { lpstrLeft = lpstrText; cchLeft = i; } lpstrLink = &lpstrText[i + 3]; bOutsideLink = false; } } else { if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 4, _T(""), 4) == CSTR_EQUAL) { cchLink = i - 3 - cchLeft; if(lpstrText[i + 4] != 0) { lpstrRight = &lpstrText[i + 4]; cchRight = cchText - (i + 4); break; } } } } } void DoEraseBackground(CDCHandle dc) { HBRUSH hBrush = (HBRUSH)::SendMessage(GetParent(), WM_CTLCOLORSTATIC, (WPARAM)dc.m_hDC, (LPARAM)m_hWnd); if(hBrush != NULL) { RECT rect = { 0 }; GetClientRect(&rect); dc.FillRect(&rect, hBrush); } } void DoPaint(CDCHandle dc) { if(IsUsingTags()) { // find tags and label parts LPTSTR lpstrLeft = NULL; int cchLeft = 0; LPTSTR lpstrLink = NULL; int cchLink = 0; LPTSTR lpstrRight = NULL; int cchRight = 0; T* pT = static_cast(this); pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight); // get label part rects RECT rcClient = { 0 }; GetClientRect(&rcClient); dc.SetBkMode(TRANSPARENT); HFONT hFontOld = dc.SelectFont(m_hFontNormal); UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK; if(lpstrLeft != NULL) dc.DrawText(lpstrLeft, cchLeft, &rcClient, DT_LEFT | uFormat); COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT))); if(m_hFontLink != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover))) dc.SelectFont(m_hFontLink); else dc.SelectFont(m_hFontNormal); dc.DrawText(lpstrLink, cchLink, &m_rcLink, DT_LEFT | uFormat); dc.SetTextColor(clrOld); dc.SelectFont(m_hFontNormal); if(lpstrRight != NULL) { RECT rcRight = { m_rcLink.right, m_rcLink.top, rcClient.right, rcClient.bottom }; dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | uFormat); } if(GetFocus() == m_hWnd) dc.DrawFocusRect(&m_rcLink); dc.SelectFont(hFontOld); } else { dc.SetBkMode(TRANSPARENT); COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT))); HFONT hFontOld = NULL; if(m_hFontLink != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover))) hFontOld = dc.SelectFont(m_hFontLink); else hFontOld = dc.SelectFont(m_hFontNormal); LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink; DWORD dwStyle = GetStyle(); UINT uFormat = DT_LEFT; if (dwStyle & SS_CENTER) uFormat = DT_CENTER; else if (dwStyle & SS_RIGHT) uFormat = DT_RIGHT; uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK; dc.DrawText(lpstrText, -1, &m_rcLink, uFormat); if(GetFocus() == m_hWnd) dc.DrawFocusRect(&m_rcLink); dc.SetTextColor(clrOld); dc.SelectFont(hFontOld); } } #ifndef _WIN32_WCE BOOL StartTrackMouseLeave() { TRACKMOUSEEVENT tme = { 0 }; tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; tme.hwndTrack = m_hWnd; return _TrackMouseEvent(&tme); } #endif // !_WIN32_WCE // Implementation helpers bool IsUnderlined() const { return ((m_dwExtendedStyle & (HLINK_NOTUNDERLINED | HLINK_UNDERLINEHOVER)) == 0); } bool IsNotUnderlined() const { return ((m_dwExtendedStyle & HLINK_NOTUNDERLINED) != 0); } bool IsUnderlineHover() const { return ((m_dwExtendedStyle & HLINK_UNDERLINEHOVER) != 0); } bool IsCommandButton() const { return ((m_dwExtendedStyle & HLINK_COMMANDBUTTON) != 0); } bool IsNotifyButton() const { return ((m_dwExtendedStyle & HLINK_NOTIFYBUTTON) == HLINK_NOTIFYBUTTON); } bool IsUsingTags() const { return ((m_dwExtendedStyle & HLINK_USETAGS) != 0); } bool IsUsingTagsBold() const { return ((m_dwExtendedStyle & HLINK_USETAGSBOLD) == HLINK_USETAGSBOLD); } bool IsUsingToolTip() const { return ((m_dwExtendedStyle & HLINK_NOTOOLTIP) == 0); } bool IsAutoCreateLinkFont() const { return ((m_dwExtendedStyle & HLINK_AUTOCREATELINKFONT) == HLINK_AUTOCREATELINKFONT); } bool IsSingleLine() const { return ((m_dwExtendedStyle & HLINK_SINGLELINE) == HLINK_SINGLELINE); } }; class CHyperLink : public CHyperLinkImpl { public: DECLARE_WND_CLASS(_T("WTL_HyperLink")) }; /////////////////////////////////////////////////////////////////////////////// // CWaitCursor - displays a wait cursor class CWaitCursor { public: // Data HCURSOR m_hWaitCursor; HCURSOR m_hOldCursor; bool m_bInUse; // Constructor/destructor CWaitCursor(bool bSet = true, LPCTSTR lpstrCursor = IDC_WAIT, bool bSys = true) : m_hOldCursor(NULL), m_bInUse(false) { HINSTANCE hInstance = bSys ? NULL : ModuleHelper::GetResourceInstance(); m_hWaitCursor = ::LoadCursor(hInstance, lpstrCursor); ATLASSERT(m_hWaitCursor != NULL); if(bSet) Set(); } ~CWaitCursor() { Restore(); } // Methods bool Set() { if(m_bInUse) return false; m_hOldCursor = ::SetCursor(m_hWaitCursor); m_bInUse = true; return true; } bool Restore() { if(!m_bInUse) return false; ::SetCursor(m_hOldCursor); m_bInUse = false; return true; } }; /////////////////////////////////////////////////////////////////////////////// // CCustomWaitCursor - for custom and animated cursors class CCustomWaitCursor : public CWaitCursor { public: // Constructor/destructor CCustomWaitCursor(ATL::_U_STRINGorID cursor, bool bSet = true, HINSTANCE hInstance = NULL) : CWaitCursor(false, IDC_WAIT, true) { if(hInstance == NULL) hInstance = ModuleHelper::GetResourceInstance(); m_hWaitCursor = (HCURSOR)::LoadImage(hInstance, cursor.m_lpstr, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE); if(bSet) Set(); } ~CCustomWaitCursor() { Restore(); #if !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP))) ::DestroyCursor(m_hWaitCursor); #endif // !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP))) } }; /////////////////////////////////////////////////////////////////////////////// // CMultiPaneStatusBarCtrl - Status Bar with multiple panes template class ATL_NO_VTABLE CMultiPaneStatusBarCtrlImpl : public ATL::CWindowImpl< T, TBase > { public: DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName()) // Data enum { m_cxPaneMargin = 3 }; int m_nPanes; int* m_pPane; // Constructor/destructor CMultiPaneStatusBarCtrlImpl() : m_nPanes(0), m_pPane(NULL) { } ~CMultiPaneStatusBarCtrlImpl() { delete [] m_pPane; } // Methods HWND Create(HWND hWndParent, LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR) { #if (_MSC_VER >= 1300) return ATL::CWindowImpl< T, TBase >::Create(hWndParent, rcDefault, lpstrText, dwStyle, 0, nID); #else // !(_MSC_VER >= 1300) typedef ATL::CWindowImpl< T, TBase > _baseClass; return _baseClass::Create(hWndParent, rcDefault, lpstrText, dwStyle, 0, nID); #endif // !(_MSC_VER >= 1300) } HWND Create(HWND hWndParent, UINT nTextID = ATL_IDS_IDLEMESSAGE, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR) { const int cchMax = 128; // max text length is 127 for status bars (+1 for null) TCHAR szText[cchMax] = { 0 }; ::LoadString(ModuleHelper::GetResourceInstance(), nTextID, szText, cchMax); return Create(hWndParent, szText, dwStyle, nID); } BOOL SetPanes(int* pPanes, int nPanes, bool bSetText = true) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nPanes > 0); m_nPanes = nPanes; delete [] m_pPane; m_pPane = NULL; ATLTRY(m_pPane = new int[nPanes]); ATLASSERT(m_pPane != NULL); if(m_pPane == NULL) return FALSE; CTempBuffer buff; int* pPanesPos = buff.Allocate(nPanes); ATLASSERT(pPanesPos != NULL); if(pPanesPos == NULL) return FALSE; SecureHelper::memcpy_x(m_pPane, nPanes * sizeof(int), pPanes, nPanes * sizeof(int)); // get status bar DC and set font CClientDC dc(m_hWnd); HFONT hOldFont = dc.SelectFont(GetFont()); // get status bar borders int arrBorders[3] = { 0 }; GetBorders(arrBorders); const int cchBuff = 128; TCHAR szBuff[cchBuff] = { 0 }; SIZE size = { 0, 0 }; int cxLeft = arrBorders[0]; // calculate right edge of each part for(int i = 0; i < nPanes; i++) { if(pPanes[i] == ID_DEFAULT_PANE) { // make very large, will be resized later pPanesPos[i] = INT_MAX / 2; } else { ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff); dc.GetTextExtent(szBuff, lstrlen(szBuff), &size); T* pT = static_cast(this); pT; pPanesPos[i] = cxLeft + size.cx + arrBorders[2] + 2 * pT->m_cxPaneMargin; } cxLeft = pPanesPos[i]; } BOOL bRet = SetParts(nPanes, pPanesPos); if(bRet && bSetText) { for(int i = 0; i < nPanes; i++) { if(pPanes[i] != ID_DEFAULT_PANE) { ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff); SetPaneText(m_pPane[i], szBuff); } } } dc.SelectFont(hOldFont); return bRet; } bool GetPaneTextLength(int nPaneID, int* pcchLength = NULL, int* pnType = NULL) const { ATLASSERT(::IsWindow(m_hWnd)); int nIndex = GetPaneIndexFromID(nPaneID); if(nIndex == -1) return false; int nLength = GetTextLength(nIndex, pnType); if(pcchLength != NULL) *pcchLength = nLength; return true; } BOOL GetPaneText(int nPaneID, LPTSTR lpstrText, int* pcchLength = NULL, int* pnType = NULL) const { ATLASSERT(::IsWindow(m_hWnd)); int nIndex = GetPaneIndexFromID(nPaneID); if(nIndex == -1) return FALSE; int nLength = GetText(nIndex, lpstrText, pnType); if(pcchLength != NULL) *pcchLength = nLength; return TRUE; } BOOL SetPaneText(int nPaneID, LPCTSTR lpstrText, int nType = 0) { ATLASSERT(::IsWindow(m_hWnd)); int nIndex = GetPaneIndexFromID(nPaneID); if(nIndex == -1) return FALSE; return SetText(nIndex, lpstrText, nType); } BOOL GetPaneRect(int nPaneID, LPRECT lpRect) const { ATLASSERT(::IsWindow(m_hWnd)); int nIndex = GetPaneIndexFromID(nPaneID); if(nIndex == -1) return FALSE; return GetRect(nIndex, lpRect); } BOOL SetPaneWidth(int nPaneID, int cxWidth) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nPaneID != ID_DEFAULT_PANE); // Can't resize this one int nIndex = GetPaneIndexFromID(nPaneID); if(nIndex == -1) return FALSE; // get pane positions CTempBuffer buff; int* pPanesPos = buff.Allocate(m_nPanes); if(pPanesPos == NULL) return FALSE; GetParts(m_nPanes, pPanesPos); // calculate offset int cxPaneWidth = pPanesPos[nIndex] - ((nIndex == 0) ? 0 : pPanesPos[nIndex - 1]); int cxOff = cxWidth - cxPaneWidth; // find variable width pane int nDef = m_nPanes; for(int i = 0; i < m_nPanes; i++) { if(m_pPane[i] == ID_DEFAULT_PANE) { nDef = i; break; } } // resize if(nIndex < nDef) // before default pane { for(int i = nIndex; i < nDef; i++) pPanesPos[i] += cxOff; } else // after default one { for(int i = nDef; i < nIndex; i++) pPanesPos[i] -= cxOff; } // set pane postions return SetParts(m_nPanes, pPanesPos); } #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) BOOL GetPaneTipText(int nPaneID, LPTSTR lpstrText, int nSize) const { ATLASSERT(::IsWindow(m_hWnd)); int nIndex = GetPaneIndexFromID(nPaneID); if(nIndex == -1) return FALSE; GetTipText(nIndex, lpstrText, nSize); return TRUE; } BOOL SetPaneTipText(int nPaneID, LPCTSTR lpstrText) { ATLASSERT(::IsWindow(m_hWnd)); int nIndex = GetPaneIndexFromID(nPaneID); if(nIndex == -1) return FALSE; SetTipText(nIndex, lpstrText); return TRUE; } #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) #if ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500)) BOOL GetPaneIcon(int nPaneID, HICON& hIcon) const { ATLASSERT(::IsWindow(m_hWnd)); int nIndex = GetPaneIndexFromID(nPaneID); if(nIndex == -1) return FALSE; hIcon = GetIcon(nIndex); return TRUE; } BOOL SetPaneIcon(int nPaneID, HICON hIcon) { ATLASSERT(::IsWindow(m_hWnd)); int nIndex = GetPaneIndexFromID(nPaneID); if(nIndex == -1) return FALSE; return SetIcon(nIndex, hIcon); } #endif // ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500)) // Message map and handlers BEGIN_MSG_MAP(CMultiPaneStatusBarCtrlImpl< T >) MESSAGE_HANDLER(WM_SIZE, OnSize) END_MSG_MAP() LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { LRESULT lRet = DefWindowProc(uMsg, wParam, lParam); if(wParam != SIZE_MINIMIZED && m_nPanes > 0) { T* pT = static_cast(this); pT->UpdatePanesLayout(); } return lRet; } // Implementation BOOL UpdatePanesLayout() { // get pane positions CTempBuffer buff; int* pPanesPos = buff.Allocate(m_nPanes); ATLASSERT(pPanesPos != NULL); if(pPanesPos == NULL) return FALSE; int nRet = GetParts(m_nPanes, pPanesPos); ATLASSERT(nRet == m_nPanes); if(nRet != m_nPanes) return FALSE; // calculate offset RECT rcClient = { 0 }; GetClientRect(&rcClient); int cxOff = rcClient.right - pPanesPos[m_nPanes - 1]; #ifndef _WIN32_WCE // Move panes left if size grip box is present if((GetStyle() & SBARS_SIZEGRIP) != 0) cxOff -= ::GetSystemMetrics(SM_CXVSCROLL) + ::GetSystemMetrics(SM_CXEDGE); #endif // !_WIN32_WCE // find variable width pane int i; for(i = 0; i < m_nPanes; i++) { if(m_pPane[i] == ID_DEFAULT_PANE) break; } // resize all panes from the variable one to the right if((i < m_nPanes) && (pPanesPos[i] + cxOff) > ((i == 0) ? 0 : pPanesPos[i - 1])) { for(; i < m_nPanes; i++) pPanesPos[i] += cxOff; } // set pane postions return SetParts(m_nPanes, pPanesPos); } int GetPaneIndexFromID(int nPaneID) const { for(int i = 0; i < m_nPanes; i++) { if(m_pPane[i] == nPaneID) return i; } return -1; // not found } }; class CMultiPaneStatusBarCtrl : public CMultiPaneStatusBarCtrlImpl { public: DECLARE_WND_SUPERCLASS(_T("WTL_MultiPaneStatusBar"), GetWndClassName()) }; /////////////////////////////////////////////////////////////////////////////// // CPaneContainer - provides header with title and close button for panes // pane container extended styles #define PANECNT_NOCLOSEBUTTON 0x00000001 #define PANECNT_VERTICAL 0x00000002 #define PANECNT_FLATBORDER 0x00000004 #define PANECNT_NOBORDER 0x00000008 template class ATL_NO_VTABLE CPaneContainerImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CCustomDraw< T > { public: DECLARE_WND_CLASS_EX(NULL, 0, -1) // Constants enum { m_cxyBorder = 2, m_cxyTextOffset = 4, m_cxyBtnOffset = 1, m_cchTitle = 80, m_cxImageTB = 13, m_cyImageTB = 11, m_cxyBtnAddTB = 7, m_cxToolBar = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder + m_cxyBtnOffset, m_xBtnImageLeft = 6, m_yBtnImageTop = 5, m_xBtnImageRight = 12, m_yBtnImageBottom = 11, m_nCloseBtnID = ID_PANE_CLOSE }; // Data members CToolBarCtrl m_tb; ATL::CWindow m_wndClient; int m_cxyHeader; TCHAR m_szTitle[m_cchTitle]; DWORD m_dwExtendedStyle; // Pane container specific extended styles HFONT m_hFont; bool m_bInternalFont; // Constructor CPaneContainerImpl() : m_cxyHeader(0), m_dwExtendedStyle(0), m_hFont(NULL), m_bInternalFont(false) { m_szTitle[0] = 0; } // Attributes DWORD GetPaneContainerExtendedStyle() const { return m_dwExtendedStyle; } DWORD SetPaneContainerExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0) { DWORD dwPrevStyle = m_dwExtendedStyle; if(dwMask == 0) m_dwExtendedStyle = dwExtendedStyle; else m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask); if(m_hWnd != NULL) { T* pT = static_cast(this); bool bUpdate = false; if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) != 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)) // add close button { pT->CreateCloseButton(); bUpdate = true; } else if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) == 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) != 0)) // remove close button { pT->DestroyCloseButton(); bUpdate = true; } if((dwPrevStyle & PANECNT_VERTICAL) != (m_dwExtendedStyle & PANECNT_VERTICAL)) // change orientation { pT->CalcSize(); bUpdate = true; } if((dwPrevStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER)) != (m_dwExtendedStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER))) // change border { bUpdate = true; } if(bUpdate) pT->UpdateLayout(); } return dwPrevStyle; } HWND GetClient() const { return m_wndClient; } HWND SetClient(HWND hWndClient) { HWND hWndOldClient = m_wndClient; m_wndClient = hWndClient; if(m_hWnd != NULL) { T* pT = static_cast(this); pT->UpdateLayout(); } return hWndOldClient; } BOOL GetTitle(LPTSTR lpstrTitle, int cchLength) const { ATLASSERT(lpstrTitle != NULL); errno_t nRet = SecureHelper::strncpy_x(lpstrTitle, cchLength, m_szTitle, _TRUNCATE); return (nRet == 0 || nRet == STRUNCATE); } BOOL SetTitle(LPCTSTR lpstrTitle) { ATLASSERT(lpstrTitle != NULL); errno_t nRet = SecureHelper::strncpy_x(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE); bool bRet = (nRet == 0 || nRet == STRUNCATE); if(bRet && m_hWnd != NULL) { T* pT = static_cast(this); pT->UpdateLayout(); } return bRet; } int GetTitleLength() const { return lstrlen(m_szTitle); } // Methods HWND Create(HWND hWndParent, LPCTSTR lpstrTitle = NULL, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL) { if(lpstrTitle != NULL) SecureHelper::strncpy_x(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE); #if (_MSC_VER >= 1300) return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam); #else // !(_MSC_VER >= 1300) typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass; return _baseClass::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam); #endif // !(_MSC_VER >= 1300) } HWND Create(HWND hWndParent, UINT uTitleID, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL) { if(uTitleID != 0U) ::LoadString(ModuleHelper::GetResourceInstance(), uTitleID, m_szTitle, m_cchTitle); #if (_MSC_VER >= 1300) return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam); #else // !(_MSC_VER >= 1300) typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass; return _baseClass::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam); #endif // !(_MSC_VER >= 1300) } BOOL SubclassWindow(HWND hWnd) { #if (_MSC_VER >= 1300) BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); #else // !(_MSC_VER >= 1300) typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass; BOOL bRet = _baseClass::SubclassWindow(hWnd); #endif // !(_MSC_VER >= 1300) if(bRet != FALSE) { T* pT = static_cast(this); pT->Init(); RECT rect = { 0 }; GetClientRect(&rect); pT->UpdateLayout(rect.right, rect.bottom); } return bRet; } BOOL EnableCloseButton(BOOL bEnable) { ATLASSERT(::IsWindow(m_hWnd)); T* pT = static_cast(this); pT; // avoid level 4 warning return (m_tb.m_hWnd != NULL) ? m_tb.EnableButton(pT->m_nCloseBtnID, bEnable) : FALSE; } void UpdateLayout() { RECT rcClient = { 0 }; GetClientRect(&rcClient); T* pT = static_cast(this); pT->UpdateLayout(rcClient.right, rcClient.bottom); } // Message map and handlers BEGIN_MSG_MAP(CPaneContainerImpl) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) MESSAGE_HANDLER(WM_GETFONT, OnGetFont) MESSAGE_HANDLER(WM_SETFONT, OnSetFont) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) MESSAGE_HANDLER(WM_PAINT, OnPaint) #ifndef _WIN32_WCE MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint) #endif // !_WIN32_WCE MESSAGE_HANDLER(WM_NOTIFY, OnNotify) MESSAGE_HANDLER(WM_COMMAND, OnCommand) FORWARD_NOTIFICATIONS() END_MSG_MAP() LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->Init(); return 0; } LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { if(m_bInternalFont) { ::DeleteObject(m_hFont); m_hFont = NULL; m_bInternalFont = false; } return 0; } LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->UpdateLayout(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return 0; } LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { if(m_wndClient.m_hWnd != NULL) m_wndClient.SetFocus(); return 0; } LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return (LRESULT)m_hFont; } LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { if(m_bInternalFont) { ::DeleteObject(m_hFont); m_bInternalFont = false; } m_hFont = (HFONT)wParam; T* pT = static_cast(this); pT->CalcSize(); if((BOOL)lParam != FALSE) pT->UpdateLayout(); return 0; } LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return 1; // no background needed } LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); if(wParam != NULL) { pT->DrawPaneTitle((HDC)wParam); if(m_wndClient.m_hWnd == NULL) // no client window pT->DrawPane((HDC)wParam); } else { CPaintDC dc(m_hWnd); pT->DrawPaneTitle(dc.m_hDC); if(m_wndClient.m_hWnd == NULL) // no client window pT->DrawPane(dc.m_hDC); } return 0; } LRESULT OnNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { if(m_tb.m_hWnd == NULL) { bHandled = FALSE; return 1; } T* pT = static_cast(this); pT; LPNMHDR lpnmh = (LPNMHDR)lParam; LRESULT lRet = 0; // pass toolbar custom draw notifications to the base class if(lpnmh->code == NM_CUSTOMDRAW && lpnmh->hwndFrom == m_tb.m_hWnd) lRet = CCustomDraw< T >::OnCustomDraw(0, lpnmh, bHandled); #ifndef _WIN32_WCE // tooltip notifications come with the tooltip window handle and button ID, // pass them to the parent if we don't handle them else if(lpnmh->code == TTN_GETDISPINFO && lpnmh->idFrom == pT->m_nCloseBtnID) bHandled = pT->GetToolTipText(lpnmh); #endif // !_WIN32_WCE // only let notifications not from the toolbar go to the parent else if(lpnmh->hwndFrom != m_tb.m_hWnd && lpnmh->idFrom != pT->m_nCloseBtnID) bHandled = FALSE; return lRet; } LRESULT OnCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // if command comes from the close button, substitute HWND of the pane container instead if(m_tb.m_hWnd != NULL && (HWND)lParam == m_tb.m_hWnd) return ::SendMessage(GetParent(), WM_COMMAND, wParam, (LPARAM)m_hWnd); bHandled = FALSE; return 1; } // Custom draw overrides DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) { return CDRF_NOTIFYITEMDRAW; // we need per-item notifications } DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw) { CDCHandle dc = lpNMCustomDraw->hdc; #if (_WIN32_IE >= 0x0400) RECT& rc = lpNMCustomDraw->rc; #else // !(_WIN32_IE >= 0x0400) RECT rc = { 0 }; m_tb.GetItemRect(0, &rc); #endif // !(_WIN32_IE >= 0x0400) dc.FillRect(&rc, COLOR_3DFACE); return CDRF_NOTIFYPOSTPAINT; } DWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw) { CDCHandle dc = lpNMCustomDraw->hdc; #if (_WIN32_IE >= 0x0400) RECT& rc = lpNMCustomDraw->rc; #else // !(_WIN32_IE >= 0x0400) RECT rc = { 0 }; m_tb.GetItemRect(0, &rc); #endif // !(_WIN32_IE >= 0x0400) RECT rcImage = { m_xBtnImageLeft, m_yBtnImageTop, m_xBtnImageRight + 1, m_yBtnImageBottom + 1 }; ::OffsetRect(&rcImage, rc.left, rc.top); T* pT = static_cast(this); if((lpNMCustomDraw->uItemState & CDIS_DISABLED) != 0) { RECT rcShadow = rcImage; ::OffsetRect(&rcShadow, 1, 1); CPen pen1; pen1.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DHILIGHT)); pT->DrawButtonImage(dc, rcShadow, pen1); CPen pen2; pen2.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DSHADOW)); pT->DrawButtonImage(dc, rcImage, pen2); } else { if((lpNMCustomDraw->uItemState & CDIS_SELECTED) != 0) ::OffsetRect(&rcImage, 1, 1); CPen pen; pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNTEXT)); pT->DrawButtonImage(dc, rcImage, pen); } return CDRF_DODEFAULT; // continue with the default item painting } // Implementation - overrideable methods void Init() { if(m_hFont == NULL) { // The same as AtlCreateControlFont() for horizontal pane #ifndef _WIN32_WCE LOGFONT lf = { 0 }; ATLVERIFY(::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0) != FALSE); if(IsVertical()) lf.lfEscapement = 900; // 90 degrees m_hFont = ::CreateFontIndirect(&lf); #else // CE specific m_hFont = (HFONT)::GetStockObject(SYSTEM_FONT); if(IsVertical()) { CLogFont lf(m_hFont); lf.lfEscapement = 900; // 90 degrees m_hFont = ::CreateFontIndirect(&lf); } #endif // _WIN32_WCE m_bInternalFont = true; } T* pT = static_cast(this); pT->CalcSize(); if((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0) pT->CreateCloseButton(); } void UpdateLayout(int cxWidth, int cyHeight) { ATLASSERT(::IsWindow(m_hWnd)); RECT rect = { 0 }; if(IsVertical()) { ::SetRect(&rect, 0, 0, m_cxyHeader, cyHeight); if(m_tb.m_hWnd != NULL) m_tb.SetWindowPos(NULL, m_cxyBorder, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); if(m_wndClient.m_hWnd != NULL) m_wndClient.SetWindowPos(NULL, m_cxyHeader, 0, cxWidth - m_cxyHeader, cyHeight, SWP_NOZORDER); else rect.right = cxWidth; } else { ::SetRect(&rect, 0, 0, cxWidth, m_cxyHeader); if(m_tb.m_hWnd != NULL) m_tb.SetWindowPos(NULL, rect.right - m_cxToolBar, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); if(m_wndClient.m_hWnd != NULL) m_wndClient.SetWindowPos(NULL, 0, m_cxyHeader, cxWidth, cyHeight - m_cxyHeader, SWP_NOZORDER); else rect.bottom = cyHeight; } InvalidateRect(&rect); } void CreateCloseButton() { ATLASSERT(m_tb.m_hWnd == NULL); // create toolbar for the "x" button m_tb.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN | CCS_NOMOVEY | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT, 0); ATLASSERT(m_tb.IsWindow()); if(m_tb.m_hWnd != NULL) { T* pT = static_cast(this); pT; // avoid level 4 warning m_tb.SetButtonStructSize(); TBBUTTON tbbtn = { 0 }; tbbtn.idCommand = pT->m_nCloseBtnID; tbbtn.fsState = TBSTATE_ENABLED; tbbtn.fsStyle = BTNS_BUTTON; m_tb.AddButtons(1, &tbbtn); m_tb.SetBitmapSize(m_cxImageTB, m_cyImageTB); m_tb.SetButtonSize(m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB); if(IsVertical()) m_tb.SetWindowPos(NULL, m_cxyBorder + m_cxyBtnOffset, m_cxyBorder + m_cxyBtnOffset, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB, SWP_NOZORDER | SWP_NOACTIVATE); else m_tb.SetWindowPos(NULL, 0, 0, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); } } void DestroyCloseButton() { if(m_tb.m_hWnd != NULL) m_tb.DestroyWindow(); } void CalcSize() { T* pT = static_cast(this); CFontHandle font = pT->GetTitleFont(); if(font.IsNull()) font = (HFONT)::GetStockObject(SYSTEM_FONT); LOGFONT lf = { 0 }; font.GetLogFont(lf); if(IsVertical()) { m_cxyHeader = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder; } else { int cyFont = abs(lf.lfHeight) + m_cxyBorder + 2 * m_cxyTextOffset; int cyBtn = m_cyImageTB + m_cxyBtnAddTB + m_cxyBorder + 2 * m_cxyBtnOffset; m_cxyHeader = __max(cyFont, cyBtn); } } HFONT GetTitleFont() const { return m_hFont; } #ifndef _WIN32_WCE BOOL GetToolTipText(LPNMHDR /*lpnmh*/) { return FALSE; } #endif // !_WIN32_WCE void DrawPaneTitle(CDCHandle dc) { RECT rect = { 0 }; GetClientRect(&rect); UINT uBorder = BF_LEFT | BF_TOP | BF_ADJUST; if(IsVertical()) { rect.right = rect.left + m_cxyHeader; uBorder |= BF_BOTTOM; } else { rect.bottom = rect.top + m_cxyHeader; uBorder |= BF_RIGHT; } if((m_dwExtendedStyle & PANECNT_NOBORDER) == 0) { if((m_dwExtendedStyle & PANECNT_FLATBORDER) != 0) uBorder |= BF_FLAT; dc.DrawEdge(&rect, EDGE_ETCHED, uBorder); } dc.FillRect(&rect, COLOR_3DFACE); // draw title text dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT)); dc.SetBkMode(TRANSPARENT); T* pT = static_cast(this); HFONT hFontOld = dc.SelectFont(pT->GetTitleFont()); #if defined(_WIN32_WCE) && !defined(DT_END_ELLIPSIS) const UINT DT_END_ELLIPSIS = 0; #endif // defined(_WIN32_WCE) && !defined(DT_END_ELLIPSIS) if(IsVertical()) { rect.top += m_cxyTextOffset; rect.bottom -= m_cxyTextOffset; if(m_tb.m_hWnd != NULL) rect.top += m_cxToolBar;; RECT rcCalc = { rect.left, rect.bottom, rect.right, rect.top }; int cxFont = dc.DrawText(m_szTitle, -1, &rcCalc, DT_TOP | DT_SINGLELINE | DT_END_ELLIPSIS | DT_CALCRECT); RECT rcText = { 0 }; rcText.left = (rect.right - rect.left - cxFont) / 2; rcText.right = rcText.left + (rect.bottom - rect.top); rcText.top = rect.bottom; rcText.bottom = rect.top; dc.DrawText(m_szTitle, -1, &rcText, DT_TOP | DT_SINGLELINE | DT_END_ELLIPSIS); } else { rect.left += m_cxyTextOffset; rect.right -= m_cxyTextOffset; if(m_tb.m_hWnd != NULL) rect.right -= m_cxToolBar;; dc.DrawText(m_szTitle, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS); } dc.SelectFont(hFontOld); } // called only if pane is empty void DrawPane(CDCHandle dc) { RECT rect = { 0 }; GetClientRect(&rect); if(IsVertical()) rect.left += m_cxyHeader; else rect.top += m_cxyHeader; if((GetExStyle() & WS_EX_CLIENTEDGE) == 0) dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); dc.FillRect(&rect, COLOR_APPWORKSPACE); } // drawing helper - draws "x" button image void DrawButtonImage(CDCHandle dc, RECT& rcImage, HPEN hPen) { #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400) HPEN hPenOld = dc.SelectPen(hPen); dc.MoveTo(rcImage.left, rcImage.top); dc.LineTo(rcImage.right, rcImage.bottom); dc.MoveTo(rcImage.left + 1, rcImage.top); dc.LineTo(rcImage.right + 1, rcImage.bottom); dc.MoveTo(rcImage.left, rcImage.bottom - 1); dc.LineTo(rcImage.right, rcImage.top - 1); dc.MoveTo(rcImage.left + 1, rcImage.bottom - 1); dc.LineTo(rcImage.right + 1, rcImage.top - 1); dc.SelectPen(hPenOld); #else // (_WIN32_WCE < 400) rcImage; hPen; // no support for the "x" button image #endif // (_WIN32_WCE < 400) } bool IsVertical() const { return ((m_dwExtendedStyle & PANECNT_VERTICAL) != 0); } }; class CPaneContainer : public CPaneContainerImpl { public: DECLARE_WND_CLASS_EX(_T("WTL_PaneContainer"), 0, -1) }; /////////////////////////////////////////////////////////////////////////////// // CSortListViewCtrl - implements sorting for a listview control // sort listview extended styles #define SORTLV_USESHELLBITMAPS 0x00000001 // Notification sent to parent when sort column is changed by user clicking header. #define SLVN_SORTCHANGED LVN_LAST // A LPNMSORTLISTVIEW is sent with the SLVN_SORTCHANGED notification typedef struct tagNMSORTLISTVIEW { NMHDR hdr; int iNewSortColumn; int iOldSortColumn; } NMSORTLISTVIEW, *LPNMSORTLISTVIEW; // Column sort types. Can be set on a per-column basis with the SetColumnSortType method. enum { LVCOLSORT_NONE, LVCOLSORT_TEXT, // default LVCOLSORT_TEXTNOCASE, LVCOLSORT_LONG, LVCOLSORT_DOUBLE, LVCOLSORT_DECIMAL, LVCOLSORT_DATETIME, LVCOLSORT_DATE, LVCOLSORT_TIME, LVCOLSORT_CUSTOM, LVCOLSORT_LAST = LVCOLSORT_CUSTOM }; template class CSortListViewImpl { public: enum { m_cchCmpTextMax = 32, // overrideable m_cxSortImage = 16, m_cySortImage = 15, m_cxSortArrow = 11, m_cySortArrow = 6, m_iSortUp = 0, // index of sort bitmaps m_iSortDown = 1, m_nShellSortUpID = 133 }; // passed to LVCompare functions as lParam1 and lParam2 struct LVCompareParam { int iItem; DWORD_PTR dwItemData; union { long lValue; double dblValue; DECIMAL decValue; LPCTSTR pszValue; }; }; // passed to LVCompare functions as the lParamSort parameter struct LVSortInfo { T* pT; int iSortCol; bool bDescending; }; bool m_bSortDescending; bool m_bCommCtrl6; int m_iSortColumn; CBitmap m_bmSort[2]; int m_fmtOldSortCol; HBITMAP m_hbmOldSortCol; DWORD m_dwSortLVExtendedStyle; ATL::CSimpleArray m_arrColSortType; bool m_bUseWaitCursor; CSortListViewImpl() : m_bSortDescending(false), m_bCommCtrl6(false), m_iSortColumn(-1), m_fmtOldSortCol(0), m_hbmOldSortCol(NULL), m_dwSortLVExtendedStyle(SORTLV_USESHELLBITMAPS), m_bUseWaitCursor(true) { #ifndef _WIN32_WCE DWORD dwMajor = 0; DWORD dwMinor = 0; HRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor); m_bCommCtrl6 = SUCCEEDED(hRet) && dwMajor >= 6; #endif // !_WIN32_WCE } // Attributes void SetSortColumn(int iCol) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); CHeaderCtrl header = pT->GetHeader(); ATLASSERT(header.m_hWnd != NULL); ATLASSERT(iCol >= -1 && iCol < m_arrColSortType.GetSize()); int iOldSortCol = m_iSortColumn; m_iSortColumn = iCol; if(m_bCommCtrl6) { #ifndef HDF_SORTUP const int HDF_SORTUP = 0x0400; #endif // HDF_SORTUP #ifndef HDF_SORTDOWN const int HDF_SORTDOWN = 0x0200; #endif // HDF_SORTDOWN const int nMask = HDF_SORTUP | HDF_SORTDOWN; HDITEM hditem = { HDI_FORMAT }; if(iOldSortCol != iCol && iOldSortCol >= 0 && header.GetItem(iOldSortCol, &hditem)) { hditem.fmt &= ~nMask; header.SetItem(iOldSortCol, &hditem); } if(iCol >= 0 && header.GetItem(iCol, &hditem)) { hditem.fmt &= ~nMask; hditem.fmt |= m_bSortDescending ? HDF_SORTDOWN : HDF_SORTUP; header.SetItem(iCol, &hditem); } return; } if(m_bmSort[m_iSortUp].IsNull()) pT->CreateSortBitmaps(); // restore previous sort column's bitmap, if any, and format HDITEM hditem = { HDI_BITMAP | HDI_FORMAT }; if(iOldSortCol != iCol && iOldSortCol >= 0) { hditem.hbm = m_hbmOldSortCol; hditem.fmt = m_fmtOldSortCol; header.SetItem(iOldSortCol, &hditem); } // save new sort column's bitmap and format, and add our sort bitmap if(iCol >= 0 && header.GetItem(iCol, &hditem)) { if(iOldSortCol != iCol) { m_fmtOldSortCol = hditem.fmt; m_hbmOldSortCol = hditem.hbm; } hditem.fmt &= ~HDF_IMAGE; hditem.fmt |= HDF_BITMAP | HDF_BITMAP_ON_RIGHT; int i = m_bSortDescending ? m_iSortDown : m_iSortUp; hditem.hbm = m_bmSort[i]; header.SetItem(iCol, &hditem); } } int GetSortColumn() const { return m_iSortColumn; } void SetColumnSortType(int iCol, WORD wType) { ATLASSERT(iCol >= 0 && iCol < m_arrColSortType.GetSize()); ATLASSERT(wType >= LVCOLSORT_NONE && wType <= LVCOLSORT_LAST); m_arrColSortType[iCol] = wType; } WORD GetColumnSortType(int iCol) const { ATLASSERT((iCol >= 0) && iCol < m_arrColSortType.GetSize()); return m_arrColSortType[iCol]; } int GetColumnCount() const { const T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); CHeaderCtrl header = pT->GetHeader(); return header.m_hWnd != NULL ? header.GetItemCount() : 0; } bool IsSortDescending() const { return m_bSortDescending; } DWORD GetSortListViewExtendedStyle() const { return m_dwSortLVExtendedStyle; } DWORD SetSortListViewExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0) { DWORD dwPrevStyle = m_dwSortLVExtendedStyle; if(dwMask == 0) m_dwSortLVExtendedStyle = dwExtendedStyle; else m_dwSortLVExtendedStyle = (m_dwSortLVExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask); return dwPrevStyle; } // Operations bool DoSortItems(int iCol, bool bDescending = false) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); ATLASSERT(iCol >= 0 && iCol < m_arrColSortType.GetSize()); WORD wType = m_arrColSortType[iCol]; if(wType == LVCOLSORT_NONE) return false; int nCount = pT->GetItemCount(); if(nCount < 2) { m_bSortDescending = bDescending; SetSortColumn(iCol); return true; } CWaitCursor waitCursor(false); if(m_bUseWaitCursor) waitCursor.Set(); LVCompareParam* pParam = NULL; ATLTRY(pParam = new LVCompareParam[nCount]); PFNLVCOMPARE pFunc = NULL; TCHAR pszTemp[pT->m_cchCmpTextMax] = { 0 }; bool bStrValue = false; switch(wType) { case LVCOLSORT_TEXT: pFunc = (PFNLVCOMPARE)pT->LVCompareText; case LVCOLSORT_TEXTNOCASE: if(pFunc == NULL) pFunc = (PFNLVCOMPARE)pT->LVCompareTextNoCase; case LVCOLSORT_CUSTOM: { if(pFunc == NULL) pFunc = (PFNLVCOMPARE)pT->LVCompareCustom; for(int i = 0; i < nCount; i++) { pParam[i].iItem = i; pParam[i].dwItemData = pT->GetItemData(i); pParam[i].pszValue = new TCHAR[pT->m_cchCmpTextMax]; pT->GetItemText(i, iCol, (LPTSTR)pParam[i].pszValue, pT->m_cchCmpTextMax); pT->SetItemData(i, (DWORD_PTR)&pParam[i]); } bStrValue = true; } break; case LVCOLSORT_LONG: { pFunc = (PFNLVCOMPARE)pT->LVCompareLong; for(int i = 0; i < nCount; i++) { pParam[i].iItem = i; pParam[i].dwItemData = pT->GetItemData(i); pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax); pParam[i].lValue = pT->StrToLong(pszTemp); pT->SetItemData(i, (DWORD_PTR)&pParam[i]); } } break; case LVCOLSORT_DOUBLE: { pFunc = (PFNLVCOMPARE)pT->LVCompareDouble; for(int i = 0; i < nCount; i++) { pParam[i].iItem = i; pParam[i].dwItemData = pT->GetItemData(i); pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax); pParam[i].dblValue = pT->StrToDouble(pszTemp); pT->SetItemData(i, (DWORD_PTR)&pParam[i]); } } break; case LVCOLSORT_DECIMAL: { pFunc = (PFNLVCOMPARE)pT->LVCompareDecimal; for(int i = 0; i < nCount; i++) { pParam[i].iItem = i; pParam[i].dwItemData = pT->GetItemData(i); pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax); pT->StrToDecimal(pszTemp, &pParam[i].decValue); pT->SetItemData(i, (DWORD_PTR)&pParam[i]); } } break; case LVCOLSORT_DATETIME: case LVCOLSORT_DATE: case LVCOLSORT_TIME: { pFunc = (PFNLVCOMPARE)pT->LVCompareDouble; DWORD dwFlags = LOCALE_NOUSEROVERRIDE; if(wType == LVCOLSORT_DATE) dwFlags |= VAR_DATEVALUEONLY; else if(wType == LVCOLSORT_TIME) dwFlags |= VAR_TIMEVALUEONLY; for(int i = 0; i < nCount; i++) { pParam[i].iItem = i; pParam[i].dwItemData = pT->GetItemData(i); pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax); pParam[i].dblValue = pT->DateStrToDouble(pszTemp, dwFlags); pT->SetItemData(i, (DWORD_PTR)&pParam[i]); } } break; default: ATLTRACE2(atlTraceUI, 0, _T("Unknown value for sort type in CSortListViewImpl::DoSortItems()\n")); break; } // switch(wType) ATLASSERT(pFunc != NULL); LVSortInfo lvsi = { pT, iCol, bDescending }; bool bRet = ((BOOL)pT->DefWindowProc(LVM_SORTITEMS, (WPARAM)&lvsi, (LPARAM)pFunc) != FALSE); for(int i = 0; i < nCount; i++) { DWORD_PTR dwItemData = pT->GetItemData(i); LVCompareParam* p = (LVCompareParam*)dwItemData; ATLASSERT(p != NULL); if(bStrValue) delete [] (TCHAR*)p->pszValue; pT->SetItemData(i, p->dwItemData); } delete [] pParam; if(bRet) { m_bSortDescending = bDescending; SetSortColumn(iCol); } if(m_bUseWaitCursor) waitCursor.Restore(); return bRet; } void CreateSortBitmaps() { if((m_dwSortLVExtendedStyle & SORTLV_USESHELLBITMAPS) != 0) { bool bFree = false; LPCTSTR pszModule = _T("shell32.dll"); HINSTANCE hShell = ::GetModuleHandle(pszModule); if (hShell == NULL) { hShell = ::LoadLibrary(pszModule); bFree = true; } if (hShell != NULL) { bool bSuccess = true; for(int i = m_iSortUp; i <= m_iSortDown; i++) { if(!m_bmSort[i].IsNull()) m_bmSort[i].DeleteObject(); m_bmSort[i] = (HBITMAP)::LoadImage(hShell, MAKEINTRESOURCE(m_nShellSortUpID + i), #ifndef _WIN32_WCE IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS); #else // CE specific IMAGE_BITMAP, 0, 0, 0); #endif // _WIN32_WCE if(m_bmSort[i].IsNull()) { bSuccess = false; break; } } if(bFree) ::FreeLibrary(hShell); if(bSuccess) return; } } T* pT = static_cast(this); for(int i = m_iSortUp; i <= m_iSortDown; i++) { if(!m_bmSort[i].IsNull()) m_bmSort[i].DeleteObject(); CDC dcMem; CClientDC dc(::GetDesktopWindow()); dcMem.CreateCompatibleDC(dc.m_hDC); m_bmSort[i].CreateCompatibleBitmap(dc.m_hDC, m_cxSortImage, m_cySortImage); HBITMAP hbmOld = dcMem.SelectBitmap(m_bmSort[i]); RECT rc = { 0, 0, m_cxSortImage, m_cySortImage }; pT->DrawSortBitmap(dcMem.m_hDC, i, &rc); dcMem.SelectBitmap(hbmOld); dcMem.DeleteDC(); } } void NotifyParentSortChanged(int iNewSortCol, int iOldSortCol) { T* pT = static_cast(this); int nID = pT->GetDlgCtrlID(); NMSORTLISTVIEW nm = { { pT->m_hWnd, nID, SLVN_SORTCHANGED }, iNewSortCol, iOldSortCol }; ::SendMessage(pT->GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nm); } // Overrideables int CompareItemsCustom(LVCompareParam* /*pItem1*/, LVCompareParam* /*pItem2*/, int /*iSortCol*/) { // pItem1 and pItem2 contain valid iItem, dwItemData, and pszValue members. // If item1 > item2 return 1, if item1 < item2 return -1, else return 0. return 0; } void DrawSortBitmap(CDCHandle dc, int iBitmap, LPRECT prc) { dc.FillRect(prc, ::GetSysColorBrush(COLOR_BTNFACE)); HBRUSH hbrOld = dc.SelectBrush(::GetSysColorBrush(COLOR_BTNSHADOW)); CPen pen; pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNSHADOW)); HPEN hpenOld = dc.SelectPen(pen); POINT ptOrg = { (m_cxSortImage - m_cxSortArrow) / 2, (m_cySortImage - m_cySortArrow) / 2 }; if(iBitmap == m_iSortUp) { POINT pts[3] = { { ptOrg.x + m_cxSortArrow / 2, ptOrg.y }, { ptOrg.x, ptOrg.y + m_cySortArrow - 1 }, { ptOrg.x + m_cxSortArrow - 1, ptOrg.y + m_cySortArrow - 1 } }; dc.Polygon(pts, 3); } else { POINT pts[3] = { { ptOrg.x, ptOrg.y }, { ptOrg.x + m_cxSortArrow / 2, ptOrg.y + m_cySortArrow - 1 }, { ptOrg.x + m_cxSortArrow - 1, ptOrg.y } }; dc.Polygon(pts, 3); } dc.SelectBrush(hbrOld); dc.SelectPen(hpenOld); } double DateStrToDouble(LPCTSTR lpstr, DWORD dwFlags) { ATLASSERT(lpstr != NULL); if(lpstr == NULL || lpstr[0] == _T('\0')) return 0; USES_CONVERSION; HRESULT hRet = E_FAIL; DATE dRet = 0; if (FAILED(hRet = ::VarDateFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, dwFlags, &dRet))) { ATLTRACE2(atlTraceUI, 0, _T("VarDateFromStr failed with result of 0x%8.8X\n"), hRet); dRet = 0; } return dRet; } long StrToLong(LPCTSTR lpstr) { ATLASSERT(lpstr != NULL); if(lpstr == NULL || lpstr[0] == _T('\0')) return 0; USES_CONVERSION; HRESULT hRet = E_FAIL; long lRet = 0; if (FAILED(hRet = ::VarI4FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &lRet))) { ATLTRACE2(atlTraceUI, 0, _T("VarI4FromStr failed with result of 0x%8.8X\n"), hRet); lRet = 0; } return lRet; } double StrToDouble(LPCTSTR lpstr) { ATLASSERT(lpstr != NULL); if(lpstr == NULL || lpstr[0] == _T('\0')) return 0; USES_CONVERSION; HRESULT hRet = E_FAIL; double dblRet = 0; if (FAILED(hRet = ::VarR8FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &dblRet))) { ATLTRACE2(atlTraceUI, 0, _T("VarR8FromStr failed with result of 0x%8.8X\n"), hRet); dblRet = 0; } return dblRet; } bool StrToDecimal(LPCTSTR lpstr, DECIMAL* pDecimal) { ATLASSERT(lpstr != NULL); ATLASSERT(pDecimal != NULL); if(lpstr == NULL || pDecimal == NULL) return false; USES_CONVERSION; HRESULT hRet = E_FAIL; if (FAILED(hRet = ::VarDecFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, pDecimal))) { ATLTRACE2(atlTraceUI, 0, _T("VarDecFromStr failed with result of 0x%8.8X\n"), hRet); pDecimal->Lo64 = 0; pDecimal->Hi32 = 0; pDecimal->signscale = 0; return false; } return true; } // Overrideable PFNLVCOMPARE functions static int CALLBACK LVCompareText(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL); LVCompareParam* pParam1 = (LVCompareParam*)lParam1; LVCompareParam* pParam2 = (LVCompareParam*)lParam2; LVSortInfo* pInfo = (LVSortInfo*)lParamSort; int nRet = lstrcmp(pParam1->pszValue, pParam2->pszValue); return pInfo->bDescending ? -nRet : nRet; } static int CALLBACK LVCompareTextNoCase(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL); LVCompareParam* pParam1 = (LVCompareParam*)lParam1; LVCompareParam* pParam2 = (LVCompareParam*)lParam2; LVSortInfo* pInfo = (LVSortInfo*)lParamSort; int nRet = lstrcmpi(pParam1->pszValue, pParam2->pszValue); return pInfo->bDescending ? -nRet : nRet; } static int CALLBACK LVCompareLong(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL); LVCompareParam* pParam1 = (LVCompareParam*)lParam1; LVCompareParam* pParam2 = (LVCompareParam*)lParam2; LVSortInfo* pInfo = (LVSortInfo*)lParamSort; int nRet = 0; if(pParam1->lValue > pParam2->lValue) nRet = 1; else if(pParam1->lValue < pParam2->lValue) nRet = -1; return pInfo->bDescending ? -nRet : nRet; } static int CALLBACK LVCompareDouble(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL); LVCompareParam* pParam1 = (LVCompareParam*)lParam1; LVCompareParam* pParam2 = (LVCompareParam*)lParam2; LVSortInfo* pInfo = (LVSortInfo*)lParamSort; int nRet = 0; if(pParam1->dblValue > pParam2->dblValue) nRet = 1; else if(pParam1->dblValue < pParam2->dblValue) nRet = -1; return pInfo->bDescending ? -nRet : nRet; } static int CALLBACK LVCompareCustom(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL); LVCompareParam* pParam1 = (LVCompareParam*)lParam1; LVCompareParam* pParam2 = (LVCompareParam*)lParam2; LVSortInfo* pInfo = (LVSortInfo*)lParamSort; int nRet = pInfo->pT->CompareItemsCustom(pParam1, pParam2, pInfo->iSortCol); return pInfo->bDescending ? -nRet : nRet; } #ifndef _WIN32_WCE static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL); LVCompareParam* pParam1 = (LVCompareParam*)lParam1; LVCompareParam* pParam2 = (LVCompareParam*)lParam2; LVSortInfo* pInfo = (LVSortInfo*)lParamSort; int nRet = (int)::VarDecCmp(&pParam1->decValue, &pParam2->decValue); nRet--; return pInfo->bDescending ? -nRet : nRet; } #else // Compare mantissas, ignore sign and scale static int CompareMantissas(const DECIMAL& decLeft, const DECIMAL& decRight) { if (decLeft.Hi32 < decRight.Hi32) { return -1; } if (decLeft.Hi32 > decRight.Hi32) { return 1; } // Here, decLeft.Hi32 == decRight.Hi32 if (decLeft.Lo64 < decRight.Lo64) { return -1; } if (decLeft.Lo64 > decRight.Lo64) { return 1; } return 0; } // return values: VARCMP_LT, VARCMP_EQ, VARCMP_GT, VARCMP_NULL static HRESULT VarDecCmp(const DECIMAL* pdecLeft, const DECIMAL* pdecRight) { static const ULONG powersOfTen[] = { 10ul, 100ul, 1000ul, 10000ul, 100000ul, 1000000ul, 10000000ul, 100000000ul, 1000000000ul }; static const int largestPower = sizeof(powersOfTen) / sizeof(powersOfTen[0]); if (!pdecLeft || !pdecRight) { return VARCMP_NULL; } // Degenerate case - at least one comparand is of the form // [+-]0*10^N (denormalized zero) bool bLeftZero = (!pdecLeft->Lo64 && !pdecLeft->Hi32); bool bRightZero = (!pdecRight->Lo64 && !pdecRight->Hi32); if (bLeftZero && bRightZero) { return VARCMP_EQ; } bool bLeftNeg = ((pdecLeft->sign & DECIMAL_NEG) != 0); bool bRightNeg = ((pdecRight->sign & DECIMAL_NEG) != 0); if (bLeftZero) { return (bRightNeg ? VARCMP_GT : VARCMP_LT); } // This also covers the case where the comparands have different signs if (bRightZero || bLeftNeg != bRightNeg) { return (bLeftNeg ? VARCMP_LT : VARCMP_GT); } // Here both comparands have the same sign and need to be compared // on mantissa and scale. The result is obvious when // 1. Scales are equal (then compare mantissas) // 2. A number with smaller scale is also the one with larger mantissa // (then this number is obviously larger) // In the remaining case, we would multiply the number with smaller // scale by 10 and simultaneously increment its scale (which amounts to // adding trailing zeros after decimal point), until the numbers fall under // one of the two cases above DECIMAL temp; bool bInvert = bLeftNeg; // the final result needs to be inverted if (pdecLeft->scale < pdecRight->scale) { temp = *pdecLeft; } else { temp = *pdecRight; pdecRight = pdecLeft; bInvert = !bInvert; } // Now temp is the number with smaller (or equal) scale, and // we can modify it freely without touching original parameters int comp; while ((comp = CompareMantissas(temp, *pdecRight)) < 0 && temp.scale < pdecRight->scale) { // Multiply by an appropriate power of 10 int scaleDiff = pdecRight->scale - temp.scale; if (scaleDiff > largestPower) { // Keep the multiplier representable in 32bit scaleDiff = largestPower; } DWORDLONG power = powersOfTen[scaleDiff - 1]; // Multiply temp's mantissa by power DWORDLONG product = temp.Lo32 * power; ULONG carry = static_cast(product >> 32); temp.Lo32 = static_cast(product); product = temp.Mid32 * power + carry; carry = static_cast(product >> 32); temp.Mid32 = static_cast(product); product = temp.Hi32 * power + carry; if (static_cast(product >> 32)) { // Multiplication overflowed - pdecLeft is clearly larger break; } temp.Hi32 = static_cast(product); temp.scale = (BYTE)(temp.scale + scaleDiff); } if (temp.scale < pdecRight->scale) { comp = 1; } if (bInvert) { comp = -comp; } return (comp > 0 ? VARCMP_GT : comp < 0 ? VARCMP_LT : VARCMP_EQ); } static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL); LVCompareParam* pParam1 = (LVCompareParam*)lParam1; LVCompareParam* pParam2 = (LVCompareParam*)lParam2; LVSortInfo* pInfo = (LVSortInfo*)lParamSort; int nRet = (int)VarDecCmp(&pParam1->decValue, &pParam2->decValue); nRet--; return pInfo->bDescending ? -nRet : nRet; } #endif // !_WIN32_WCE BEGIN_MSG_MAP(CSortListViewImpl) MESSAGE_HANDLER(LVM_INSERTCOLUMN, OnInsertColumn) MESSAGE_HANDLER(LVM_DELETECOLUMN, OnDeleteColumn) NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, OnHeaderItemClick) NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, OnHeaderItemClick) MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange) END_MSG_MAP() LRESULT OnInsertColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { T* pT = static_cast(this); LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam); if(lRet == -1) return -1; WORD wType = 0; m_arrColSortType.Add(wType); int nCount = m_arrColSortType.GetSize(); ATLASSERT(nCount == GetColumnCount()); for(int i = nCount - 1; i > lRet; i--) m_arrColSortType[i] = m_arrColSortType[i - 1]; m_arrColSortType[(int)lRet] = LVCOLSORT_TEXT; if(lRet <= m_iSortColumn) m_iSortColumn++; return lRet; } LRESULT OnDeleteColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { T* pT = static_cast(this); LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam); if(lRet == 0) return 0; int iCol = (int)wParam; if(m_iSortColumn == iCol) m_iSortColumn = -1; else if(m_iSortColumn > iCol) m_iSortColumn--; m_arrColSortType.RemoveAt(iCol); return lRet; } LRESULT OnHeaderItemClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled) { LPNMHEADER p = (LPNMHEADER)pnmh; if(p->iButton == 0) { int iOld = m_iSortColumn; bool bDescending = (m_iSortColumn == p->iItem) ? !m_bSortDescending : false; if(DoSortItems(p->iItem, bDescending)) NotifyParentSortChanged(p->iItem, iOld); } bHandled = FALSE; return 0; } LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { #ifndef _WIN32_WCE if(wParam == SPI_SETNONCLIENTMETRICS) GetSystemSettings(); #else // CE specific wParam; // avoid level 4 warning GetSystemSettings(); #endif // _WIN32_WCE bHandled = FALSE; return 0; } void GetSystemSettings() { if(!m_bCommCtrl6 && !m_bmSort[m_iSortUp].IsNull()) { T* pT = static_cast(this); pT->CreateSortBitmaps(); if(m_iSortColumn != -1) SetSortColumn(m_iSortColumn); } } }; typedef ATL::CWinTraits CSortListViewCtrlTraits; template class ATL_NO_VTABLE CSortListViewCtrlImpl: public ATL::CWindowImpl, public CSortListViewImpl { public: DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName()) bool SortItems(int iCol, bool bDescending = false) { return DoSortItems(iCol, bDescending); } BEGIN_MSG_MAP(CSortListViewCtrlImpl) MESSAGE_HANDLER(LVM_INSERTCOLUMN, CSortListViewImpl::OnInsertColumn) MESSAGE_HANDLER(LVM_DELETECOLUMN, CSortListViewImpl::OnDeleteColumn) NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, CSortListViewImpl::OnHeaderItemClick) NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, CSortListViewImpl::OnHeaderItemClick) MESSAGE_HANDLER(WM_SETTINGCHANGE, CSortListViewImpl::OnSettingChange) END_MSG_MAP() }; class CSortListViewCtrl : public CSortListViewCtrlImpl { public: DECLARE_WND_SUPERCLASS(_T("WTL_SortListViewCtrl"), GetWndClassName()) }; /////////////////////////////////////////////////////////////////////////////// // CTabView - implements tab view window // TabView Notifications #define TBVN_PAGEACTIVATED (0U-741) #define TBVN_CONTEXTMENU (0U-742) // Notification data for TBVN_CONTEXTMENU struct TBVCONTEXTMENUINFO { NMHDR hdr; POINT pt; }; typedef TBVCONTEXTMENUINFO* LPTBVCONTEXTMENUINFO; template class ATL_NO_VTABLE CTabViewImpl : public ATL::CWindowImpl< T, TBase, TWinTraits > { public: DECLARE_WND_CLASS_EX(NULL, 0, COLOR_APPWORKSPACE) // Declarations and enums struct TABVIEWPAGE { HWND hWnd; LPTSTR lpstrTitle; LPVOID pData; }; struct TCITEMEXTRA { TCITEMHEADER tciheader; TABVIEWPAGE tvpage; operator LPTCITEM() { return (LPTCITEM)this; } }; enum { m_nTabID = 1313, m_cxMoveMark = 6, m_cyMoveMark = 3, m_nMenuItemsMax = (ID_WINDOW_TABLAST - ID_WINDOW_TABFIRST + 1) }; // Data members ATL::CContainedWindowT m_tab; int m_cyTabHeight; int m_nActivePage; int m_nInsertItem; POINT m_ptStartDrag; CMenuHandle m_menu; int m_cchTabTextLength; int m_nMenuItemsCount; ATL::CWindow m_wndTitleBar; LPTSTR m_lpstrTitleBarBase; int m_cchTitleBarLength; CImageList m_ilDrag; bool m_bDestroyPageOnRemove:1; bool m_bDestroyImageList:1; bool m_bActivePageMenuItem:1; bool m_bActiveAsDefaultMenuItem:1; bool m_bEmptyMenuItem:1; bool m_bWindowsMenuItem:1; bool m_bNoTabDrag:1; // internal bool m_bTabCapture:1; bool m_bTabDrag:1; bool m_bInternalFont:1; // Constructor/destructor CTabViewImpl() : m_nActivePage(-1), m_cyTabHeight(0), m_tab(this, 1), m_nInsertItem(-1), m_cchTabTextLength(30), m_nMenuItemsCount(10), m_lpstrTitleBarBase(NULL), m_cchTitleBarLength(100), m_bDestroyPageOnRemove(true), m_bDestroyImageList(true), m_bActivePageMenuItem(true), m_bActiveAsDefaultMenuItem(false), m_bEmptyMenuItem(false), m_bWindowsMenuItem(false), m_bNoTabDrag(false), m_bTabCapture(false), m_bTabDrag(false), m_bInternalFont(false) { m_ptStartDrag.x = 0; m_ptStartDrag.y = 0; } ~CTabViewImpl() { delete [] m_lpstrTitleBarBase; } // Message filter function - to be called from PreTranslateMessage of the main window BOOL PreTranslateMessage(MSG* pMsg) { if(IsWindow() == FALSE) return FALSE; BOOL bRet = FALSE; // Check for TabView built-in accelerators (Ctrl+Tab/Ctrl+Shift+Tab - next/previous page) int nCount = GetPageCount(); if(nCount > 0) { bool bControl = (::GetKeyState(VK_CONTROL) < 0); if((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_TAB) && bControl) { if(nCount > 1) { int nPage = m_nActivePage; bool bShift = (::GetKeyState(VK_SHIFT) < 0); if(bShift) nPage = (nPage > 0) ? (nPage - 1) : (nCount - 1); else nPage = ((nPage >= 0) && (nPage < (nCount - 1))) ? (nPage + 1) : 0; SetActivePage(nPage); T* pT = static_cast(this); pT->OnPageActivated(m_nActivePage); } bRet = TRUE; } } // If we are doing drag-drop, check for Escape key that cancels it if(bRet == FALSE) { if(m_bTabCapture && pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE) { ::ReleaseCapture(); bRet = TRUE; } } // Pass the message to the active page if(bRet == FALSE) { if(m_nActivePage != -1) bRet = (BOOL)::SendMessage(GetPageHWND(m_nActivePage), WM_FORWARDMSG, 0, (LPARAM)pMsg); } return bRet; } // Attributes int GetPageCount() const { ATLASSERT(::IsWindow(m_hWnd)); return m_tab.GetItemCount(); } int GetActivePage() const { return m_nActivePage; } void SetActivePage(int nPage) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(IsValidPageIndex(nPage)); T* pT = static_cast(this); SetRedraw(FALSE); if(m_nActivePage != -1) ::ShowWindow(GetPageHWND(m_nActivePage), FALSE); m_nActivePage = nPage; m_tab.SetCurSel(m_nActivePage); ::ShowWindow(GetPageHWND(m_nActivePage), TRUE); pT->UpdateLayout(); SetRedraw(TRUE); RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); if(::GetFocus() != m_tab.m_hWnd) ::SetFocus(GetPageHWND(m_nActivePage)); pT->UpdateTitleBar(); pT->UpdateMenu(); } HIMAGELIST GetImageList() const { ATLASSERT(::IsWindow(m_hWnd)); return m_tab.GetImageList(); } HIMAGELIST SetImageList(HIMAGELIST hImageList) { ATLASSERT(::IsWindow(m_hWnd)); return m_tab.SetImageList(hImageList); } void SetWindowMenu(HMENU hMenu) { ATLASSERT(::IsWindow(m_hWnd)); m_menu = hMenu; T* pT = static_cast(this); pT->UpdateMenu(); } void SetTitleBarWindow(HWND hWnd) { ATLASSERT(::IsWindow(m_hWnd)); delete [] m_lpstrTitleBarBase; m_lpstrTitleBarBase = NULL; m_wndTitleBar = hWnd; if(hWnd == NULL) return; int cchLen = m_wndTitleBar.GetWindowTextLength() + 1; ATLTRY(m_lpstrTitleBarBase = new TCHAR[cchLen]); if(m_lpstrTitleBarBase != NULL) { m_wndTitleBar.GetWindowText(m_lpstrTitleBarBase, cchLen); T* pT = static_cast(this); pT->UpdateTitleBar(); } } // Page attributes HWND GetPageHWND(int nPage) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(IsValidPageIndex(nPage)); TCITEMEXTRA tcix = { 0 }; tcix.tciheader.mask = TCIF_PARAM; m_tab.GetItem(nPage, tcix); return tcix.tvpage.hWnd; } LPCTSTR GetPageTitle(int nPage) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(IsValidPageIndex(nPage)); TCITEMEXTRA tcix = { 0 }; tcix.tciheader.mask = TCIF_PARAM; if(m_tab.GetItem(nPage, tcix) == FALSE) return NULL; return tcix.tvpage.lpstrTitle; } bool SetPageTitle(int nPage, LPCTSTR lpstrTitle) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(IsValidPageIndex(nPage)); T* pT = static_cast(this); int cchBuff = lstrlen(lpstrTitle) + 1; LPTSTR lpstrBuff = NULL; ATLTRY(lpstrBuff = new TCHAR[cchBuff]); if(lpstrBuff == NULL) return false; SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle); TCITEMEXTRA tcix = { 0 }; tcix.tciheader.mask = TCIF_PARAM; if(m_tab.GetItem(nPage, tcix) == FALSE) return false; CTempBuffer buff; LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1); if(lpstrTabText == NULL) return false; delete [] tcix.tvpage.lpstrTitle; pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1); tcix.tciheader.mask = TCIF_TEXT | TCIF_PARAM; tcix.tciheader.pszText = lpstrTabText; tcix.tvpage.lpstrTitle = lpstrBuff; if(m_tab.SetItem(nPage, tcix) == FALSE) return false; pT->UpdateTitleBar(); pT->UpdateMenu(); return true; } LPVOID GetPageData(int nPage) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(IsValidPageIndex(nPage)); TCITEMEXTRA tcix = { 0 }; tcix.tciheader.mask = TCIF_PARAM; m_tab.GetItem(nPage, tcix); return tcix.tvpage.pData; } LPVOID SetPageData(int nPage, LPVOID pData) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(IsValidPageIndex(nPage)); TCITEMEXTRA tcix = { 0 }; tcix.tciheader.mask = TCIF_PARAM; m_tab.GetItem(nPage, tcix); LPVOID pDataOld = tcix.tvpage.pData; tcix.tvpage.pData = pData; m_tab.SetItem(nPage, tcix); return pDataOld; } int GetPageImage(int nPage) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(IsValidPageIndex(nPage)); TCITEMEXTRA tcix = { 0 }; tcix.tciheader.mask = TCIF_IMAGE; m_tab.GetItem(nPage, tcix); return tcix.tciheader.iImage; } int SetPageImage(int nPage, int nImage) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(IsValidPageIndex(nPage)); TCITEMEXTRA tcix = { 0 }; tcix.tciheader.mask = TCIF_IMAGE; m_tab.GetItem(nPage, tcix); int nImageOld = tcix.tciheader.iImage; tcix.tciheader.iImage = nImage; m_tab.SetItem(nPage, tcix); return nImageOld; } // Operations bool AddPage(HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL) { return InsertPage(GetPageCount(), hWndView, lpstrTitle, nImage, pData); } bool InsertPage(int nPage, HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(nPage == GetPageCount() || IsValidPageIndex(nPage)); T* pT = static_cast(this); int cchBuff = lstrlen(lpstrTitle) + 1; LPTSTR lpstrBuff = NULL; ATLTRY(lpstrBuff = new TCHAR[cchBuff]); if(lpstrBuff == NULL) return false; SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle); CTempBuffer buff; LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1); if(lpstrTabText == NULL) return false; pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1); SetRedraw(FALSE); TCITEMEXTRA tcix = { 0 }; tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM; tcix.tciheader.pszText = lpstrTabText; tcix.tciheader.iImage = nImage; tcix.tvpage.hWnd = hWndView; tcix.tvpage.lpstrTitle = lpstrBuff; tcix.tvpage.pData = pData; int nItem = m_tab.InsertItem(nPage, tcix); if(nItem == -1) { delete [] lpstrBuff; SetRedraw(TRUE); return false; } // adjust active page index, if inserted before it if(nPage <= m_nActivePage) m_nActivePage++; SetActivePage(nItem); pT->OnPageActivated(m_nActivePage); if(GetPageCount() == 1) pT->ShowTabControl(true); pT->UpdateLayout(); SetRedraw(TRUE); RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); return true; } void RemovePage(int nPage) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(IsValidPageIndex(nPage)); T* pT = static_cast(this); SetRedraw(FALSE); if(GetPageCount() == 1) pT->ShowTabControl(false); if(m_bDestroyPageOnRemove) ::DestroyWindow(GetPageHWND(nPage)); else ::ShowWindow(GetPageHWND(nPage), FALSE); LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(nPage); delete [] lpstrTitle; ATLVERIFY(m_tab.DeleteItem(nPage) != FALSE); if(m_nActivePage == nPage) { m_nActivePage = -1; if(nPage > 0) { SetActivePage(nPage - 1); } else if(GetPageCount() > 0) { SetActivePage(nPage); } else { SetRedraw(TRUE); Invalidate(); UpdateWindow(); pT->UpdateTitleBar(); pT->UpdateMenu(); } } else { nPage = (nPage < m_nActivePage) ? (m_nActivePage - 1) : m_nActivePage; m_nActivePage = -1; SetActivePage(nPage); } pT->OnPageActivated(m_nActivePage); } void RemoveAllPages() { ATLASSERT(::IsWindow(m_hWnd)); if(GetPageCount() == 0) return; T* pT = static_cast(this); SetRedraw(FALSE); pT->ShowTabControl(false); for(int i = 0; i < GetPageCount(); i++) { if(m_bDestroyPageOnRemove) ::DestroyWindow(GetPageHWND(i)); else ::ShowWindow(GetPageHWND(i), FALSE); LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(i); delete [] lpstrTitle; } m_tab.DeleteAllItems(); m_nActivePage = -1; pT->OnPageActivated(m_nActivePage); SetRedraw(TRUE); Invalidate(); UpdateWindow(); pT->UpdateTitleBar(); pT->UpdateMenu(); } int PageIndexFromHwnd(HWND hWnd) const { int nIndex = -1; for(int i = 0; i < GetPageCount(); i++) { if(GetPageHWND(i) == hWnd) { nIndex = i; break; } } return nIndex; } void BuildWindowMenu(HMENU hMenu, int nMenuItemsCount = 10, bool bEmptyMenuItem = true, bool bWindowsMenuItem = true, bool bActivePageMenuItem = true, bool bActiveAsDefaultMenuItem = false) { ATLASSERT(::IsWindow(m_hWnd)); CMenuHandle menu = hMenu; T* pT = static_cast(this); pT; // avoid level 4 warning int nFirstPos = 0; // Find first menu item in our range #ifndef _WIN32_WCE for(nFirstPos = 0; nFirstPos < menu.GetMenuItemCount(); nFirstPos++) { UINT nID = menu.GetMenuItemID(nFirstPos); if((nID >= ID_WINDOW_TABFIRST && nID <= ID_WINDOW_TABLAST) || nID == ID_WINDOW_SHOWTABLIST) break; } #else // CE specific for(nFirstPos = 0; ; nFirstPos++) { CMenuItemInfo mii; mii.fMask = MIIM_ID; BOOL bRet = menu.GetMenuItemInfo(nFirstPos, TRUE, &mii); if(bRet == FALSE) break; if((mii.wID >= ID_WINDOW_TABFIRST && mii.wID <= ID_WINDOW_TABLAST) || mii.wID == ID_WINDOW_SHOWTABLIST) break; } #endif // _WIN32_WCE // Remove all menu items for tab pages BOOL bRet = TRUE; while(bRet != FALSE) bRet = menu.DeleteMenu(nFirstPos, MF_BYPOSITION); // Add separator if it's not already there int nPageCount = GetPageCount(); if((bWindowsMenuItem || (nPageCount > 0)) && (nFirstPos > 0)) { CMenuItemInfo mii; mii.fMask = MIIM_TYPE; menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii); if((nFirstPos <= 0) || ((mii.fType & MFT_SEPARATOR) == 0)) { menu.AppendMenu(MF_SEPARATOR); nFirstPos++; } } // Add menu items for all pages if(nPageCount > 0) { // Append menu items for all pages const int cchPrefix = 3; // 2 digits + space nMenuItemsCount = __min(__min(nPageCount, nMenuItemsCount), (int)m_nMenuItemsMax); ATLASSERT(nMenuItemsCount < 100); // 2 digits only if(nMenuItemsCount >= 100) nMenuItemsCount = 99; for(int i = 0; i < nMenuItemsCount; i++) { LPCTSTR lpstrTitle = GetPageTitle(i); int nLen = lstrlen(lpstrTitle); CTempBuffer buff; LPTSTR lpstrText = buff.Allocate(cchPrefix + nLen + 1); ATLASSERT(lpstrText != NULL); if(lpstrText != NULL) { LPCTSTR lpstrFormat = (i < 9) ? _T("&%i %s") : _T("%i %s"); SecureHelper::wsprintf_x(lpstrText, cchPrefix + nLen + 1, lpstrFormat, i + 1, lpstrTitle); menu.AppendMenu(MF_STRING, ID_WINDOW_TABFIRST + i, lpstrText); } } // Mark active page if(bActivePageMenuItem && (m_nActivePage != -1)) { #ifndef _WIN32_WCE if(bActiveAsDefaultMenuItem) { menu.SetMenuDefaultItem((UINT)-1, TRUE); menu.SetMenuDefaultItem(nFirstPos + m_nActivePage, TRUE); } else #else // CE specific bActiveAsDefaultMenuItem; // avoid level 4 warning #endif // _WIN32_WCE { menu.CheckMenuRadioItem(nFirstPos, nFirstPos + nMenuItemsCount, nFirstPos + m_nActivePage, MF_BYPOSITION); } } } else { if(bEmptyMenuItem) { menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_TABFIRST, pT->GetEmptyListText()); menu.EnableMenuItem(ID_WINDOW_TABFIRST, MF_GRAYED); } // Remove separator if nothing else is there if(!bEmptyMenuItem && !bWindowsMenuItem && (nFirstPos > 0)) { CMenuItemInfo mii; mii.fMask = MIIM_TYPE; menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii); if((mii.fType & MFT_SEPARATOR) != 0) menu.DeleteMenu(nFirstPos - 1, MF_BYPOSITION); } } // Add "Windows..." menu item if(bWindowsMenuItem) menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_SHOWTABLIST, pT->GetWindowsMenuItemText()); } BOOL SubclassWindow(HWND hWnd) { #if (_MSC_VER >= 1300) BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); #else // !(_MSC_VER >= 1300) typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass; BOOL bRet = _baseClass::SubclassWindow(hWnd); #endif // !(_MSC_VER >= 1300) if(bRet != FALSE) { T* pT = static_cast(this); pT->CreateTabControl(); pT->UpdateLayout(); } return bRet; } // Message map and handlers BEGIN_MSG_MAP(CTabViewImpl) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) MESSAGE_HANDLER(WM_GETFONT, OnGetFont) MESSAGE_HANDLER(WM_SETFONT, OnSetFont) NOTIFY_HANDLER(m_nTabID, TCN_SELCHANGE, OnTabChanged) NOTIFY_ID_HANDLER(m_nTabID, OnTabNotification) #ifndef _WIN32_WCE NOTIFY_CODE_HANDLER(TTN_GETDISPINFO, OnTabGetDispInfo) #endif // !_WIN32_WCE FORWARD_NOTIFICATIONS() ALT_MSG_MAP(1) // tab control MESSAGE_HANDLER(WM_LBUTTONDOWN, OnTabLButtonDown) MESSAGE_HANDLER(WM_LBUTTONUP, OnTabLButtonUp) MESSAGE_HANDLER(WM_CAPTURECHANGED, OnTabCaptureChanged) MESSAGE_HANDLER(WM_MOUSEMOVE, OnTabMouseMove) MESSAGE_HANDLER(WM_RBUTTONUP, OnTabRButtonUp) MESSAGE_HANDLER(WM_SYSKEYDOWN, OnTabSysKeyDown) END_MSG_MAP() LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->CreateTabControl(); return 0; } LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { RemoveAllPages(); if(m_bDestroyImageList) { CImageList il = m_tab.SetImageList(NULL); if(il.m_hImageList != NULL) il.Destroy(); } if(m_bInternalFont) { HFONT hFont = m_tab.GetFont(); m_tab.SetFont(NULL, FALSE); ::DeleteObject(hFont); m_bInternalFont = false; } return 0; } LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->UpdateLayout(); return 0; } LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { if(m_nActivePage != -1) ::SetFocus(GetPageHWND(m_nActivePage)); return 0; } LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return m_tab.SendMessage(WM_GETFONT); } LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { if(m_bInternalFont) { HFONT hFont = m_tab.GetFont(); m_tab.SetFont(NULL, FALSE); ::DeleteObject(hFont); m_bInternalFont = false; } m_tab.SendMessage(WM_SETFONT, wParam, lParam); T* pT = static_cast(this); m_cyTabHeight = pT->CalcTabHeight(); if((BOOL)lParam != FALSE) pT->UpdateLayout(); return 0; } LRESULT OnTabChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) { SetActivePage(m_tab.GetCurSel()); T* pT = static_cast(this); pT->OnPageActivated(m_nActivePage); return 0; } LRESULT OnTabNotification(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) { // nothing to do - this just blocks all tab control // notifications from being propagated further return 0; } #ifndef _WIN32_WCE LRESULT OnTabGetDispInfo(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled) { LPNMTTDISPINFO pTTDI = (LPNMTTDISPINFO)pnmh; if(pTTDI->hdr.hwndFrom == m_tab.GetTooltips()) { T* pT = static_cast(this); pT->UpdateTooltipText(pTTDI); } else { bHandled = FALSE; } return 0; } #endif // !_WIN32_WCE // Tab control message handlers LRESULT OnTabLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { if(!m_bNoTabDrag && (m_tab.GetItemCount() > 1)) { m_bTabCapture = true; m_tab.SetCapture(); m_ptStartDrag.x = GET_X_LPARAM(lParam); m_ptStartDrag.y = GET_Y_LPARAM(lParam); } bHandled = FALSE; return 0; } LRESULT OnTabLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { if(m_bTabCapture) { if(m_bTabDrag) { TCHITTESTINFO hti = { 0 }; hti.pt.x = GET_X_LPARAM(lParam); hti.pt.y = GET_Y_LPARAM(lParam); int nItem = m_tab.HitTest(&hti); if(nItem != -1) MovePage(m_nActivePage, nItem); } ::ReleaseCapture(); } bHandled = FALSE; return 0; } LRESULT OnTabCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(m_bTabCapture) { m_bTabCapture = false; if(m_bTabDrag) { m_bTabDrag = false; T* pT = static_cast(this); pT->DrawMoveMark(-1); #ifndef _WIN32_WCE m_ilDrag.DragLeave(GetDesktopWindow()); #endif // !_WIN32_WCE m_ilDrag.EndDrag(); m_ilDrag.Destroy(); m_ilDrag.m_hImageList = NULL; } } bHandled = FALSE; return 0; } LRESULT OnTabMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { bHandled = FALSE; if(m_bTabCapture) { POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; if(!m_bTabDrag) { #ifndef _WIN32_WCE if(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CXDRAG) || abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CYDRAG)) #else // CE specific if(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= 4 || abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= 4) #endif // _WIN32_WCE { T* pT = static_cast(this); pT->GenerateDragImage(m_nActivePage); int cxCursor = ::GetSystemMetrics(SM_CXCURSOR); int cyCursor = ::GetSystemMetrics(SM_CYCURSOR); m_ilDrag.BeginDrag(0, -(cxCursor / 2), -(cyCursor / 2)); #ifndef _WIN32_WCE POINT ptEnter = m_ptStartDrag; m_tab.ClientToScreen(&ptEnter); m_ilDrag.DragEnter(GetDesktopWindow(), ptEnter); #endif // !_WIN32_WCE m_bTabDrag = true; } } if(m_bTabDrag) { TCHITTESTINFO hti = { 0 }; hti.pt = pt; int nItem = m_tab.HitTest(&hti); T* pT = static_cast(this); pT->SetMoveCursor(nItem != -1); if(m_nInsertItem != nItem) pT->DrawMoveMark(nItem); m_ilDrag.DragShowNolock((nItem != -1) ? TRUE : FALSE); m_tab.ClientToScreen(&pt); m_ilDrag.DragMove(pt); bHandled = TRUE; } } return 0; } LRESULT OnTabRButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/) { TCHITTESTINFO hti = { 0 }; hti.pt.x = GET_X_LPARAM(lParam); hti.pt.y = GET_Y_LPARAM(lParam); int nItem = m_tab.HitTest(&hti); if(nItem != -1) { T* pT = static_cast(this); pT->OnContextMenu(nItem, hti.pt); } return 0; } LRESULT OnTabSysKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { bool bShift = (::GetKeyState(VK_SHIFT) < 0); if(wParam == VK_F10 && bShift) { if(m_nActivePage != -1) { RECT rect = { 0 }; m_tab.GetItemRect(m_nActivePage, &rect); POINT pt = { rect.left, rect.bottom }; T* pT = static_cast(this); pT->OnContextMenu(m_nActivePage, pt); } } else { bHandled = FALSE; } return 0; } // Implementation helpers bool IsValidPageIndex(int nPage) const { return (nPage >= 0 && nPage < GetPageCount()); } bool MovePage(int nMovePage, int nInsertBeforePage) { ATLASSERT(IsValidPageIndex(nMovePage)); ATLASSERT(IsValidPageIndex(nInsertBeforePage)); if(!IsValidPageIndex(nMovePage) || !IsValidPageIndex(nInsertBeforePage)) return false; if(nMovePage == nInsertBeforePage) return true; // nothing to do CTempBuffer buff; LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1); if(lpstrTabText == NULL) return false; TCITEMEXTRA tcix = { 0 }; tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM; tcix.tciheader.pszText = lpstrTabText; tcix.tciheader.cchTextMax = m_cchTabTextLength + 1; BOOL bRet = m_tab.GetItem(nMovePage, tcix); ATLASSERT(bRet != FALSE); if(bRet == FALSE) return false; int nInsertItem = (nInsertBeforePage > nMovePage) ? nInsertBeforePage + 1 : nInsertBeforePage; int nNewItem = m_tab.InsertItem(nInsertItem, tcix); ATLASSERT(nNewItem == nInsertItem); if(nNewItem != nInsertItem) { ATLVERIFY(m_tab.DeleteItem(nNewItem)); return false; } if(nMovePage > nInsertBeforePage) ATLVERIFY(m_tab.DeleteItem(nMovePage + 1) != FALSE); else if(nMovePage < nInsertBeforePage) ATLVERIFY(m_tab.DeleteItem(nMovePage) != FALSE); SetActivePage(nInsertBeforePage); T* pT = static_cast(this); pT->OnPageActivated(m_nActivePage); return true; } // Implementation overrideables bool CreateTabControl() { #ifndef _WIN32_WCE m_tab.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_TOOLTIPS, 0, m_nTabID); #else // CE specific m_tab.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, m_nTabID); #endif // _WIN32_WCE ATLASSERT(m_tab.m_hWnd != NULL); if(m_tab.m_hWnd == NULL) return false; m_tab.SetFont(AtlCreateControlFont()); m_bInternalFont = true; m_tab.SetItemExtra(sizeof(TABVIEWPAGE)); T* pT = static_cast(this); m_cyTabHeight = pT->CalcTabHeight(); return true; } int CalcTabHeight() { int nCount = m_tab.GetItemCount(); TCITEMEXTRA tcix = { 0 }; tcix.tciheader.mask = TCIF_TEXT; tcix.tciheader.pszText = _T("NS"); int nIndex = m_tab.InsertItem(nCount, tcix); RECT rect = { 0, 0, 1000, 1000 }; m_tab.AdjustRect(FALSE, &rect); RECT rcWnd = { 0, 0, 1000, rect.top }; ::AdjustWindowRectEx(&rcWnd, m_tab.GetStyle(), FALSE, m_tab.GetExStyle()); int nHeight = rcWnd.bottom - rcWnd.top; m_tab.DeleteItem(nIndex); return nHeight; } void ShowTabControl(bool bShow) { m_tab.ShowWindow(bShow ? SW_SHOWNOACTIVATE : SW_HIDE); T* pT = static_cast(this); pT->UpdateLayout(); } void UpdateLayout() { RECT rect = { 0 }; GetClientRect(&rect); int cyOffset = 0; if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0)) { m_tab.SetWindowPos(NULL, 0, 0, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER); cyOffset = m_cyTabHeight; } if(m_nActivePage != -1) ::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, cyOffset, rect.right - rect.left, rect.bottom - rect.top - cyOffset, SWP_NOZORDER); } void UpdateMenu() { if(m_menu.m_hMenu != NULL) BuildWindowMenu(m_menu, m_nMenuItemsCount, m_bEmptyMenuItem, m_bWindowsMenuItem, m_bActivePageMenuItem, m_bActiveAsDefaultMenuItem); } void UpdateTitleBar() { if(!m_wndTitleBar.IsWindow() || m_lpstrTitleBarBase == NULL) return; // nothing to do if(m_nActivePage != -1) { T* pT = static_cast(this); LPCTSTR lpstrTitle = pT->GetPageTitle(m_nActivePage); LPCTSTR lpstrDivider = pT->GetTitleDividerText(); int cchBuffer = m_cchTitleBarLength + lstrlen(lpstrDivider) + lstrlen(m_lpstrTitleBarBase) + 1; CTempBuffer buff; LPTSTR lpstrPageTitle = buff.Allocate(cchBuffer); ATLASSERT(lpstrPageTitle != NULL); if(lpstrPageTitle != NULL) { pT->ShortenTitle(lpstrTitle, lpstrPageTitle, m_cchTitleBarLength + 1); SecureHelper::strcat_x(lpstrPageTitle, cchBuffer, lpstrDivider); SecureHelper::strcat_x(lpstrPageTitle, cchBuffer, m_lpstrTitleBarBase); } else { lpstrPageTitle = m_lpstrTitleBarBase; } m_wndTitleBar.SetWindowText(lpstrPageTitle); } else { m_wndTitleBar.SetWindowText(m_lpstrTitleBarBase); } } void DrawMoveMark(int nItem) { T* pT = static_cast(this); if(m_nInsertItem != -1) { RECT rect = { 0 }; pT->GetMoveMarkRect(rect); m_tab.InvalidateRect(&rect); } m_nInsertItem = nItem; if(m_nInsertItem != -1) { CClientDC dc(m_tab.m_hWnd); RECT rect = { 0 }; pT->GetMoveMarkRect(rect); CPen pen; pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_WINDOWTEXT)); CBrush brush; brush.CreateSolidBrush(::GetSysColor(COLOR_WINDOWTEXT)); HPEN hPenOld = dc.SelectPen(pen); HBRUSH hBrushOld = dc.SelectBrush(brush); int x = rect.left; int y = rect.top; POINT ptsTop[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y + m_cyMoveMark } }; dc.Polygon(ptsTop, 3); y = rect.bottom - 1; POINT ptsBottom[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y - m_cyMoveMark } }; dc.Polygon(ptsBottom, 3); dc.SelectPen(hPenOld); dc.SelectBrush(hBrushOld); } } void GetMoveMarkRect(RECT& rect) const { m_tab.GetClientRect(&rect); RECT rcItem = { 0 }; m_tab.GetItemRect(m_nInsertItem, &rcItem); if(m_nInsertItem <= m_nActivePage) { rect.left = rcItem.left - m_cxMoveMark / 2 - 1; rect.right = rcItem.left + m_cxMoveMark / 2; } else { rect.left = rcItem.right - m_cxMoveMark / 2 - 1; rect.right = rcItem.right + m_cxMoveMark / 2; } } void SetMoveCursor(bool bCanMove) { ::SetCursor(::LoadCursor(NULL, bCanMove ? IDC_ARROW : IDC_NO)); } void GenerateDragImage(int nItem) { ATLASSERT(IsValidPageIndex(nItem)); #ifndef _WIN32_WCE RECT rcItem = { 0 }; m_tab.GetItemRect(nItem, &rcItem); ::InflateRect(&rcItem, 2, 2); // make bigger to cover selected item #else // CE specific nItem; // avoid level 4 warning RECT rcItem = { 0, 0, 40, 20 }; #endif // _WIN32_WCE ATLASSERT(m_ilDrag.m_hImageList == NULL); m_ilDrag.Create(rcItem.right - rcItem.left, rcItem.bottom - rcItem.top, ILC_COLORDDB | ILC_MASK, 1, 1); CClientDC dc(m_hWnd); CDC dcMem; dcMem.CreateCompatibleDC(dc); ATLASSERT(dcMem.m_hDC != NULL); dcMem.SetViewportOrg(-rcItem.left, -rcItem.top); CBitmap bmp; bmp.CreateCompatibleBitmap(dc, rcItem.right - rcItem.left, rcItem.bottom - rcItem.top); ATLASSERT(bmp.m_hBitmap != NULL); HBITMAP hBmpOld = dcMem.SelectBitmap(bmp); #ifndef _WIN32_WCE m_tab.SendMessage(WM_PRINTCLIENT, (WPARAM)dcMem.m_hDC); #else // CE specific dcMem.Rectangle(&rcItem); #endif // _WIN32_WCE dcMem.SelectBitmap(hBmpOld); ATLVERIFY(m_ilDrag.Add(bmp.m_hBitmap, RGB(255, 0, 255)) != -1); } void ShortenTitle(LPCTSTR lpstrTitle, LPTSTR lpstrShortTitle, int cchShortTitle) { if(lstrlen(lpstrTitle) >= cchShortTitle) { LPCTSTR lpstrEllipsis = _T("..."); int cchEllipsis = lstrlen(lpstrEllipsis); SecureHelper::strncpy_x(lpstrShortTitle, cchShortTitle, lpstrTitle, cchShortTitle - cchEllipsis - 1); SecureHelper::strcat_x(lpstrShortTitle, cchShortTitle, lpstrEllipsis); } else { SecureHelper::strcpy_x(lpstrShortTitle, cchShortTitle, lpstrTitle); } } #ifndef _WIN32_WCE void UpdateTooltipText(LPNMTTDISPINFO pTTDI) { ATLASSERT(pTTDI != NULL); pTTDI->lpszText = (LPTSTR)GetPageTitle((int)pTTDI->hdr.idFrom); } #endif // !_WIN32_WCE // Text for menu items and title bar - override to provide different strings static LPCTSTR GetEmptyListText() { return _T("(Empty)"); } static LPCTSTR GetWindowsMenuItemText() { return _T("&Windows..."); } static LPCTSTR GetTitleDividerText() { return _T(" - "); } // Notifications - override to provide different behavior void OnPageActivated(int nPage) { NMHDR nmhdr = { 0 }; nmhdr.hwndFrom = m_hWnd; nmhdr.idFrom = nPage; nmhdr.code = TBVN_PAGEACTIVATED; ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr); } void OnContextMenu(int nPage, POINT pt) { m_tab.ClientToScreen(&pt); TBVCONTEXTMENUINFO cmi = { 0 }; cmi.hdr.hwndFrom = m_hWnd; cmi.hdr.idFrom = nPage; cmi.hdr.code = TBVN_CONTEXTMENU; cmi.pt = pt; ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&cmi); } }; class CTabView : public CTabViewImpl { public: DECLARE_WND_CLASS_EX(_T("WTL_TabView"), 0, COLOR_APPWORKSPACE) }; }; // namespace WTL #endif // __ATLCTRLX_H__ ================================================ FILE: src/Setup/wtl90/atlddx.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLDDX_H__ #define __ATLDDX_H__ #pragma once #ifndef __ATLAPP_H__ #error atlddx.h requires atlapp.h to be included first #endif #if defined(_ATL_USE_DDX_FLOAT) && defined(_ATL_MIN_CRT) #error Cannot use floating point DDX with _ATL_MIN_CRT defined #endif // defined(_ATL_USE_DDX_FLOAT) && defined(_ATL_MIN_CRT) #ifdef _ATL_USE_DDX_FLOAT #include #endif // _ATL_USE_DDX_FLOAT /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CWinDataExchange namespace WTL { // Constants #define DDX_LOAD FALSE #define DDX_SAVE TRUE // DDX map macros #define BEGIN_DDX_MAP(thisClass) \ BOOL DoDataExchange(BOOL bSaveAndValidate = FALSE, UINT nCtlID = (UINT)-1) \ { \ bSaveAndValidate; \ nCtlID; #define DDX_TEXT(nID, var) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate)) \ return FALSE; \ } #define DDX_TEXT_LEN(nID, var, len) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate, TRUE, len)) \ return FALSE; \ } #define DDX_INT(nID, var) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Int(nID, var, TRUE, bSaveAndValidate)) \ return FALSE; \ } #define DDX_INT_RANGE(nID, var, min, max) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Int(nID, var, TRUE, bSaveAndValidate, TRUE, min, max)) \ return FALSE; \ } #define DDX_UINT(nID, var) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Int(nID, var, FALSE, bSaveAndValidate)) \ return FALSE; \ } #define DDX_UINT_RANGE(nID, var, min, max) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Int(nID, var, FALSE, bSaveAndValidate, TRUE, min, max)) \ return FALSE; \ } #ifdef _ATL_USE_DDX_FLOAT #define DDX_FLOAT(nID, var) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Float(nID, var, bSaveAndValidate)) \ return FALSE; \ } #define DDX_FLOAT_RANGE(nID, var, min, max) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max)) \ return FALSE; \ } #define DDX_FLOAT_P(nID, var, precision) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Float(nID, var, bSaveAndValidate, FALSE, 0, 0, precision)) \ return FALSE; \ } #define DDX_FLOAT_P_RANGE(nID, var, min, max, precision) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Float(nID, var, bSaveAndValidate, TRUE, min, max, precision)) \ return FALSE; \ } #endif // _ATL_USE_DDX_FLOAT #define DDX_CONTROL(nID, obj) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ DDX_Control(nID, obj, bSaveAndValidate); #define DDX_CONTROL_HANDLE(nID, obj) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ DDX_Control_Handle(nID, obj, bSaveAndValidate); #define DDX_CHECK(nID, var) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ DDX_Check(nID, var, bSaveAndValidate); #define DDX_RADIO(nID, var) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ DDX_Radio(nID, var, bSaveAndValidate); #define END_DDX_MAP() \ return TRUE; \ } // DDX support for Tab, Combo, ListBox and ListView selection index // Note: Specialized versions require atlctrls.h to be included first #if (_MSC_VER >= 1300) #define DDX_INDEX(CtrlClass, nID, var) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ DDX_Index(nID, var, bSaveAndValidate); #ifdef __ATLCTRLS_H__ #define DDX_TAB_INDEX(nID, var) DDX_INDEX(WTL::CTabCtrl, nID, var) #ifndef WIN32_PLATFORM_WFSP // No COMBOBOX on SmartPhones #define DDX_COMBO_INDEX(nID, var) DDX_INDEX(WTL::CComboBox, nID, var) #endif #define DDX_LISTBOX_INDEX(nID, var) DDX_INDEX(WTL::CListBox, nID, var) #define DDX_LISTVIEW_INDEX(nID, var) DDX_INDEX(WTL::CListViewCtrl, nID, var) #endif // __ATLCTRLS_H__ #endif // (_MSC_VER >= 1300) /////////////////////////////////////////////////////////////////////////////// // CWinDataExchange - provides support for DDX template class CWinDataExchange { public: // Data exchange method - override in your derived class BOOL DoDataExchange(BOOL /*bSaveAndValidate*/ = FALSE, UINT /*nCtlID*/ = (UINT)-1) { // this one should never be called, override it in // your derived class by implementing DDX map ATLASSERT(FALSE); return FALSE; } // Helpers for validation error reporting enum _XDataType { ddxDataNull = 0, ddxDataText = 1, ddxDataInt = 2, ddxDataFloat = 3, ddxDataDouble = 4 }; struct _XTextData { int nLength; int nMaxLength; }; struct _XIntData { long nVal; long nMin; long nMax; }; struct _XFloatData { double nVal; double nMin; double nMax; }; struct _XData { _XDataType nDataType; union { _XTextData textData; _XIntData intData; _XFloatData floatData; }; }; // Text exchange BOOL DDX_Text(UINT nID, LPTSTR lpstrText, int cbSize, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) { T* pT = static_cast(this); BOOL bSuccess = TRUE; if(bSave) { HWND hWndCtrl = pT->GetDlgItem(nID); int nRetLen = ::GetWindowText(hWndCtrl, lpstrText, cbSize / sizeof(TCHAR)); if(nRetLen < ::GetWindowTextLength(hWndCtrl)) bSuccess = FALSE; } else { ATLASSERT(!bValidate || lstrlen(lpstrText) <= nLength); bSuccess = pT->SetDlgItemText(nID, lpstrText); } if(!bSuccess) { pT->OnDataExchangeError(nID, bSave); } else if(bSave && bValidate) // validation { ATLASSERT(nLength > 0); if(lstrlen(lpstrText) > nLength) { _XData data = { ddxDataText }; data.textData.nLength = lstrlen(lpstrText); data.textData.nMaxLength = nLength; pT->OnDataValidateError(nID, bSave, data); bSuccess = FALSE; } } return bSuccess; } BOOL DDX_Text(UINT nID, BSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) { T* pT = static_cast(this); BOOL bSuccess = TRUE; if(bSave) { bSuccess = pT->GetDlgItemText(nID, bstrText); } else { USES_CONVERSION; LPTSTR lpstrText = OLE2T(bstrText); ATLASSERT(!bValidate || lstrlen(lpstrText) <= nLength); bSuccess = pT->SetDlgItemText(nID, lpstrText); } if(!bSuccess) { pT->OnDataExchangeError(nID, bSave); } else if(bSave && bValidate) // validation { ATLASSERT(nLength > 0); if((int)::SysStringLen(bstrText) > nLength) { _XData data = { ddxDataText }; data.textData.nLength = (int)::SysStringLen(bstrText); data.textData.nMaxLength = nLength; pT->OnDataValidateError(nID, bSave, data); bSuccess = FALSE; } } return bSuccess; } BOOL DDX_Text(UINT nID, ATL::CComBSTR& bstrText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) { T* pT = static_cast(this); BOOL bSuccess = TRUE; if(bSave) { bSuccess = pT->GetDlgItemText(nID, (BSTR&)bstrText); } else { USES_CONVERSION; LPTSTR lpstrText = OLE2T(bstrText); ATLASSERT(!bValidate || lstrlen(lpstrText) <= nLength); bSuccess = pT->SetDlgItemText(nID, lpstrText); } if(!bSuccess) { pT->OnDataExchangeError(nID, bSave); } else if(bSave && bValidate) // validation { ATLASSERT(nLength > 0); if((int)bstrText.Length() > nLength) { _XData data = { ddxDataText }; data.textData.nLength = (int)bstrText.Length(); data.textData.nMaxLength = nLength; pT->OnDataValidateError(nID, bSave, data); bSuccess = FALSE; } } return bSuccess; } #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) BOOL DDX_Text(UINT nID, _CSTRING_NS::CString& strText, int /*cbSize*/, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) { T* pT = static_cast(this); BOOL bSuccess = TRUE; if(bSave) { HWND hWndCtrl = pT->GetDlgItem(nID); int nLen = ::GetWindowTextLength(hWndCtrl); int nRetLen = -1; LPTSTR lpstr = strText.GetBufferSetLength(nLen); if(lpstr != NULL) { nRetLen = ::GetWindowText(hWndCtrl, lpstr, nLen + 1); strText.ReleaseBuffer(); } if(nRetLen < nLen) bSuccess = FALSE; } else { bSuccess = pT->SetDlgItemText(nID, strText); } if(!bSuccess) { pT->OnDataExchangeError(nID, bSave); } else if(bSave && bValidate) // validation { ATLASSERT(nLength > 0); if(strText.GetLength() > nLength) { _XData data = { ddxDataText }; data.textData.nLength = strText.GetLength(); data.textData.nMaxLength = nLength; pT->OnDataValidateError(nID, bSave, data); bSuccess = FALSE; } } return bSuccess; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) // Numeric exchange template BOOL DDX_Int(UINT nID, Type& nVal, BOOL bSigned, BOOL bSave, BOOL bValidate = FALSE, Type nMin = 0, Type nMax = 0) { T* pT = static_cast(this); BOOL bSuccess = TRUE; if(bSave) { nVal = (Type)pT->GetDlgItemInt(nID, &bSuccess, bSigned); } else { ATLASSERT(!bValidate || nVal >= nMin && nVal <= nMax); bSuccess = pT->SetDlgItemInt(nID, nVal, bSigned); } if(!bSuccess) { pT->OnDataExchangeError(nID, bSave); } else if(bSave && bValidate) // validation { ATLASSERT(nMin != nMax); if(nVal < nMin || nVal > nMax) { _XData data = { ddxDataInt }; data.intData.nVal = (long)nVal; data.intData.nMin = (long)nMin; data.intData.nMax = (long)nMax; pT->OnDataValidateError(nID, bSave, data); bSuccess = FALSE; } } return bSuccess; } // Float exchange #ifdef _ATL_USE_DDX_FLOAT static BOOL _AtlSimpleFloatParse(LPCTSTR lpszText, double& d) { ATLASSERT(lpszText != NULL); while (*lpszText == _T(' ') || *lpszText == _T('\t')) lpszText++; TCHAR chFirst = lpszText[0]; d = _tcstod(lpszText, (LPTSTR*)&lpszText); if (d == 0.0 && chFirst != _T('0')) return FALSE; // could not convert while (*lpszText == _T(' ') || *lpszText == _T('\t')) lpszText++; if (*lpszText != _T('\0')) return FALSE; // not terminated properly return TRUE; } BOOL DDX_Float(UINT nID, float& nVal, BOOL bSave, BOOL bValidate = FALSE, float nMin = 0.F, float nMax = 0.F, int nPrecision = FLT_DIG) { T* pT = static_cast(this); BOOL bSuccess = TRUE; const int cchBuff = 32; TCHAR szBuff[cchBuff] = { 0 }; if(bSave) { pT->GetDlgItemText(nID, szBuff, cchBuff); double d = 0; if(_AtlSimpleFloatParse(szBuff, d)) nVal = (float)d; else bSuccess = FALSE; } else { ATLASSERT(!bValidate || nVal >= nMin && nVal <= nMax); SecureHelper::sprintf_x(szBuff, cchBuff, _T("%.*g"), nPrecision, nVal); bSuccess = pT->SetDlgItemText(nID, szBuff); } if(!bSuccess) { pT->OnDataExchangeError(nID, bSave); } else if(bSave && bValidate) // validation { ATLASSERT(nMin != nMax); if(nVal < nMin || nVal > nMax) { _XData data = { ddxDataFloat }; data.floatData.nVal = (double)nVal; data.floatData.nMin = (double)nMin; data.floatData.nMax = (double)nMax; pT->OnDataValidateError(nID, bSave, data); bSuccess = FALSE; } } return bSuccess; } BOOL DDX_Float(UINT nID, double& nVal, BOOL bSave, BOOL bValidate = FALSE, double nMin = 0., double nMax = 0., int nPrecision = DBL_DIG) { T* pT = static_cast(this); BOOL bSuccess = TRUE; const int cchBuff = 32; TCHAR szBuff[cchBuff] = { 0 }; if(bSave) { pT->GetDlgItemText(nID, szBuff, cchBuff); double d = 0; if(_AtlSimpleFloatParse(szBuff, d)) nVal = d; else bSuccess = FALSE; } else { ATLASSERT(!bValidate || nVal >= nMin && nVal <= nMax); SecureHelper::sprintf_x(szBuff, cchBuff, _T("%.*g"), nPrecision, nVal); bSuccess = pT->SetDlgItemText(nID, szBuff); } if(!bSuccess) { pT->OnDataExchangeError(nID, bSave); } else if(bSave && bValidate) // validation { ATLASSERT(nMin != nMax); if(nVal < nMin || nVal > nMax) { _XData data = { ddxDataFloat }; data.floatData.nVal = nVal; data.floatData.nMin = nMin; data.floatData.nMax = nMax; pT->OnDataValidateError(nID, bSave, data); bSuccess = FALSE; } } return bSuccess; } #endif // _ATL_USE_DDX_FLOAT // Full control subclassing (for CWindowImpl derived controls) template void DDX_Control(UINT nID, TControl& ctrl, BOOL bSave) { if(!bSave && ctrl.m_hWnd == NULL) { T* pT = static_cast(this); ctrl.SubclassWindow(pT->GetDlgItem(nID)); } } // Simple control attaching (for HWND wrapper controls) template void DDX_Control_Handle(UINT nID, TControl& ctrl, BOOL bSave) { if(!bSave && ctrl.m_hWnd == NULL) { T* pT = static_cast(this); ctrl = pT->GetDlgItem(nID); } } // Control state void DDX_Check(UINT nID, int& nValue, BOOL bSave) { T* pT = static_cast(this); HWND hWndCtrl = pT->GetDlgItem(nID); if(bSave) { nValue = (int)::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L); ATLASSERT(nValue >= 0 && nValue <= 2); } else { if(nValue < 0 || nValue > 2) { ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - dialog data checkbox value (%d) out of range.\n"), nValue); nValue = 0; // default to off } ::SendMessage(hWndCtrl, BM_SETCHECK, nValue, 0L); } } // variant that supports bool (checked/not-checked, no intermediate state) void DDX_Check(UINT nID, bool& bCheck, BOOL bSave) { int nValue = bCheck ? 1 : 0; DDX_Check(nID, nValue, bSave); if(bSave) { if(nValue == 2) ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - checkbox state (%d) out of supported range.\n"), nValue); bCheck = (nValue == 1); } } void DDX_Radio(UINT nID, int& nValue, BOOL bSave) { T* pT = static_cast(this); HWND hWndCtrl = pT->GetDlgItem(nID); ATLASSERT(hWndCtrl != NULL); // must be first in a group of auto radio buttons ATLASSERT(::GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP); ATLASSERT(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON); if(bSave) nValue = -1; // value if none found // walk all children in group int nButton = 0; do { if(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON) { // control in group is a radio button if(bSave) { if(::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L) != 0) { ATLASSERT(nValue == -1); // only set once nValue = nButton; } } else { // select button ::SendMessage(hWndCtrl, BM_SETCHECK, (nButton == nValue), 0L); } nButton++; } else { ATLTRACE2(atlTraceUI, 0, _T("ATL: Warning - skipping non-radio button in group.\n")); } hWndCtrl = ::GetWindow(hWndCtrl, GW_HWNDNEXT); } while (hWndCtrl != NULL && !(GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP)); } // DDX support for Tab, Combo, ListBox and ListView selection index #if (_MSC_VER >= 1300) template INT _getSel(TCtrl& tCtrl) { return tCtrl.GetCurSel(); } template void _setSel(TCtrl& tCtrl, INT iSel) { if(iSel < 0) tCtrl.SetCurSel(-1); else tCtrl.SetCurSel(iSel); } #ifdef __ATLCTRLS_H__ // ListViewCtrl specialization template <> INT _getSel(WTL::CListViewCtrl& tCtrl) { return tCtrl.GetSelectedIndex(); } template <> void _setSel(WTL::CListViewCtrl& tCtrl, INT iSel) { if(iSel < 0) tCtrl.SelectItem(-1); else tCtrl.SelectItem(iSel); } #endif // __ATLCTRLS_H__ template void DDX_Index(UINT nID, INT& nVal, BOOL bSave) { T* pT = static_cast(this); TCtrl ctrl(pT->GetDlgItem(nID)); if(bSave) nVal = _getSel(ctrl); else _setSel(ctrl, nVal); } #endif // (_MSC_VER >= 1300) // Overrideables void OnDataExchangeError(UINT nCtrlID, BOOL /*bSave*/) { // Override to display an error message ::MessageBeep((UINT)-1); T* pT = static_cast(this); ::SetFocus(pT->GetDlgItem(nCtrlID)); } void OnDataValidateError(UINT nCtrlID, BOOL /*bSave*/, _XData& /*data*/) { // Override to display an error message ::MessageBeep((UINT)-1); T* pT = static_cast(this); ::SetFocus(pT->GetDlgItem(nCtrlID)); } }; }; // namespace WTL #endif // __ATLDDX_H__ ================================================ FILE: src/Setup/wtl90/atldlgs.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLDLGS_H__ #define __ATLDLGS_H__ #pragma once #ifndef __ATLAPP_H__ #error atldlgs.h requires atlapp.h to be included first #endif #ifndef __ATLWIN_H__ #error atldlgs.h requires atlwin.h to be included first #endif #include #include #if (_WIN32_WINNT >= 0x0600) && !defined(_WIN32_WCE) #include #endif // (_WIN32_WINNT >= 0x0600) && !defined(_WIN32_WCE) /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CFileDialogImpl // CFileDialog // CFileDialogEx // CMultiFileDialogImpl // CMultiFileDialog // CShellFileDialogImpl // CShellFileOpenDialogImpl // CShellFileOpenDialog // CShellFileSaveDialogImpl // CShellFileSaveDialog // CFolderDialogImpl // CFolderDialog // CFontDialogImpl // CFontDialog // CRichEditFontDialogImpl // CRichEditFontDialog // CColorDialogImpl // CColorDialog // CPrintDialogImpl // CPrintDialog // CPrintDialogExImpl // CPrintDialogEx // CPageSetupDialogImpl // CPageSetupDialog // CFindReplaceDialogImpl // CFindReplaceDialog // // CDialogBaseUnits // CMemDlgTemplate // CIndirectDialogImpl // // CPropertySheetWindow // CPropertySheetImpl // CPropertySheet // CPropertyPageWindow // CPropertyPageImpl // CPropertyPage // CAxPropertyPageImpl // CAxPropertyPage // // CWizard97SheetWindow // CWizard97SheetImpl // CWizard97Sheet // CWizard97PageWindow // CWizard97PageImpl // CWizard97ExteriorPageImpl // CWizard97InteriorPageImpl // // CAeroWizardFrameWindow // CAeroWizardFrameImpl // CAeroWizardFrame // CAeroWizardPageWindow // CAeroWizardPageImpl // CAeroWizardPage // CAeroWizardAxPageImpl // CAeroWizardAxPage // // CTaskDialogConfig // CTaskDialogImpl // CTaskDialog // // Global functions: // AtlTaskDialog() namespace WTL { /////////////////////////////////////////////////////////////////////////////// // CFileDialogImpl - used for File Open or File Save As // compatibility with the old (vc6.0) headers #if (_WIN32_WINNT >= 0x0500) && !defined(OPENFILENAME_SIZE_VERSION_400) #ifndef CDSIZEOF_STRUCT #define CDSIZEOF_STRUCT(structname, member) (((int)((LPBYTE)(&((structname*)0)->member) - ((LPBYTE)((structname*)0)))) + sizeof(((structname*)0)->member)) #endif #define OPENFILENAME_SIZE_VERSION_400A CDSIZEOF_STRUCT(OPENFILENAMEA,lpTemplateName) #define OPENFILENAME_SIZE_VERSION_400W CDSIZEOF_STRUCT(OPENFILENAMEW,lpTemplateName) #ifdef UNICODE #define OPENFILENAME_SIZE_VERSION_400 OPENFILENAME_SIZE_VERSION_400W #else #define OPENFILENAME_SIZE_VERSION_400 OPENFILENAME_SIZE_VERSION_400A #endif // !UNICODE #endif // (_WIN32_WINNT >= 0x0500) && !defined(OPENFILENAME_SIZE_VERSION_400) #if !defined(_WIN32_WCE) && !defined(CDN_INCLUDEITEM) #define CDN_INCLUDEITEM (CDN_FIRST - 0x0007) #endif template class ATL_NO_VTABLE CFileDialogImpl : public ATL::CDialogImplBase { public: #if defined(__AYGSHELL_H__) && (_WIN32_WCE >= 0x0501) OPENFILENAMEEX m_ofn; #else OPENFILENAME m_ofn; #endif BOOL m_bOpenFileDialog; // TRUE for file open, FALSE for file save TCHAR m_szFileTitle[_MAX_FNAME]; // contains file title after return TCHAR m_szFileName[_MAX_PATH]; // contains full path name after return CFileDialogImpl(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, HWND hWndParent = NULL) { memset(&m_ofn, 0, sizeof(m_ofn)); // initialize structure to 0/NULL m_szFileName[0] = _T('\0'); m_szFileTitle[0] = _T('\0'); m_bOpenFileDialog = bOpenFileDialog; #if defined(__AYGSHELL_H__) && (_WIN32_WCE >= 0x0501) m_ofn.lStructSize = bOpenFileDialog ? sizeof(m_ofn) : sizeof(OPENFILENAME); #else m_ofn.lStructSize = sizeof(m_ofn); #endif #if (_WIN32_WINNT >= 0x0500) // adjust struct size if running on older version of Windows if(AtlIsOldWindows()) { ATLASSERT(sizeof(m_ofn) > OPENFILENAME_SIZE_VERSION_400); // must be m_ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; } #endif // (_WIN32_WINNT >= 0x0500) m_ofn.lpstrFile = m_szFileName; m_ofn.nMaxFile = _MAX_PATH; m_ofn.lpstrDefExt = lpszDefExt; m_ofn.lpstrFileTitle = (LPTSTR)m_szFileTitle; m_ofn.nMaxFileTitle = _MAX_FNAME; #ifndef _WIN32_WCE m_ofn.Flags = dwFlags | OFN_EXPLORER | OFN_ENABLEHOOK | OFN_ENABLESIZING; #else // CE specific m_ofn.Flags = dwFlags | OFN_EXPLORER | OFN_ENABLEHOOK; #endif // !_WIN32_WCE m_ofn.lpstrFilter = lpszFilter; m_ofn.hInstance = ModuleHelper::GetResourceInstance(); m_ofn.lpfnHook = (LPOFNHOOKPROC)T::StartDialogProc; m_ofn.hwndOwner = hWndParent; // setup initial file name if(lpszFileName != NULL) SecureHelper::strncpy_x(m_szFileName, _countof(m_szFileName), lpszFileName, _TRUNCATE); } INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow()) { ATLASSERT((m_ofn.Flags & OFN_ENABLEHOOK) != 0); ATLASSERT(m_ofn.lpfnHook != NULL); // can still be a user hook ATLASSERT((m_ofn.Flags & OFN_EXPLORER) != 0); if(m_ofn.hwndOwner == NULL) // set only if not specified before m_ofn.hwndOwner = hWndParent; ATLASSERT(m_hWnd == NULL); #if (_ATL_VER >= 0x0800) // Allocate the thunk structure here, where we can fail gracefully. BOOL bRetTh = m_thunk.Init(NULL, NULL); if(bRetTh == FALSE) { ::SetLastError(ERROR_OUTOFMEMORY); return -1; } #endif // (_ATL_VER >= 0x0800) ModuleHelper::AddCreateWndData(&m_thunk.cd, (ATL::CDialogImplBase*)this); BOOL bRet; if(m_bOpenFileDialog) #if defined(__AYGSHELL_H__) && (_WIN32_WCE >= 0x0501) bRet = ::GetOpenFileNameEx(&m_ofn); else bRet = ::GetSaveFileName((LPOPENFILENAME)&m_ofn); #else bRet = ::GetOpenFileName(&m_ofn); else bRet = ::GetSaveFileName(&m_ofn); #endif m_hWnd = NULL; return bRet ? IDOK : IDCANCEL; } // Attributes ATL::CWindow GetFileDialogWindow() const { ATLASSERT(::IsWindow(m_hWnd)); return ATL::CWindow(GetParent()); } int GetFilePath(LPTSTR lpstrFilePath, int nLength) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((m_ofn.Flags & OFN_EXPLORER) != 0); return (int)GetFileDialogWindow().SendMessage(CDM_GETFILEPATH, nLength, (LPARAM)lpstrFilePath); } int GetFolderIDList(LPVOID lpBuff, int nLength) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((m_ofn.Flags & OFN_EXPLORER) != 0); return (int)GetFileDialogWindow().SendMessage(CDM_GETFOLDERIDLIST, nLength, (LPARAM)lpBuff); } int GetFolderPath(LPTSTR lpstrFolderPath, int nLength) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((m_ofn.Flags & OFN_EXPLORER) != 0); return (int)GetFileDialogWindow().SendMessage(CDM_GETFOLDERPATH, nLength, (LPARAM)lpstrFolderPath); } int GetSpec(LPTSTR lpstrSpec, int nLength) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((m_ofn.Flags & OFN_EXPLORER) != 0); return (int)GetFileDialogWindow().SendMessage(CDM_GETSPEC, nLength, (LPARAM)lpstrSpec); } void SetControlText(int nCtrlID, LPCTSTR lpstrText) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((m_ofn.Flags & OFN_EXPLORER) != 0); GetFileDialogWindow().SendMessage(CDM_SETCONTROLTEXT, nCtrlID, (LPARAM)lpstrText); } void SetDefExt(LPCTSTR lpstrExt) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((m_ofn.Flags & OFN_EXPLORER) != 0); GetFileDialogWindow().SendMessage(CDM_SETDEFEXT, 0, (LPARAM)lpstrExt); } BOOL GetReadOnlyPref() const // return TRUE if readonly checked { return ((m_ofn.Flags & OFN_READONLY) != 0) ? TRUE : FALSE; } // Operations void HideControl(int nCtrlID) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((m_ofn.Flags & OFN_EXPLORER) != 0); GetFileDialogWindow().SendMessage(CDM_HIDECONTROL, nCtrlID); } // Special override for common dialogs BOOL EndDialog(INT_PTR /*nRetCode*/ = 0) { ATLASSERT(::IsWindow(m_hWnd)); GetFileDialogWindow().SendMessage(WM_COMMAND, MAKEWPARAM(IDCANCEL, 0)); return TRUE; } // Message map and handlers BEGIN_MSG_MAP(CFileDialogImpl) NOTIFY_CODE_HANDLER(CDN_FILEOK, _OnFileOK) NOTIFY_CODE_HANDLER(CDN_FOLDERCHANGE, _OnFolderChange) NOTIFY_CODE_HANDLER(CDN_HELP, _OnHelp) NOTIFY_CODE_HANDLER(CDN_INITDONE, _OnInitDone) NOTIFY_CODE_HANDLER(CDN_SELCHANGE, _OnSelChange) NOTIFY_CODE_HANDLER(CDN_SHAREVIOLATION, _OnShareViolation) NOTIFY_CODE_HANDLER(CDN_TYPECHANGE, _OnTypeChange) #ifndef _WIN32_WCE NOTIFY_CODE_HANDLER(CDN_INCLUDEITEM, _OnIncludeItem) #endif // !_WIN32_WCE END_MSG_MAP() LRESULT _OnFileOK(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) { ATLASSERT(::IsWindow(m_hWnd)); T* pT = static_cast(this); return !pT->OnFileOK((LPOFNOTIFY)pnmh); } LRESULT _OnFolderChange(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) { ATLASSERT(::IsWindow(m_hWnd)); T* pT = static_cast(this); pT->OnFolderChange((LPOFNOTIFY)pnmh); return 0; } LRESULT _OnHelp(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) { ATLASSERT(::IsWindow(m_hWnd)); T* pT = static_cast(this); pT->OnHelp((LPOFNOTIFY)pnmh); return 0; } LRESULT _OnInitDone(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) { ATLASSERT(::IsWindow(m_hWnd)); T* pT = static_cast(this); pT->OnInitDone((LPOFNOTIFY)pnmh); return 0; } LRESULT _OnSelChange(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) { ATLASSERT(::IsWindow(m_hWnd)); T* pT = static_cast(this); pT->OnSelChange((LPOFNOTIFY)pnmh); return 0; } LRESULT _OnShareViolation(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) { ATLASSERT(::IsWindow(m_hWnd)); T* pT = static_cast(this); return pT->OnShareViolation((LPOFNOTIFY)pnmh); } LRESULT _OnTypeChange(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) { ATLASSERT(::IsWindow(m_hWnd)); T* pT = static_cast(this); pT->OnTypeChange((LPOFNOTIFY)pnmh); return 0; } #ifndef _WIN32_WCE LRESULT _OnIncludeItem(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) { ATLASSERT(::IsWindow(m_hWnd)); T* pT = static_cast(this); return pT->OnIncludeItem((LPOFNOTIFYEX)pnmh); } #endif // !_WIN32_WCE // Overrideables BOOL OnFileOK(LPOFNOTIFY /*lpon*/) { return TRUE; } void OnFolderChange(LPOFNOTIFY /*lpon*/) { } void OnHelp(LPOFNOTIFY /*lpon*/) { } void OnInitDone(LPOFNOTIFY /*lpon*/) { } void OnSelChange(LPOFNOTIFY /*lpon*/) { } int OnShareViolation(LPOFNOTIFY /*lpon*/) { return 0; } void OnTypeChange(LPOFNOTIFY /*lpon*/) { } #ifndef _WIN32_WCE BOOL OnIncludeItem(LPOFNOTIFYEX /*lponex*/) { return TRUE; // include item } #endif // !_WIN32_WCE }; class CFileDialog : public CFileDialogImpl { public: CFileDialog(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, HWND hWndParent = NULL) : CFileDialogImpl(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, hWndParent) { } // override base class map and references to handlers DECLARE_EMPTY_MSG_MAP() }; #if defined(__AYGSHELL_H__) && (_WIN32_WCE >= 0x0501) class CFileDialogEx : public CFileDialogImpl { public: CFileDialogEx( // Supports only FileOpen LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, OFN_EXFLAG ExFlags = OFN_EXFLAG_THUMBNAILVIEW, OFN_SORTORDER dwSortOrder = OFN_SORTORDER_AUTO, LPCTSTR lpszFilter = NULL, HWND hWndParent = NULL) : CFileDialogImpl(TRUE, lpszDefExt, lpszFileName, dwFlags, lpszFilter, hWndParent) { m_ofn.ExFlags = ExFlags; m_ofn.dwSortOrder = dwSortOrder; } // override base class map and references to handlers DECLARE_EMPTY_MSG_MAP() }; #endif // defined(__AYGSHELL_H__) && (_WIN32_WCE >= 0x0501) /////////////////////////////////////////////////////////////////////////////// // Multi File Dialog - Multi-select File Open dialog #ifndef _WIN32_WCE // The class dynamically resizes the buffer as the file selection changes // (as described in Knowledge Base article 131462). It also expands selected // shortcut files to take into account the full path of the target file. // Note that this doesn't work on Win9x for the old style dialogs, as well as // on NT for non-Unicode builds. #ifndef _WTL_FIXED_OFN_BUFFER_LENGTH #define _WTL_FIXED_OFN_BUFFER_LENGTH 0x10000 #endif template class ATL_NO_VTABLE CMultiFileDialogImpl : public CFileDialogImpl< T > { public: mutable LPCTSTR m_pNextFile; #ifndef _UNICODE bool m_bIsNT; #endif CMultiFileDialogImpl( LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY, LPCTSTR lpszFilter = NULL, HWND hWndParent = NULL) : CFileDialogImpl(TRUE, lpszDefExt, lpszFileName, dwFlags, lpszFilter, hWndParent), m_pNextFile(NULL) { m_ofn.Flags |= OFN_ALLOWMULTISELECT; // Force multiple selection mode #ifndef _UNICODE #ifdef _versionhelpers_H_INCLUDED_ OSVERSIONINFOEX ovi = { sizeof(OSVERSIONINFOEX) }; ovi.dwPlatformId = VER_PLATFORM_WIN32_NT; DWORDLONG const dwlConditionMask = ::VerSetConditionMask(0, VER_PLATFORMID, VER_EQUAL); m_bIsNT = (::VerifyVersionInfo(&ovi, VER_PLATFORMID, dwlConditionMask) != FALSE); #else // !_versionhelpers_H_INCLUDED_ OSVERSIONINFO ovi = { sizeof(OSVERSIONINFO) }; ::GetVersionEx(&ovi); m_bIsNT = (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT); #endif // _versionhelpers_H_INCLUDED_ if (m_bIsNT) { // On NT platforms, GetOpenFileNameA thunks to GetOpenFileNameW and there // is absolutely nothing we can do except to start off with a large buffer. ATLVERIFY(ResizeFilenameBuffer(_WTL_FIXED_OFN_BUFFER_LENGTH)); } #endif } ~CMultiFileDialogImpl() { if (m_ofn.lpstrFile != m_szFileName) // Free the buffer if we allocated it delete[] m_ofn.lpstrFile; } // Operations // Get the directory that the files were chosen from. // The function returns the number of characters copied, not including the terminating zero. // If the buffer is NULL, the function returns the required size, in characters, including the terminating zero. // If the function fails, the return value is zero. int GetDirectory(LPTSTR pBuffer, int nBufLen) const { if (m_ofn.lpstrFile == NULL) return 0; LPCTSTR pStr = m_ofn.lpstrFile; int nLength = lstrlen(pStr); if (pStr[nLength + 1] == 0) { // The OFN buffer contains a single item so extract its path. LPCTSTR pSep = MinCrtHelper::_strrchr(pStr, _T('\\')); if (pSep != NULL) nLength = (int)(DWORD_PTR)(pSep - pStr); } int nRet = 0; if (pBuffer == NULL) // If the buffer is NULL, return the required length { nRet = nLength + 1; } else if (nBufLen > nLength) { SecureHelper::strncpy_x(pBuffer, nBufLen, pStr, nLength); nRet = nLength; } return nRet; } #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) bool GetDirectory(_CSTRING_NS::CString& strDir) const { bool bRet = false; int nLength = GetDirectory(NULL, 0); if (nLength > 0) { bRet = (GetDirectory(strDir.GetBuffer(nLength), nLength) > 0); strDir.ReleaseBuffer(nLength - 1); } return bRet; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) // Get the first filename as a pointer into the buffer. LPCTSTR GetFirstFileName() const { if (m_ofn.lpstrFile == NULL) return NULL; m_pNextFile = NULL; // Reset internal buffer pointer LPCTSTR pStr = m_ofn.lpstrFile; int nLength = lstrlen(pStr); if (pStr[nLength + 1] != 0) { // Multiple items were selected. The first string is the directory, // so skip forwards to the second string. pStr += nLength + 1; // Set up m_pNext so it points to the second item (or null). m_pNextFile = pStr; GetNextFileName(); } else { // A single item was selected. Skip forward past the path. LPCTSTR pSep = MinCrtHelper::_strrchr(pStr, _T('\\')); if (pSep != NULL) pStr = pSep + 1; } return pStr; } // Get the next filename as a pointer into the buffer. LPCTSTR GetNextFileName() const { if (m_pNextFile == NULL) return NULL; LPCTSTR pStr = m_pNextFile; // Set "m_pNextFile" to point to the next file name, or null if we // have reached the last file in the list. int nLength = lstrlen(pStr); m_pNextFile = (pStr[nLength + 1] != 0) ? &pStr[nLength + 1] : NULL; return pStr; } // Get the first filename as a full path. // The function returns the number of characters copied, not including the terminating zero. // If the buffer is NULL, the function returns the required size, in characters, including the terminating zero. // If the function fails, the return value is zero. int GetFirstPathName(LPTSTR pBuffer, int nBufLen) const { LPCTSTR pStr = GetFirstFileName(); int nLengthDir = GetDirectory(NULL, 0); if((pStr == NULL) || (nLengthDir == 0)) return 0; // Figure out the required length. int nLengthTotal = nLengthDir + lstrlen(pStr); int nRet = 0; if(pBuffer == NULL) // If the buffer is NULL, return the required length { nRet = nLengthTotal + 1; } else if (nBufLen > nLengthTotal) // If the buffer is big enough, go ahead and construct the path { GetDirectory(pBuffer, nBufLen); SecureHelper::strcat_x(pBuffer, nBufLen, _T("\\")); SecureHelper::strcat_x(pBuffer, nBufLen, pStr); nRet = nLengthTotal; } return nRet; } #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) bool GetFirstPathName(_CSTRING_NS::CString& strPath) const { bool bRet = false; int nLength = GetFirstPathName(NULL, 0); if (nLength > 0) { bRet = (GetFirstPathName(strPath.GetBuffer(nLength), nLength) > 0); strPath.ReleaseBuffer(nLength - 1); } return bRet; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) // Get the next filename as a full path. // The function returns the number of characters copied, not including the terminating zero. // If the buffer is NULL, the function returns the required size, in characters, including the terminating zero. // If the function fails, the return value is zero. // The internal position marker is moved forward only if the function succeeds and the buffer was large enough. int GetNextPathName(LPTSTR pBuffer, int nBufLen) const { if (m_pNextFile == NULL) return 0; int nRet = 0; LPCTSTR pStr = m_pNextFile; // Does the filename contain a backslash? if (MinCrtHelper::_strrchr(pStr, _T('\\')) != NULL) { // Yes, so we'll assume it's a full path. int nLength = lstrlen(pStr); if (pBuffer == NULL) // If the buffer is NULL, return the required length { nRet = nLength + 1; } else if (nBufLen > nLength) // The buffer is big enough, so go ahead and copy the filename { SecureHelper::strcpy_x(pBuffer, nBufLen, GetNextFileName()); nRet = nBufLen; } } else { // The filename is relative, so construct the full path. int nLengthDir = GetDirectory(NULL, 0); if (nLengthDir > 0) { // Calculate the required space. int nLengthTotal = nLengthDir + lstrlen(pStr); if(pBuffer == NULL) // If the buffer is NULL, return the required length { nRet = nLengthTotal + 1; } else if (nBufLen > nLengthTotal) // If the buffer is big enough, go ahead and construct the path { GetDirectory(pBuffer, nBufLen); SecureHelper::strcat_x(pBuffer, nBufLen, _T("\\")); SecureHelper::strcat_x(pBuffer, nBufLen, GetNextFileName()); nRet = nLengthTotal; } } } return nRet; } #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) bool GetNextPathName(_CSTRING_NS::CString& strPath) const { bool bRet = false; int nLength = GetNextPathName(NULL, 0); if (nLength > 0) { bRet = (GetNextPathName(strPath.GetBuffer(nLength), nLength) > 0); strPath.ReleaseBuffer(nLength - 1); } return bRet; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) // Implementation bool ResizeFilenameBuffer(DWORD dwLength) { if (dwLength > m_ofn.nMaxFile) { // Free the old buffer. if (m_ofn.lpstrFile != m_szFileName) { delete[] m_ofn.lpstrFile; m_ofn.lpstrFile = NULL; m_ofn.nMaxFile = 0; } // Allocate the new buffer. LPTSTR lpstrBuff = NULL; ATLTRY(lpstrBuff = new TCHAR[dwLength]); if (lpstrBuff != NULL) { m_ofn.lpstrFile = lpstrBuff; m_ofn.lpstrFile[0] = 0; m_ofn.nMaxFile = dwLength; } } return (m_ofn.lpstrFile != NULL); } void OnSelChange(LPOFNOTIFY /*lpon*/) { #ifndef _UNICODE // There is no point resizing the buffer in ANSI builds running on NT. if (m_bIsNT) return; #endif // Get the buffer length required to hold the spec. int nLength = GetSpec(NULL, 0); if (nLength <= 1) return; // no files are selected, presumably // Add room for the directory, and an extra terminating zero. nLength += GetFolderPath(NULL, 0) + 1; if (!ResizeFilenameBuffer(nLength)) { ATLASSERT(FALSE); return; } // If we are not following links then our work is done. if ((m_ofn.Flags & OFN_NODEREFERENCELINKS) != 0) return; // Get the file spec, which is the text in the edit control. if (GetSpec(m_ofn.lpstrFile, m_ofn.nMaxFile) <= 0) return; // Get the ID-list of the current folder. int nBytes = GetFolderIDList(NULL, 0); #ifdef STRICT_TYPED_ITEMIDS CTempBuffer idlist; #else CTempBuffer idlist; #endif idlist.AllocateBytes(nBytes); if ((nBytes <= 0) || (GetFolderIDList(idlist, nBytes) <= 0)) return; // First bind to the desktop folder, then to the current folder. ATL::CComPtr pDesktop, pFolder; if (FAILED(::SHGetDesktopFolder(&pDesktop))) return; if (FAILED(pDesktop->BindToObject(idlist, NULL, IID_IShellFolder, (void**)&pFolder))) return; // Work through the file spec, looking for quoted filenames. If we find a shortcut file, then // we need to add enough extra buffer space to hold its target path. DWORD nExtraChars = 0; bool bInsideQuotes = false; LPCTSTR pAnchor = m_ofn.lpstrFile; LPCTSTR pChar = m_ofn.lpstrFile; for ( ; *pChar; ++pChar) { // Look for quotation marks. if (*pChar == _T('\"')) { // We are either entering or leaving a passage of quoted text. bInsideQuotes = !bInsideQuotes; // Is it an opening or closing quote? if (bInsideQuotes) { // We found an opening quote, so set "pAnchor" to the following character. pAnchor = pChar + 1; } else // closing quote { // Each quoted entity should be shorter than MAX_PATH. if (pChar - pAnchor >= MAX_PATH) return; // Get the ID-list and attributes of the file. USES_CONVERSION; int nFileNameLength = (int)(DWORD_PTR)(pChar - pAnchor); TCHAR szFileName[MAX_PATH] = { 0 }; SecureHelper::strncpy_x(szFileName, MAX_PATH, pAnchor, nFileNameLength); #ifdef STRICT_TYPED_ITEMIDS PIDLIST_RELATIVE pidl = NULL; #else LPITEMIDLIST pidl = NULL; #endif DWORD dwAttrib = SFGAO_LINK; if (SUCCEEDED(pFolder->ParseDisplayName(NULL, NULL, T2W(szFileName), NULL, &pidl, &dwAttrib))) { // Is it a shortcut file? if (dwAttrib & SFGAO_LINK) { // Bind to its IShellLink interface. ATL::CComPtr pLink; if (SUCCEEDED(pFolder->BindToObject(pidl, NULL, IID_IShellLink, (void**)&pLink))) { // Get the shortcut's target path. TCHAR szPath[MAX_PATH] = { 0 }; if (SUCCEEDED(pLink->GetPath(szPath, MAX_PATH, NULL, 0))) { // If the target path is longer than the shortcut name, then add on the number // of extra characters that are required. int nNewLength = lstrlen(szPath); if (nNewLength > nFileNameLength) nExtraChars += nNewLength - nFileNameLength; } } } // Free the ID-list returned by ParseDisplayName. ::CoTaskMemFree(pidl); } } } } // If we need more space for shortcut targets, then reallocate. if (nExtraChars > 0) ATLVERIFY(ResizeFilenameBuffer(m_ofn.nMaxFile + nExtraChars)); } }; class CMultiFileDialog : public CMultiFileDialogImpl { public: CMultiFileDialog( LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY, LPCTSTR lpszFilter = NULL, HWND hWndParent = NULL) : CMultiFileDialogImpl(lpszDefExt, lpszFileName, dwFlags, lpszFilter, hWndParent) { } BEGIN_MSG_MAP(CMultiFileDialog) CHAIN_MSG_MAP(CMultiFileDialogImpl) END_MSG_MAP() }; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // Shell File Dialog - new Shell File Open and Save dialogs in Vista // Note: Use GetPtr() to access dialog interface methods. // Example: // CShellFileOpenDialog dlg; // dlg.GetPtr()->SetTitle(L"MyFileOpenDialog"); #if (_WIN32_WINNT >= 0x0600) && !defined(_WIN32_WCE) /////////////////////////////////////////////////////////////////////////////// // CShellFileDialogImpl - base class for CShellFileOpenDialogImpl and CShellFileSaveDialogImpl template class ATL_NO_VTABLE CShellFileDialogImpl : public IFileDialogEvents { public: // Operations INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow()) { INT_PTR nRet = -1; T* pT = static_cast(this); if(pT->m_spFileDlg == NULL) { ATLASSERT(FALSE); return nRet; } DWORD dwCookie = 0; pT->_Advise(dwCookie); HRESULT hRet = pT->m_spFileDlg->Show(hWndParent); if(SUCCEEDED(hRet)) nRet = IDOK; else if(hRet == HRESULT_FROM_WIN32(ERROR_CANCELLED)) nRet = IDCANCEL; else ATLASSERT(FALSE); // error pT->_Unadvise(dwCookie); return nRet; } bool IsNull() const { const T* pT = static_cast(this); return (pT->m_spFileDlg == NULL); } // Operations - get file path after dialog returns HRESULT GetFilePath(LPWSTR lpstrFilePath, int cchLength) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg != NULL); ATL::CComPtr spItem; HRESULT hRet = pT->m_spFileDlg->GetResult(&spItem); if(SUCCEEDED(hRet)) hRet = GetFileNameFromShellItem(spItem, SIGDN_FILESYSPATH, lpstrFilePath, cchLength); return hRet; } HRESULT GetFileTitle(LPWSTR lpstrFileTitle, int cchLength) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg != NULL); ATL::CComPtr spItem; HRESULT hRet = pT->m_spFileDlg->GetResult(&spItem); if(SUCCEEDED(hRet)) hRet = GetFileNameFromShellItem(spItem, SIGDN_NORMALDISPLAY, lpstrFileTitle, cchLength); return hRet; } #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) HRESULT GetFilePath(_CSTRING_NS::CString& strFilePath) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg != NULL); ATL::CComPtr spItem; HRESULT hRet = pT->m_spFileDlg->GetResult(&spItem); if(SUCCEEDED(hRet)) hRet = GetFileNameFromShellItem(spItem, SIGDN_FILESYSPATH, strFilePath); return hRet; } HRESULT GetFileTitle(_CSTRING_NS::CString& strFileTitle) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg != NULL); ATL::CComPtr spItem; HRESULT hRet = pT->m_spFileDlg->GetResult(&spItem); if(SUCCEEDED(hRet)) hRet = GetFileNameFromShellItem(spItem, SIGDN_NORMALDISPLAY, strFileTitle); return hRet; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) // Helpers for IShellItem static HRESULT GetFileNameFromShellItem(IShellItem* pShellItem, SIGDN type, LPWSTR lpstr, int cchLength) { ATLASSERT(pShellItem != NULL); LPWSTR lpstrName = NULL; HRESULT hRet = pShellItem->GetDisplayName(type, &lpstrName); if(SUCCEEDED(hRet)) { if(lstrlenW(lpstrName) < cchLength) { SecureHelper::strcpyW_x(lpstr, cchLength, lpstrName); } else { ATLASSERT(FALSE); hRet = DISP_E_BUFFERTOOSMALL; } ::CoTaskMemFree(lpstrName); } return hRet; } #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) static HRESULT GetFileNameFromShellItem(IShellItem* pShellItem, SIGDN type, _CSTRING_NS::CString& str) { ATLASSERT(pShellItem != NULL); LPWSTR lpstrName = NULL; HRESULT hRet = pShellItem->GetDisplayName(type, &lpstrName); if(SUCCEEDED(hRet)) { str = lpstrName; ::CoTaskMemFree(lpstrName); } return hRet; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) // Implementation void _Advise(DWORD& dwCookie) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg != NULL); HRESULT hRet = pT->m_spFileDlg->Advise((IFileDialogEvents*)this, &dwCookie); ATLVERIFY(SUCCEEDED(hRet)); } void _Unadvise(DWORD dwCookie) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg != NULL); HRESULT hRet = pT->m_spFileDlg->Unadvise(dwCookie); ATLVERIFY(SUCCEEDED(hRet)); } void _Init(LPCWSTR lpszFileName, DWORD dwOptions, LPCWSTR lpszDefExt, const COMDLG_FILTERSPEC* arrFilterSpec, UINT uFilterSpecCount) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg != NULL); HRESULT hRet = E_FAIL; if(lpszFileName != NULL) { hRet = pT->m_spFileDlg->SetFileName(lpszFileName); ATLASSERT(SUCCEEDED(hRet)); } hRet = pT->m_spFileDlg->SetOptions(dwOptions); ATLASSERT(SUCCEEDED(hRet)); if(lpszDefExt != NULL) { hRet = pT->m_spFileDlg->SetDefaultExtension(lpszDefExt); ATLASSERT(SUCCEEDED(hRet)); } if(arrFilterSpec != NULL && uFilterSpecCount != 0U) { hRet = pT->m_spFileDlg->SetFileTypes(uFilterSpecCount, arrFilterSpec); ATLASSERT(SUCCEEDED(hRet)); } } // Implementation - IUnknown interface STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) { if(ppvObject == NULL) return E_POINTER; T* pT = static_cast(this); if(IsEqualGUID(riid, IID_IUnknown) || IsEqualGUID(riid, IID_IFileDialogEvents)) { *ppvObject = (IFileDialogEvents*)pT; // AddRef() not needed return S_OK; } return E_NOINTERFACE; } virtual ULONG STDMETHODCALLTYPE AddRef() { return 1; } virtual ULONG STDMETHODCALLTYPE Release() { return 1; } // Implementation - IFileDialogEvents interface virtual HRESULT STDMETHODCALLTYPE IFileDialogEvents::OnFileOk(IFileDialog* pfd) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg.IsEqualObject(pfd)); pfd; // avoid level 4 warning return pT->OnFileOk(); } virtual HRESULT STDMETHODCALLTYPE IFileDialogEvents::OnFolderChanging(IFileDialog* pfd, IShellItem* psiFolder) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg.IsEqualObject(pfd)); pfd; // avoid level 4 warning return pT->OnFolderChanging(psiFolder); } virtual HRESULT STDMETHODCALLTYPE IFileDialogEvents::OnFolderChange(IFileDialog* pfd) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg.IsEqualObject(pfd)); pfd; // avoid level 4 warning return pT->OnFolderChange(); } virtual HRESULT STDMETHODCALLTYPE IFileDialogEvents::OnSelectionChange(IFileDialog* pfd) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg.IsEqualObject(pfd)); pfd; // avoid level 4 warning return pT->OnSelectionChange(); } virtual HRESULT STDMETHODCALLTYPE IFileDialogEvents::OnShareViolation(IFileDialog* pfd, IShellItem* psi, FDE_SHAREVIOLATION_RESPONSE* pResponse) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg.IsEqualObject(pfd)); pfd; // avoid level 4 warning return pT->OnShareViolation(psi, pResponse); } virtual HRESULT STDMETHODCALLTYPE IFileDialogEvents::OnTypeChange(IFileDialog* pfd) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg.IsEqualObject(pfd)); pfd; // avoid level 4 warning return pT->OnTypeChange(); } virtual HRESULT STDMETHODCALLTYPE IFileDialogEvents::OnOverwrite(IFileDialog* pfd, IShellItem* psi, FDE_OVERWRITE_RESPONSE* pResponse) { T* pT = static_cast(this); ATLASSERT(pT->m_spFileDlg.IsEqualObject(pfd)); pfd; // avoid level 4 warning return pT->OnOverwrite(psi, pResponse); } // Overrideables - Event handlers HRESULT OnFileOk() { return E_NOTIMPL; } HRESULT OnFolderChanging(IShellItem* /*psiFolder*/) { return E_NOTIMPL; } HRESULT OnFolderChange() { return E_NOTIMPL; } HRESULT OnSelectionChange() { return E_NOTIMPL; } HRESULT OnShareViolation(IShellItem* /*psi*/, FDE_SHAREVIOLATION_RESPONSE* /*pResponse*/) { return E_NOTIMPL; } HRESULT OnTypeChange() { return E_NOTIMPL; } HRESULT OnOverwrite(IShellItem* /*psi*/, FDE_OVERWRITE_RESPONSE* /*pResponse*/) { return E_NOTIMPL; } }; /////////////////////////////////////////////////////////////////////////////// // CShellFileOpenDialogImpl - implements new Shell File Open dialog template class ATL_NO_VTABLE CShellFileOpenDialogImpl : public CShellFileDialogImpl< T > { public: ATL::CComPtr m_spFileDlg; CShellFileOpenDialogImpl(LPCWSTR lpszFileName = NULL, DWORD dwOptions = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST, LPCWSTR lpszDefExt = NULL, const COMDLG_FILTERSPEC* arrFilterSpec = NULL, UINT uFilterSpecCount = 0U) { HRESULT hRet = m_spFileDlg.CoCreateInstance(CLSID_FileOpenDialog); if(SUCCEEDED(hRet)) _Init(lpszFileName, dwOptions, lpszDefExt, arrFilterSpec, uFilterSpecCount); } IFileOpenDialog* GetPtr() { return m_spFileDlg; } }; /////////////////////////////////////////////////////////////////////////////// // CShellFileOpenDialog - new Shell File Open dialog without events class CShellFileOpenDialog : public CShellFileOpenDialogImpl { public: CShellFileOpenDialog(LPCWSTR lpszFileName = NULL, DWORD dwOptions = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST, LPCWSTR lpszDefExt = NULL, const COMDLG_FILTERSPEC* arrFilterSpec = NULL, UINT uFilterSpecCount = 0U) : CShellFileOpenDialogImpl(lpszFileName, dwOptions, lpszDefExt, arrFilterSpec, uFilterSpecCount) { } // Implementation (remove _Advise/_Unadvise code using template magic) void _Advise(DWORD& /*dwCookie*/) { } void _Unadvise(DWORD /*dwCookie*/) { } }; /////////////////////////////////////////////////////////////////////////////// // CShellFileSaveDialogImpl - implements new Shell File Save dialog template class ATL_NO_VTABLE CShellFileSaveDialogImpl : public CShellFileDialogImpl< T > { public: ATL::CComPtr m_spFileDlg; CShellFileSaveDialogImpl(LPCWSTR lpszFileName = NULL, DWORD dwOptions = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT, LPCWSTR lpszDefExt = NULL, const COMDLG_FILTERSPEC* arrFilterSpec = NULL, UINT uFilterSpecCount = 0U) { HRESULT hRet = m_spFileDlg.CoCreateInstance(CLSID_FileSaveDialog); if(SUCCEEDED(hRet)) _Init(lpszFileName, dwOptions, lpszDefExt, arrFilterSpec, uFilterSpecCount); } IFileSaveDialog* GetPtr() { return m_spFileDlg; } }; /////////////////////////////////////////////////////////////////////////////// // CShellFileSaveDialog - new Shell File Save dialog without events class CShellFileSaveDialog : public CShellFileSaveDialogImpl { public: CShellFileSaveDialog(LPCWSTR lpszFileName = NULL, DWORD dwOptions = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT, LPCWSTR lpszDefExt = NULL, const COMDLG_FILTERSPEC* arrFilterSpec = NULL, UINT uFilterSpecCount = 0U) : CShellFileSaveDialogImpl(lpszFileName, dwOptions, lpszDefExt, arrFilterSpec, uFilterSpecCount) { } // Implementation (remove _Advise/_Unadvise code using template magic) void _Advise(DWORD& /*dwCookie*/) { } void _Unadvise(DWORD /*dwCookie*/) { } }; #endif // (_WIN32_WINNT >= 0x0600) && !defined(_WIN32_WCE) /////////////////////////////////////////////////////////////////////////////// // CFolderDialogImpl - used for browsing for a folder #ifndef _WIN32_WCE template class ATL_NO_VTABLE CFolderDialogImpl { public: BROWSEINFO m_bi; LPCTSTR m_lpstrInitialFolder; LPCITEMIDLIST m_pidlInitialSelection; bool m_bExpandInitialSelection; TCHAR m_szFolderDisplayName[MAX_PATH]; TCHAR m_szFolderPath[MAX_PATH]; #ifdef STRICT_TYPED_ITEMIDS PIDLIST_ABSOLUTE m_pidlSelected; #else LPITEMIDLIST m_pidlSelected; #endif HWND m_hWnd; // used only in the callback function // Constructor CFolderDialogImpl(HWND hWndParent = NULL, LPCTSTR lpstrTitle = NULL, UINT uFlags = BIF_RETURNONLYFSDIRS) : m_lpstrInitialFolder(NULL), m_pidlInitialSelection(NULL), m_bExpandInitialSelection(false), m_pidlSelected(NULL), m_hWnd(NULL) { memset(&m_bi, 0, sizeof(m_bi)); // initialize structure to 0/NULL m_bi.hwndOwner = hWndParent; m_bi.pidlRoot = NULL; m_bi.pszDisplayName = m_szFolderDisplayName; m_bi.lpszTitle = lpstrTitle; m_bi.ulFlags = uFlags; m_bi.lpfn = BrowseCallbackProc; m_bi.lParam = (LPARAM)static_cast(this); m_szFolderPath[0] = 0; m_szFolderDisplayName[0] = 0; } ~CFolderDialogImpl() { ::CoTaskMemFree(m_pidlSelected); } // Operations INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow()) { if(m_bi.hwndOwner == NULL) // set only if not specified before m_bi.hwndOwner = hWndParent; // Clear out any previous results m_szFolderPath[0] = 0; m_szFolderDisplayName[0] = 0; ::CoTaskMemFree(m_pidlSelected); INT_PTR nRet = IDCANCEL; m_pidlSelected = ::SHBrowseForFolder(&m_bi); if(m_pidlSelected != NULL) { nRet = IDOK; // If BIF_RETURNONLYFSDIRS is set, we try to get the filesystem path. // Otherwise, the caller must handle the ID-list directly. if((m_bi.ulFlags & BIF_RETURNONLYFSDIRS) != 0) { if(::SHGetPathFromIDList(m_pidlSelected, m_szFolderPath) == FALSE) nRet = IDCANCEL; } } return nRet; } // Methods to call before DoModal void SetInitialFolder(LPCTSTR lpstrInitialFolder, bool bExpand = true) { // lpstrInitialFolder may be a file if BIF_BROWSEINCLUDEFILES is specified m_lpstrInitialFolder = lpstrInitialFolder; m_bExpandInitialSelection = bExpand; } void SetInitialSelection(LPCITEMIDLIST pidl, bool bExpand = true) { m_pidlInitialSelection = pidl; m_bExpandInitialSelection = bExpand; } #ifdef STRICT_TYPED_ITEMIDS void SetRootFolder(PCIDLIST_ABSOLUTE pidl) #else void SetRootFolder(LPCITEMIDLIST pidl) #endif { m_bi.pidlRoot = pidl; } // Methods to call after DoModal LPITEMIDLIST GetSelectedItem(bool bDetach = false) { LPITEMIDLIST pidl = m_pidlSelected; if(bDetach) m_pidlSelected = NULL; return pidl; } LPCTSTR GetFolderPath() const { return m_szFolderPath; } LPCTSTR GetFolderDisplayName() const { return m_szFolderDisplayName; } int GetFolderImageIndex() const { return m_bi.iImage; } // Callback function and overrideables static int CALLBACK BrowseCallbackProc(HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData) { #ifndef BFFM_VALIDATEFAILED #ifdef UNICODE const int BFFM_VALIDATEFAILED = 4; #else const int BFFM_VALIDATEFAILED = 3; #endif #endif // !BFFM_VALIDATEFAILED #ifndef BFFM_IUNKNOWN const int BFFM_IUNKNOWN = 5; #endif // !BFFM_IUNKNOWN #ifndef BIF_NEWDIALOGSTYLE const UINT BIF_NEWDIALOGSTYLE = 0x0040; #endif // !BIF_NEWDIALOGSTYLE int nRet = 0; T* pT = (T*)lpData; bool bClear = false; if(pT->m_hWnd == NULL) { pT->m_hWnd = hWnd; bClear = true; } else { ATLASSERT(pT->m_hWnd == hWnd); } switch(uMsg) { case BFFM_INITIALIZED: // Set initial selection // Note that m_pidlInitialSelection, if set, takes precedence over m_lpstrInitialFolder if(pT->m_pidlInitialSelection != NULL) pT->SetSelection(pT->m_pidlInitialSelection); else if(pT->m_lpstrInitialFolder != NULL) pT->SetSelection(pT->m_lpstrInitialFolder); // Expand initial selection if appropriate if(pT->m_bExpandInitialSelection && ((pT->m_bi.ulFlags & BIF_NEWDIALOGSTYLE) != 0)) { if(pT->m_pidlInitialSelection != NULL) pT->SetExpanded(pT->m_pidlInitialSelection); else if(pT->m_lpstrInitialFolder != NULL) pT->SetExpanded(pT->m_lpstrInitialFolder); } pT->OnInitialized(); break; case BFFM_SELCHANGED: pT->OnSelChanged((LPITEMIDLIST)lParam); break; case BFFM_VALIDATEFAILED: nRet = pT->OnValidateFailed((LPCTSTR)lParam); break; case BFFM_IUNKNOWN: pT->OnIUnknown((IUnknown*)lParam); break; default: ATLTRACE2(atlTraceUI, 0, _T("Unknown message received in CFolderDialogImpl::BrowseCallbackProc\n")); break; } if(bClear) pT->m_hWnd = NULL; return nRet; } void OnInitialized() { } void OnSelChanged(LPITEMIDLIST /*pItemIDList*/) { } int OnValidateFailed(LPCTSTR /*lpstrFolderPath*/) { return 1; // 1=continue, 0=EndDialog } void OnIUnknown(IUnknown* /*pUnknown*/) { } // Commands - valid to call only from handlers void EnableOK(BOOL bEnable) { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, BFFM_ENABLEOK, 0, bEnable); } void SetSelection(LPCITEMIDLIST pItemIDList) { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, BFFM_SETSELECTION, FALSE, (LPARAM)pItemIDList); } void SetSelection(LPCTSTR lpstrFolderPath) { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, BFFM_SETSELECTION, TRUE, (LPARAM)lpstrFolderPath); } void SetStatusText(LPCTSTR lpstrText) { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)lpstrText); } void SetOKText(LPCTSTR lpstrOKText) { #ifndef BFFM_SETOKTEXT const UINT BFFM_SETOKTEXT = WM_USER + 105; #endif ATLASSERT(m_hWnd != NULL); USES_CONVERSION; LPCWSTR lpstr = T2CW(lpstrOKText); ::SendMessage(m_hWnd, BFFM_SETOKTEXT, 0, (LPARAM)lpstr); } void SetExpanded(LPCITEMIDLIST pItemIDList) { #ifndef BFFM_SETEXPANDED const UINT BFFM_SETEXPANDED = WM_USER + 106; #endif ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, BFFM_SETEXPANDED, FALSE, (LPARAM)pItemIDList); } void SetExpanded(LPCTSTR lpstrFolderPath) { #ifndef BFFM_SETEXPANDED const UINT BFFM_SETEXPANDED = WM_USER + 106; #endif ATLASSERT(m_hWnd != NULL); USES_CONVERSION; LPCWSTR lpstr = T2CW(lpstrFolderPath); ::SendMessage(m_hWnd, BFFM_SETEXPANDED, TRUE, (LPARAM)lpstr); } }; class CFolderDialog : public CFolderDialogImpl { public: CFolderDialog(HWND hWndParent = NULL, LPCTSTR lpstrTitle = NULL, UINT uFlags = BIF_RETURNONLYFSDIRS) : CFolderDialogImpl(hWndParent, lpstrTitle, uFlags) { } }; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CCommonDialogImplBase - base class for common dialog classes class ATL_NO_VTABLE CCommonDialogImplBase : public ATL::CWindowImplBase { public: static UINT_PTR APIENTRY HookProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(uMsg != WM_INITDIALOG) return 0; CCommonDialogImplBase* pT = (CCommonDialogImplBase*)ModuleHelper::ExtractCreateWndData(); ATLASSERT(pT != NULL); ATLASSERT(pT->m_hWnd == NULL); ATLASSERT(::IsWindow(hWnd)); // subclass dialog's window if(!pT->SubclassWindow(hWnd)) { ATLTRACE2(atlTraceUI, 0, _T("Subclassing a common dialog failed\n")); return 0; } // check message map for WM_INITDIALOG handler LRESULT lRes = 0; if(pT->ProcessWindowMessage(pT->m_hWnd, uMsg, wParam, lParam, lRes, 0) == FALSE) return 0; return lRes; } // Special override for common dialogs BOOL EndDialog(INT_PTR /*nRetCode*/ = 0) { ATLASSERT(::IsWindow(m_hWnd)); SendMessage(WM_COMMAND, MAKEWPARAM(IDABORT, 0)); return TRUE; } // Implementation - try to override these, to prevent errors HWND Create(HWND, ATL::_U_RECT, LPCTSTR, DWORD, DWORD, ATL::_U_MENUorID, ATOM, LPVOID) { ATLASSERT(FALSE); // should not be called return NULL; } static LRESULT CALLBACK StartWindowProc(HWND /*hWnd*/, UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/) { ATLASSERT(FALSE); // should not be called return 0; } }; /////////////////////////////////////////////////////////////////////////////// // CFontDialogImpl - font selection dialog #ifndef _WIN32_WCE template class ATL_NO_VTABLE CFontDialogImpl : public CCommonDialogImplBase { public: enum { _cchStyleName = 64 }; CHOOSEFONT m_cf; TCHAR m_szStyleName[_cchStyleName]; // contains style name after return LOGFONT m_lf; // default LOGFONT to store the info // Constructors CFontDialogImpl(LPLOGFONT lplfInitial = NULL, DWORD dwFlags = CF_EFFECTS | CF_SCREENFONTS, HDC hDCPrinter = NULL, HWND hWndParent = NULL) { memset(&m_cf, 0, sizeof(m_cf)); memset(&m_lf, 0, sizeof(m_lf)); memset(&m_szStyleName, 0, sizeof(m_szStyleName)); m_cf.lStructSize = sizeof(m_cf); m_cf.hwndOwner = hWndParent; m_cf.rgbColors = RGB(0, 0, 0); m_cf.lpszStyle = (LPTSTR)&m_szStyleName; m_cf.Flags = dwFlags | CF_ENABLEHOOK; m_cf.lpfnHook = (LPCFHOOKPROC)T::HookProc; if(lplfInitial != NULL) { m_cf.lpLogFont = lplfInitial; m_cf.Flags |= CF_INITTOLOGFONTSTRUCT; m_lf = *lplfInitial; } else { m_cf.lpLogFont = &m_lf; } if(hDCPrinter != NULL) { m_cf.hDC = hDCPrinter; m_cf.Flags |= CF_PRINTERFONTS; } } // Operations INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow()) { ATLASSERT((m_cf.Flags & CF_ENABLEHOOK) != 0); ATLASSERT(m_cf.lpfnHook != NULL); // can still be a user hook if(m_cf.hwndOwner == NULL) // set only if not specified before m_cf.hwndOwner = hWndParent; ATLASSERT(m_hWnd == NULL); #if (_ATL_VER >= 0x0800) // Allocate the thunk structure here, where we can fail gracefully. BOOL bRetTh = m_thunk.Init(NULL, NULL); if(bRetTh == FALSE) { ::SetLastError(ERROR_OUTOFMEMORY); return -1; } #endif // (_ATL_VER >= 0x0800) ModuleHelper::AddCreateWndData(&m_thunk.cd, (CCommonDialogImplBase*)this); BOOL bRet = ::ChooseFont(&m_cf); m_hWnd = NULL; if(bRet) // copy logical font from user's initialization buffer (if needed) SecureHelper::memcpy_x(&m_lf, sizeof(m_lf), m_cf.lpLogFont, sizeof(m_lf)); return bRet ? IDOK : IDCANCEL; } // works only when the dialog is dislayed or after void GetCurrentFont(LPLOGFONT lplf) const { ATLASSERT(lplf != NULL); if(m_hWnd != NULL) ::SendMessage(m_hWnd, WM_CHOOSEFONT_GETLOGFONT, 0, (LPARAM)lplf); else *lplf = m_lf; } // works only when the dialog is dislayed or before #ifndef _WIN32_WCE void SetLogFont(LPLOGFONT lplf) { ATLASSERT(lplf != NULL); #ifndef WM_CHOOSEFONT_SETLOGFONT const UINT WM_CHOOSEFONT_SETLOGFONT = (WM_USER + 101); #endif if(m_hWnd != NULL) { ::SendMessage(m_hWnd, WM_CHOOSEFONT_SETLOGFONT, 0, (LPARAM)lplf); } else { m_lf = *lplf; m_cf.Flags |= CF_INITTOLOGFONTSTRUCT; } } void SetFlags(DWORD dwFlags) { #ifndef WM_CHOOSEFONT_SETFLAGS const UINT WM_CHOOSEFONT_SETFLAGS = (WM_USER + 102); #endif if(m_hWnd != NULL) { CHOOSEFONT cf = { sizeof(CHOOSEFONT) }; cf.Flags = dwFlags; ::SendMessage(m_hWnd, WM_CHOOSEFONT_SETFLAGS, 0, (LPARAM)&cf); } else { m_cf.Flags = dwFlags; } } #endif // !_WIN32_WCE // Helpers for parsing information after successful return LPCTSTR GetFaceName() const // return the face name of the font { return (LPCTSTR)m_cf.lpLogFont->lfFaceName; } LPCTSTR GetStyleName() const // return the style name of the font { return m_cf.lpszStyle; } int GetSize() const // return the pt size of the font { return m_cf.iPointSize; } COLORREF GetColor() const // return the color of the font { return m_cf.rgbColors; } int GetWeight() const // return the chosen font weight { return (int)m_cf.lpLogFont->lfWeight; } BOOL IsStrikeOut() const // return TRUE if strikeout { return (m_cf.lpLogFont->lfStrikeOut) ? TRUE : FALSE; } BOOL IsUnderline() const // return TRUE if underline { return (m_cf.lpLogFont->lfUnderline) ? TRUE : FALSE; } BOOL IsBold() const // return TRUE if bold font { return (m_cf.lpLogFont->lfWeight == FW_BOLD) ? TRUE : FALSE; } BOOL IsItalic() const // return TRUE if italic font { return m_cf.lpLogFont->lfItalic ? TRUE : FALSE; } }; class CFontDialog : public CFontDialogImpl { public: CFontDialog(LPLOGFONT lplfInitial = NULL, DWORD dwFlags = CF_EFFECTS | CF_SCREENFONTS, HDC hDCPrinter = NULL, HWND hWndParent = NULL) : CFontDialogImpl(lplfInitial, dwFlags, hDCPrinter, hWndParent) { } DECLARE_EMPTY_MSG_MAP() }; #endif // _WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CRichEditFontDialogImpl - font selection for the Rich Edit ctrl #if defined(_RICHEDIT_) && !defined(_WIN32_WCE) template class ATL_NO_VTABLE CRichEditFontDialogImpl : public CFontDialogImpl< T > { public: CRichEditFontDialogImpl(const CHARFORMAT& charformat, DWORD dwFlags = CF_SCREENFONTS, HDC hDCPrinter = NULL, HWND hWndParent = NULL) : CFontDialogImpl< T >(NULL, dwFlags, hDCPrinter, hWndParent) { m_cf.Flags |= CF_INITTOLOGFONTSTRUCT; m_cf.Flags |= FillInLogFont(charformat); m_cf.lpLogFont = &m_lf; if((charformat.dwMask & CFM_COLOR) != 0) m_cf.rgbColors = charformat.crTextColor; } void GetCharFormat(CHARFORMAT& cf) const { USES_CONVERSION; cf.dwEffects = 0; cf.dwMask = 0; if((m_cf.Flags & CF_NOSTYLESEL) == 0) { cf.dwMask |= CFM_BOLD | CFM_ITALIC; cf.dwEffects |= IsBold() ? CFE_BOLD : 0; cf.dwEffects |= IsItalic() ? CFE_ITALIC : 0; } if((m_cf.Flags & CF_NOSIZESEL) == 0) { cf.dwMask |= CFM_SIZE; // GetSize() returns in tenths of points so mulitply by 2 to get twips cf.yHeight = GetSize() * 2; } if((m_cf.Flags & CF_NOFACESEL) == 0) { cf.dwMask |= CFM_FACE; cf.bPitchAndFamily = m_cf.lpLogFont->lfPitchAndFamily; #if (_RICHEDIT_VER >= 0x0200) SecureHelper::strcpy_x(cf.szFaceName, _countof(cf.szFaceName), GetFaceName()); #else // !(_RICHEDIT_VER >= 0x0200) SecureHelper::strcpyA_x(cf.szFaceName, _countof(cf.szFaceName), T2A((LPTSTR)(LPCTSTR)GetFaceName())); #endif // !(_RICHEDIT_VER >= 0x0200) } if((m_cf.Flags & CF_EFFECTS) != 0) { cf.dwMask |= CFM_UNDERLINE | CFM_STRIKEOUT | CFM_COLOR; cf.dwEffects |= IsUnderline() ? CFE_UNDERLINE : 0; cf.dwEffects |= IsStrikeOut() ? CFE_STRIKEOUT : 0; cf.crTextColor = GetColor(); } if((m_cf.Flags & CF_NOSCRIPTSEL) == 0) { cf.bCharSet = m_cf.lpLogFont->lfCharSet; cf.dwMask |= CFM_CHARSET; } cf.yOffset = 0; } DWORD FillInLogFont(const CHARFORMAT& cf) { USES_CONVERSION; DWORD dwFlags = 0; if((cf.dwMask & CFM_SIZE) != 0) { HDC hDC = ::CreateDC(_T("DISPLAY"), NULL, NULL, NULL); LONG yPerInch = ::GetDeviceCaps(hDC, LOGPIXELSY); m_lf.lfHeight = -(int)((cf.yHeight * yPerInch) / 1440); } else m_lf.lfHeight = 0; m_lf.lfWidth = 0; m_lf.lfEscapement = 0; m_lf.lfOrientation = 0; if((cf.dwMask & (CFM_ITALIC | CFM_BOLD)) == (CFM_ITALIC | CFM_BOLD)) { m_lf.lfWeight = ((cf.dwEffects & CFE_BOLD) != 0) ? FW_BOLD : FW_NORMAL; m_lf.lfItalic = (BYTE)(((cf.dwEffects & CFE_ITALIC) != 0) ? TRUE : FALSE); } else { dwFlags |= CF_NOSTYLESEL; m_lf.lfWeight = FW_DONTCARE; m_lf.lfItalic = FALSE; } if((cf.dwMask & (CFM_UNDERLINE | CFM_STRIKEOUT | CFM_COLOR)) == (CFM_UNDERLINE|CFM_STRIKEOUT|CFM_COLOR)) { dwFlags |= CF_EFFECTS; m_lf.lfUnderline = (BYTE)(((cf.dwEffects & CFE_UNDERLINE) != 0) ? TRUE : FALSE); m_lf.lfStrikeOut = (BYTE)(((cf.dwEffects & CFE_STRIKEOUT) != 0) ? TRUE : FALSE); } else { m_lf.lfUnderline = (BYTE)FALSE; m_lf.lfStrikeOut = (BYTE)FALSE; } if((cf.dwMask & CFM_CHARSET) != 0) m_lf.lfCharSet = cf.bCharSet; else dwFlags |= CF_NOSCRIPTSEL; m_lf.lfOutPrecision = OUT_DEFAULT_PRECIS; m_lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; m_lf.lfQuality = DEFAULT_QUALITY; if((cf.dwMask & CFM_FACE) != 0) { m_lf.lfPitchAndFamily = cf.bPitchAndFamily; #if (_RICHEDIT_VER >= 0x0200) SecureHelper::strcpy_x(m_lf.lfFaceName, _countof(m_lf.lfFaceName), cf.szFaceName); #else // !(_RICHEDIT_VER >= 0x0200) SecureHelper::strcpy_x(m_lf.lfFaceName, _countof(m_lf.lfFaceName), A2T((LPSTR)cf.szFaceName)); #endif // !(_RICHEDIT_VER >= 0x0200) } else { m_lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE; m_lf.lfFaceName[0] = (TCHAR)0; } return dwFlags; } }; class CRichEditFontDialog : public CRichEditFontDialogImpl { public: CRichEditFontDialog(const CHARFORMAT& charformat, DWORD dwFlags = CF_SCREENFONTS, HDC hDCPrinter = NULL, HWND hWndParent = NULL) : CRichEditFontDialogImpl(charformat, dwFlags, hDCPrinter, hWndParent) { } DECLARE_EMPTY_MSG_MAP() }; #endif // defined(_RICHEDIT_) && !defined(_WIN32_WCE) /////////////////////////////////////////////////////////////////////////////// // CColorDialogImpl - color selection #if !defined(_WIN32_WCE) || ((_WIN32_WCE > 420) && !(defined(WIN32_PLATFORM_WFSP) && (_WIN32_WCE > 0x0500))) #ifdef _WIN32_WCE #pragma comment(lib, "commdlg.lib") #ifndef SETRGBSTRING #define SETRGBSTRING _T("commdlg_SetRGBColor") #endif #ifndef COLOROKSTRING #define COLOROKSTRING _T("commdlg_ColorOK") #endif #endif template class ATL_NO_VTABLE CColorDialogImpl : public CCommonDialogImplBase { public: CHOOSECOLOR m_cc; // Constructor CColorDialogImpl(COLORREF clrInit = 0, DWORD dwFlags = 0, HWND hWndParent = NULL) { memset(&m_cc, 0, sizeof(m_cc)); m_cc.lStructSize = sizeof(m_cc); m_cc.lpCustColors = GetCustomColors(); m_cc.hwndOwner = hWndParent; m_cc.Flags = dwFlags | CC_ENABLEHOOK; m_cc.lpfnHook = (LPCCHOOKPROC)T::HookProc; if(clrInit != 0) { m_cc.rgbResult = clrInit; m_cc.Flags |= CC_RGBINIT; } } // Operations INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow()) { ATLASSERT((m_cc.Flags & CC_ENABLEHOOK) != 0); ATLASSERT(m_cc.lpfnHook != NULL); // can still be a user hook if(m_cc.hwndOwner == NULL) // set only if not specified before m_cc.hwndOwner = hWndParent; ATLASSERT(m_hWnd == NULL); #if (_ATL_VER >= 0x0800) // Allocate the thunk structure here, where we can fail gracefully. BOOL bRetTh = m_thunk.Init(NULL, NULL); if(bRetTh == FALSE) { ::SetLastError(ERROR_OUTOFMEMORY); return -1; } #endif // (_ATL_VER >= 0x0800) ModuleHelper::AddCreateWndData(&m_thunk.cd, (CCommonDialogImplBase*)this); BOOL bRet = ::ChooseColor(&m_cc); m_hWnd = NULL; return bRet ? IDOK : IDCANCEL; } // Set the current color while dialog is displayed void SetCurrentColor(COLORREF clr) { ATLASSERT(::IsWindow(m_hWnd)); SendMessage(_GetSetRGBMessage(), 0, (LPARAM)clr); } // Get the selected color after DoModal returns, or in OnColorOK COLORREF GetColor() const { return m_cc.rgbResult; } // Special override for the color dialog static UINT_PTR APIENTRY HookProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(uMsg != WM_INITDIALOG && uMsg != _GetColorOKMessage()) return 0; LPCHOOSECOLOR lpCC = (LPCHOOSECOLOR)lParam; CCommonDialogImplBase* pT = NULL; if(uMsg == WM_INITDIALOG) { pT = (CCommonDialogImplBase*)ModuleHelper::ExtractCreateWndData(); lpCC->lCustData = (LPARAM)pT; ATLASSERT(pT != NULL); ATLASSERT(pT->m_hWnd == NULL); ATLASSERT(::IsWindow(hWnd)); // subclass dialog's window if(!pT->SubclassWindow(hWnd)) { ATLTRACE2(atlTraceUI, 0, _T("Subclassing a Color common dialog failed\n")); return 0; } } else if(uMsg == _GetColorOKMessage()) { pT = (CCommonDialogImplBase*)lpCC->lCustData; ATLASSERT(pT != NULL); ATLASSERT(::IsWindow(pT->m_hWnd)); } else { ATLASSERT(FALSE); return 0; } // pass to the message map LRESULT lRes = 0; if(pT->ProcessWindowMessage(pT->m_hWnd, uMsg, wParam, lParam, lRes, 0) == FALSE) return 0; return lRes; } // Helpers static COLORREF* GetCustomColors() { static COLORREF rgbCustomColors[16] = { RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), RGB(255, 255, 255), }; return rgbCustomColors; } static UINT _GetSetRGBMessage() { static UINT uSetRGBMessage = 0; if(uSetRGBMessage == 0) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CColorDialogImpl::_GetSetRGBMessage.\n")); ATLASSERT(FALSE); return 0; } if(uSetRGBMessage == 0) uSetRGBMessage = ::RegisterWindowMessage(SETRGBSTRING); lock.Unlock(); } ATLASSERT(uSetRGBMessage != 0); return uSetRGBMessage; } static UINT _GetColorOKMessage() { static UINT uColorOKMessage = 0; if(uColorOKMessage == 0) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CColorDialogImpl::_GetColorOKMessage.\n")); ATLASSERT(FALSE); return 0; } if(uColorOKMessage == 0) uColorOKMessage = ::RegisterWindowMessage(COLOROKSTRING); lock.Unlock(); } ATLASSERT(uColorOKMessage != 0); return uColorOKMessage; } // Message map and handlers BEGIN_MSG_MAP(CColorDialogImpl) MESSAGE_HANDLER(_GetColorOKMessage(), _OnColorOK) END_MSG_MAP() LRESULT _OnColorOK(UINT, WPARAM, LPARAM, BOOL&) { T* pT = static_cast(this); return pT->OnColorOK(); } // Overrideable BOOL OnColorOK() // validate color { return FALSE; } }; class CColorDialog : public CColorDialogImpl { public: CColorDialog(COLORREF clrInit = 0, DWORD dwFlags = 0, HWND hWndParent = NULL) : CColorDialogImpl(clrInit, dwFlags, hWndParent) { } // override base class map and references to handlers DECLARE_EMPTY_MSG_MAP() }; #endif // !defined(_WIN32_WCE) || ((_WIN32_WCE > 420) && !(defined(WIN32_PLATFORM_WFSP) && (_WIN32_WCE > 0x0500))) /////////////////////////////////////////////////////////////////////////////// // CPrintDialogImpl - used for Print... and PrintSetup... #ifndef _WIN32_WCE // global helper static inline HDC _AtlCreateDC(HGLOBAL hDevNames, HGLOBAL hDevMode) { if(hDevNames == NULL) return NULL; LPDEVNAMES lpDevNames = (LPDEVNAMES)::GlobalLock(hDevNames); LPDEVMODE lpDevMode = (hDevMode != NULL) ? (LPDEVMODE)::GlobalLock(hDevMode) : NULL; if(lpDevNames == NULL) return NULL; HDC hDC = ::CreateDC((LPCTSTR)lpDevNames + lpDevNames->wDriverOffset, (LPCTSTR)lpDevNames + lpDevNames->wDeviceOffset, (LPCTSTR)lpDevNames + lpDevNames->wOutputOffset, lpDevMode); ::GlobalUnlock(hDevNames); if(hDevMode != NULL) ::GlobalUnlock(hDevMode); return hDC; } #pragma warning(push) #pragma warning(disable: 4512) // assignment operator could not be generated template class ATL_NO_VTABLE CPrintDialogImpl : public CCommonDialogImplBase { public: // print dialog parameter block (note this is a reference) PRINTDLG& m_pd; // Constructors CPrintDialogImpl(BOOL bPrintSetupOnly = FALSE, // TRUE for Print Setup, FALSE for Print Dialog DWORD dwFlags = PD_ALLPAGES | PD_USEDEVMODECOPIES | PD_NOPAGENUMS | PD_NOSELECTION, HWND hWndParent = NULL) : m_pd(m_pdActual) { memset(&m_pdActual, 0, sizeof(m_pdActual)); m_pd.lStructSize = sizeof(m_pdActual); m_pd.hwndOwner = hWndParent; m_pd.Flags = (dwFlags | PD_ENABLEPRINTHOOK | PD_ENABLESETUPHOOK); m_pd.lpfnPrintHook = (LPPRINTHOOKPROC)T::HookProc; m_pd.lpfnSetupHook = (LPSETUPHOOKPROC)T::HookProc; if(bPrintSetupOnly) m_pd.Flags |= PD_PRINTSETUP; else m_pd.Flags |= PD_RETURNDC; m_pd.Flags &= ~PD_RETURNIC; // do not support information context } // Operations INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow()) { ATLASSERT((m_pd.Flags & PD_ENABLEPRINTHOOK) != 0); ATLASSERT((m_pd.Flags & PD_ENABLESETUPHOOK) != 0); ATLASSERT(m_pd.lpfnPrintHook != NULL); // can still be a user hook ATLASSERT(m_pd.lpfnSetupHook != NULL); // can still be a user hook ATLASSERT((m_pd.Flags & PD_RETURNDEFAULT) == 0); // use GetDefaults for this if(m_pd.hwndOwner == NULL) // set only if not specified before m_pd.hwndOwner = hWndParent; ATLASSERT(m_hWnd == NULL); #if (_ATL_VER >= 0x0800) // Allocate the thunk structure here, where we can fail gracefully. BOOL bRetTh = m_thunk.Init(NULL, NULL); if(bRetTh == FALSE) { ::SetLastError(ERROR_OUTOFMEMORY); return -1; } #endif // (_ATL_VER >= 0x0800) ModuleHelper::AddCreateWndData(&m_thunk.cd, (CCommonDialogImplBase*)this); BOOL bRet = ::PrintDlg(&m_pd); m_hWnd = NULL; return bRet ? IDOK : IDCANCEL; } // GetDefaults will not display a dialog but will get device defaults BOOL GetDefaults() { m_pd.Flags |= PD_RETURNDEFAULT; ATLASSERT(m_pd.hDevMode == NULL); // must be NULL ATLASSERT(m_pd.hDevNames == NULL); // must be NULL return ::PrintDlg(&m_pd); } // Helpers for parsing information after successful return num. copies requested int GetCopies() const { if((m_pd.Flags & PD_USEDEVMODECOPIES) != 0) { LPDEVMODE lpDevMode = GetDevMode(); return (lpDevMode != NULL) ? lpDevMode->dmCopies : -1; } return m_pd.nCopies; } BOOL PrintCollate() const // TRUE if collate checked { return ((m_pd.Flags & PD_COLLATE) != 0) ? TRUE : FALSE; } BOOL PrintSelection() const // TRUE if printing selection { return ((m_pd.Flags & PD_SELECTION) != 0) ? TRUE : FALSE; } BOOL PrintAll() const // TRUE if printing all pages { return (!PrintRange() && !PrintSelection()) ? TRUE : FALSE; } BOOL PrintRange() const // TRUE if printing page range { return ((m_pd.Flags & PD_PAGENUMS) != 0) ? TRUE : FALSE; } BOOL PrintToFile() const // TRUE if printing to a file { return ((m_pd.Flags & PD_PRINTTOFILE) != 0) ? TRUE : FALSE; } int GetFromPage() const // starting page if valid { return PrintRange() ? m_pd.nFromPage : -1; } int GetToPage() const // ending page if valid { return PrintRange() ? m_pd.nToPage : -1; } LPDEVMODE GetDevMode() const // return DEVMODE { if(m_pd.hDevMode == NULL) return NULL; return (LPDEVMODE)::GlobalLock(m_pd.hDevMode); } LPCTSTR GetDriverName() const // return driver name { if(m_pd.hDevNames == NULL) return NULL; LPDEVNAMES lpDev = (LPDEVNAMES)::GlobalLock(m_pd.hDevNames); if(lpDev == NULL) return NULL; return (LPCTSTR)lpDev + lpDev->wDriverOffset; } LPCTSTR GetDeviceName() const // return device name { if(m_pd.hDevNames == NULL) return NULL; LPDEVNAMES lpDev = (LPDEVNAMES)::GlobalLock(m_pd.hDevNames); if(lpDev == NULL) return NULL; return (LPCTSTR)lpDev + lpDev->wDeviceOffset; } LPCTSTR GetPortName() const // return output port name { if(m_pd.hDevNames == NULL) return NULL; LPDEVNAMES lpDev = (LPDEVNAMES)::GlobalLock(m_pd.hDevNames); if(lpDev == NULL) return NULL; return (LPCTSTR)lpDev + lpDev->wOutputOffset; } HDC GetPrinterDC() const // return HDC (caller must delete) { ATLASSERT((m_pd.Flags & PD_RETURNDC) != 0); return m_pd.hDC; } // This helper creates a DC based on the DEVNAMES and DEVMODE structures. // This DC is returned, but also stored in m_pd.hDC as though it had been // returned by CommDlg. It is assumed that any previously obtained DC // has been/will be deleted by the user. This may be // used without ever invoking the print/print setup dialogs. HDC CreatePrinterDC() { m_pd.hDC = _AtlCreateDC(m_pd.hDevNames, m_pd.hDevMode); return m_pd.hDC; } // Implementation PRINTDLG m_pdActual; // the Print/Print Setup need to share this // The following handle the case of print setup... from the print dialog CPrintDialogImpl(PRINTDLG& pdInit) : m_pd(pdInit) { } BEGIN_MSG_MAP(CPrintDialogImpl) #ifdef psh1 COMMAND_ID_HANDLER(psh1, OnPrintSetup) // print setup button when print is displayed #else // !psh1 COMMAND_ID_HANDLER(0x0400, OnPrintSetup) // value from dlgs.h #endif // !psh1 END_MSG_MAP() LRESULT OnPrintSetup(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& /*bHandled*/) { T dlgSetup(m_pd); ModuleHelper::AddCreateWndData(&dlgSetup.m_thunk.cd, (CCommonDialogImplBase*)&dlgSetup); return DefWindowProc(WM_COMMAND, MAKEWPARAM(wID, wNotifyCode), (LPARAM)hWndCtl); } }; class CPrintDialog : public CPrintDialogImpl { public: CPrintDialog(BOOL bPrintSetupOnly = FALSE, DWORD dwFlags = PD_ALLPAGES | PD_USEDEVMODECOPIES | PD_NOPAGENUMS | PD_NOSELECTION, HWND hWndParent = NULL) : CPrintDialogImpl(bPrintSetupOnly, dwFlags, hWndParent) { } CPrintDialog(PRINTDLG& pdInit) : CPrintDialogImpl(pdInit) { } }; #pragma warning(pop) #endif // _WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CPrintDialogExImpl - new print dialog for Windows 2000 #if (WINVER >= 0x0500) && !defined(_WIN32_WCE) }; // namespace WTL #include extern "C" const __declspec(selectany) IID IID_IPrintDialogCallback = {0x5852a2c3, 0x6530, 0x11d1, {0xb6, 0xa3, 0x0, 0x0, 0xf8, 0x75, 0x7b, 0xf9}}; extern "C" const __declspec(selectany) IID IID_IPrintDialogServices = {0x509aaeda, 0x5639, 0x11d1, {0xb6, 0xa1, 0x0, 0x0, 0xf8, 0x75, 0x7b, 0xf9}}; namespace WTL { template class ATL_NO_VTABLE CPrintDialogExImpl : public ATL::CWindow, public ATL::CMessageMap, public IPrintDialogCallback, public ATL::IObjectWithSiteImpl< T > { public: PRINTDLGEX m_pdex; // Constructor CPrintDialogExImpl(DWORD dwFlags = PD_ALLPAGES | PD_USEDEVMODECOPIES | PD_NOPAGENUMS | PD_NOSELECTION | PD_NOCURRENTPAGE, HWND hWndParent = NULL) { memset(&m_pdex, 0, sizeof(m_pdex)); m_pdex.lStructSize = sizeof(PRINTDLGEX); m_pdex.hwndOwner = hWndParent; m_pdex.Flags = dwFlags; m_pdex.nStartPage = START_PAGE_GENERAL; // callback object will be set in DoModal m_pdex.Flags &= ~PD_RETURNIC; // do not support information context } // Operations HRESULT DoModal(HWND hWndParent = ::GetActiveWindow()) { ATLASSERT(m_hWnd == NULL); ATLASSERT((m_pdex.Flags & PD_RETURNDEFAULT) == 0); // use GetDefaults for this if(m_pdex.hwndOwner == NULL) // set only if not specified before m_pdex.hwndOwner = hWndParent; T* pT = static_cast(this); m_pdex.lpCallback = (IUnknown*)(IPrintDialogCallback*)pT; HRESULT hResult = ::PrintDlgEx(&m_pdex); m_hWnd = NULL; return hResult; } BOOL EndDialog(INT_PTR /*nRetCode*/ = 0) { ATLASSERT(::IsWindow(m_hWnd)); SendMessage(WM_COMMAND, MAKEWPARAM(IDABORT, 0)); return TRUE; } // GetDefaults will not display a dialog but will get device defaults HRESULT GetDefaults() { ATLASSERT(m_pdex.hDevMode == NULL); // must be NULL ATLASSERT(m_pdex.hDevNames == NULL); // must be NULL if(m_pdex.hwndOwner == NULL) // set only if not specified before m_pdex.hwndOwner = ::GetActiveWindow(); m_pdex.Flags |= PD_RETURNDEFAULT; HRESULT hRet = ::PrintDlgEx(&m_pdex); m_pdex.Flags &= ~PD_RETURNDEFAULT; return hRet; } // Helpers for parsing information after successful return num. copies requested int GetCopies() const { if((m_pdex.Flags & PD_USEDEVMODECOPIES) != 0) { LPDEVMODE lpDevMode = GetDevMode(); return (lpDevMode != NULL) ? lpDevMode->dmCopies : -1; } return m_pdex.nCopies; } BOOL PrintCollate() const // TRUE if collate checked { return ((m_pdex.Flags & PD_COLLATE) != 0) ? TRUE : FALSE; } BOOL PrintSelection() const // TRUE if printing selection { return ((m_pdex.Flags & PD_SELECTION) != 0) ? TRUE : FALSE; } BOOL PrintAll() const // TRUE if printing all pages { return (!PrintRange() && !PrintSelection()) ? TRUE : FALSE; } BOOL PrintRange() const // TRUE if printing page range { return ((m_pdex.Flags & PD_PAGENUMS) != 0) ? TRUE : FALSE; } BOOL PrintToFile() const // TRUE if printing to a file { return ((m_pdex.Flags & PD_PRINTTOFILE) != 0) ? TRUE : FALSE; } LPDEVMODE GetDevMode() const // return DEVMODE { if(m_pdex.hDevMode == NULL) return NULL; return (LPDEVMODE)::GlobalLock(m_pdex.hDevMode); } LPCTSTR GetDriverName() const // return driver name { if(m_pdex.hDevNames == NULL) return NULL; LPDEVNAMES lpDev = (LPDEVNAMES)::GlobalLock(m_pdex.hDevNames); if(lpDev == NULL) return NULL; return (LPCTSTR)lpDev + lpDev->wDriverOffset; } LPCTSTR GetDeviceName() const // return device name { if(m_pdex.hDevNames == NULL) return NULL; LPDEVNAMES lpDev = (LPDEVNAMES)::GlobalLock(m_pdex.hDevNames); if(lpDev == NULL) return NULL; return (LPCTSTR)lpDev + lpDev->wDeviceOffset; } LPCTSTR GetPortName() const // return output port name { if(m_pdex.hDevNames == NULL) return NULL; LPDEVNAMES lpDev = (LPDEVNAMES)::GlobalLock(m_pdex.hDevNames); if(lpDev == NULL) return NULL; return (LPCTSTR)lpDev + lpDev->wOutputOffset; } HDC GetPrinterDC() const // return HDC (caller must delete) { ATLASSERT((m_pdex.Flags & PD_RETURNDC) != 0); return m_pdex.hDC; } // This helper creates a DC based on the DEVNAMES and DEVMODE structures. // This DC is returned, but also stored in m_pdex.hDC as though it had been // returned by CommDlg. It is assumed that any previously obtained DC // has been/will be deleted by the user. This may be // used without ever invoking the print/print setup dialogs. HDC CreatePrinterDC() { m_pdex.hDC = _AtlCreateDC(m_pdex.hDevNames, m_pdex.hDevMode); return m_pdex.hDC; } // Implementation - interfaces // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) { if(ppvObject == NULL) return E_POINTER; T* pT = static_cast(this); if(IsEqualGUID(riid, IID_IUnknown) || IsEqualGUID(riid, IID_IPrintDialogCallback)) { *ppvObject = (IPrintDialogCallback*)pT; // AddRef() not needed return S_OK; } else if(IsEqualGUID(riid, IID_IObjectWithSite)) { *ppvObject = (IObjectWithSite*)pT; // AddRef() not needed return S_OK; } return E_NOINTERFACE; } virtual ULONG STDMETHODCALLTYPE AddRef() { return 1; } virtual ULONG STDMETHODCALLTYPE Release() { return 1; } // IPrintDialogCallback STDMETHOD(InitDone)() { return S_FALSE; } STDMETHOD(SelectionChange)() { return S_FALSE; } STDMETHOD(HandleMessage)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plResult) { // set up m_hWnd the first time if(m_hWnd == NULL) Attach(hWnd); // call message map HRESULT hRet = ProcessWindowMessage(hWnd, uMsg, wParam, lParam, *plResult, 0) ? S_OK : S_FALSE; if(hRet == S_OK && uMsg == WM_NOTIFY) // return in DWLP_MSGRESULT ::SetWindowLongPtr(GetParent(), DWLP_MSGRESULT, (LONG_PTR)*plResult); if(uMsg == WM_INITDIALOG && hRet == S_OK && (BOOL)*plResult != FALSE) hRet = S_FALSE; return hRet; } }; class CPrintDialogEx : public CPrintDialogExImpl { public: CPrintDialogEx( DWORD dwFlags = PD_ALLPAGES | PD_USEDEVMODECOPIES | PD_NOPAGENUMS | PD_NOSELECTION | PD_NOCURRENTPAGE, HWND hWndParent = NULL) : CPrintDialogExImpl(dwFlags, hWndParent) { } DECLARE_EMPTY_MSG_MAP() }; #endif // (WINVER >= 0x0500) && !defined(_WIN32_WCE) /////////////////////////////////////////////////////////////////////////////// // CPageSetupDialogImpl - Page Setup dialog #ifndef _WIN32_WCE template class ATL_NO_VTABLE CPageSetupDialogImpl : public CCommonDialogImplBase { public: PAGESETUPDLG m_psd; ATL::CWndProcThunk m_thunkPaint; // Constructors CPageSetupDialogImpl(DWORD dwFlags = PSD_MARGINS | PSD_INWININIINTLMEASURE, HWND hWndParent = NULL) { memset(&m_psd, 0, sizeof(m_psd)); m_psd.lStructSize = sizeof(m_psd); m_psd.hwndOwner = hWndParent; m_psd.Flags = (dwFlags | PSD_ENABLEPAGESETUPHOOK | PSD_ENABLEPAGEPAINTHOOK); m_psd.lpfnPageSetupHook = (LPPAGESETUPHOOK)T::HookProc; m_thunkPaint.Init((WNDPROC)T::PaintHookProc, this); #if (_ATL_VER >= 0x0700) m_psd.lpfnPagePaintHook = (LPPAGEPAINTHOOK)m_thunkPaint.GetWNDPROC(); #else m_psd.lpfnPagePaintHook = (LPPAGEPAINTHOOK)&(m_thunkPaint.thunk); #endif } DECLARE_EMPTY_MSG_MAP() // Attributes LPDEVMODE GetDevMode() const // return DEVMODE { if(m_psd.hDevMode == NULL) return NULL; return (LPDEVMODE)::GlobalLock(m_psd.hDevMode); } LPCTSTR GetDriverName() const // return driver name { if(m_psd.hDevNames == NULL) return NULL; LPDEVNAMES lpDev = (LPDEVNAMES)::GlobalLock(m_psd.hDevNames); return (LPCTSTR)lpDev + lpDev->wDriverOffset; } LPCTSTR GetDeviceName() const // return device name { if(m_psd.hDevNames == NULL) return NULL; LPDEVNAMES lpDev = (LPDEVNAMES)::GlobalLock(m_psd.hDevNames); return (LPCTSTR)lpDev + lpDev->wDeviceOffset; } LPCTSTR GetPortName() const // return output port name { if(m_psd.hDevNames == NULL) return NULL; LPDEVNAMES lpDev = (LPDEVNAMES)::GlobalLock(m_psd.hDevNames); return (LPCTSTR)lpDev + lpDev->wOutputOffset; } HDC CreatePrinterDC() { return _AtlCreateDC(m_psd.hDevNames, m_psd.hDevMode); } SIZE GetPaperSize() const { SIZE size = { m_psd.ptPaperSize.x, m_psd.ptPaperSize.y }; return size; } void GetMargins(LPRECT lpRectMargins, LPRECT lpRectMinMargins) const { if(lpRectMargins != NULL) *lpRectMargins = m_psd.rtMargin; if(lpRectMinMargins != NULL) *lpRectMinMargins = m_psd.rtMinMargin; } // Operations INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow()) { ATLASSERT((m_psd.Flags & PSD_ENABLEPAGESETUPHOOK) != 0); ATLASSERT((m_psd.Flags & PSD_ENABLEPAGEPAINTHOOK) != 0); ATLASSERT(m_psd.lpfnPageSetupHook != NULL); // can still be a user hook ATLASSERT(m_psd.lpfnPagePaintHook != NULL); // can still be a user hook if(m_psd.hwndOwner == NULL) // set only if not specified before m_psd.hwndOwner = hWndParent; ATLASSERT(m_hWnd == NULL); #if (_ATL_VER >= 0x0800) // Allocate the thunk structure here, where we can fail gracefully. BOOL bRetTh = m_thunk.Init(NULL, NULL); if(bRetTh == FALSE) { ::SetLastError(ERROR_OUTOFMEMORY); return -1; } #endif // (_ATL_VER >= 0x0800) ModuleHelper::AddCreateWndData(&m_thunk.cd, (CCommonDialogImplBase*)this); BOOL bRet = ::PageSetupDlg(&m_psd); m_hWnd = NULL; return bRet ? IDOK : IDCANCEL; } // Implementation static UINT_PTR CALLBACK PaintHookProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { T* pT = (T*)hWnd; UINT_PTR uRet = 0; switch(uMsg) { case WM_PSD_PAGESETUPDLG: uRet = pT->PreDrawPage(LOWORD(wParam), HIWORD(wParam), (LPPAGESETUPDLG)lParam); break; case WM_PSD_FULLPAGERECT: case WM_PSD_MINMARGINRECT: case WM_PSD_MARGINRECT: case WM_PSD_GREEKTEXTRECT: case WM_PSD_ENVSTAMPRECT: case WM_PSD_YAFULLPAGERECT: uRet = pT->OnDrawPage(uMsg, (HDC)wParam, (LPRECT)lParam); break; default: ATLTRACE2(atlTraceUI, 0, _T("CPageSetupDialogImpl::PaintHookProc - unknown message received\n")); break; } return uRet; } // Overridables UINT_PTR PreDrawPage(WORD /*wPaper*/, WORD /*wFlags*/, LPPAGESETUPDLG /*pPSD*/) { // return 1 to prevent any more drawing return 0; } UINT_PTR OnDrawPage(UINT /*uMsg*/, HDC /*hDC*/, LPRECT /*lpRect*/) { return 0; // do the default } }; class CPageSetupDialog : public CPageSetupDialogImpl { public: CPageSetupDialog(DWORD dwFlags = PSD_MARGINS | PSD_INWININIINTLMEASURE, HWND hWndParent = NULL) : CPageSetupDialogImpl(dwFlags, hWndParent) { } // override PaintHookProc and references to handlers static UINT_PTR CALLBACK PaintHookProc(HWND, UINT, WPARAM, LPARAM) { return 0; } }; #endif // _WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CFindReplaceDialogImpl - Find/FindReplace modeless dialogs #ifndef _WIN32_WCE template class ATL_NO_VTABLE CFindReplaceDialogImpl : public CCommonDialogImplBase { public: enum { _cchFindReplaceBuffer = 128 }; FINDREPLACE m_fr; TCHAR m_szFindWhat[_cchFindReplaceBuffer]; TCHAR m_szReplaceWith[_cchFindReplaceBuffer]; // Constructors CFindReplaceDialogImpl() { memset(&m_fr, 0, sizeof(m_fr)); m_szFindWhat[0] = _T('\0'); m_szReplaceWith[0] = _T('\0'); m_fr.lStructSize = sizeof(m_fr); m_fr.Flags = FR_ENABLEHOOK; m_fr.lpfnHook = (LPFRHOOKPROC)T::HookProc; m_fr.lpstrFindWhat = (LPTSTR)m_szFindWhat; m_fr.wFindWhatLen = _cchFindReplaceBuffer; m_fr.lpstrReplaceWith = (LPTSTR)m_szReplaceWith; m_fr.wReplaceWithLen = _cchFindReplaceBuffer; } // Note: You must allocate the object on the heap. // If you do not, you must override OnFinalMessage() virtual void OnFinalMessage(HWND /*hWnd*/) { delete this; } HWND Create(BOOL bFindDialogOnly, // TRUE for Find, FALSE for FindReplace LPCTSTR lpszFindWhat, LPCTSTR lpszReplaceWith = NULL, DWORD dwFlags = FR_DOWN, HWND hWndParent = NULL) { ATLASSERT((m_fr.Flags & FR_ENABLEHOOK) != 0); ATLASSERT(m_fr.lpfnHook != NULL); m_fr.Flags |= dwFlags; if(hWndParent == NULL) m_fr.hwndOwner = ::GetActiveWindow(); else m_fr.hwndOwner = hWndParent; ATLASSERT(m_fr.hwndOwner != NULL); // must have an owner for modeless dialog if(lpszFindWhat != NULL) SecureHelper::strncpy_x(m_szFindWhat, _countof(m_szFindWhat), lpszFindWhat, _TRUNCATE); if(lpszReplaceWith != NULL) SecureHelper::strncpy_x(m_szReplaceWith, _countof(m_szReplaceWith), lpszReplaceWith, _TRUNCATE); ATLASSERT(m_hWnd == NULL); #if (_ATL_VER >= 0x0800) // Allocate the thunk structure here, where we can fail gracefully. BOOL bRet = m_thunk.Init(NULL, NULL); if(bRet == FALSE) { ::SetLastError(ERROR_OUTOFMEMORY); return NULL; } #endif // (_ATL_VER >= 0x0800) ModuleHelper::AddCreateWndData(&m_thunk.cd, (CCommonDialogImplBase*)this); HWND hWnd = NULL; if(bFindDialogOnly) hWnd = ::FindText(&m_fr); else hWnd = ::ReplaceText(&m_fr); ATLASSERT(m_hWnd == hWnd); return hWnd; } static const UINT GetFindReplaceMsg() { static const UINT nMsgFindReplace = ::RegisterWindowMessage(FINDMSGSTRING); return nMsgFindReplace; } // call while handling FINDMSGSTRING registered message // to retreive the object static T* PASCAL GetNotifier(LPARAM lParam) { ATLASSERT(lParam != NULL); T* pDlg = (T*)(lParam - offsetof(T, m_fr)); return pDlg; } // Operations // Helpers for parsing information after successful return LPCTSTR GetFindString() const // get find string { return (LPCTSTR)m_fr.lpstrFindWhat; } LPCTSTR GetReplaceString() const // get replacement string { return (LPCTSTR)m_fr.lpstrReplaceWith; } BOOL SearchDown() const // TRUE if search down, FALSE is up { return ((m_fr.Flags & FR_DOWN) != 0) ? TRUE : FALSE; } BOOL FindNext() const // TRUE if command is find next { return ((m_fr.Flags & FR_FINDNEXT) != 0) ? TRUE : FALSE; } BOOL MatchCase() const // TRUE if matching case { return ((m_fr.Flags & FR_MATCHCASE) != 0) ? TRUE : FALSE; } BOOL MatchWholeWord() const // TRUE if matching whole words only { return ((m_fr.Flags & FR_WHOLEWORD) != 0) ? TRUE : FALSE; } BOOL ReplaceCurrent() const // TRUE if replacing current string { return ((m_fr. Flags & FR_REPLACE) != 0) ? TRUE : FALSE; } BOOL ReplaceAll() const // TRUE if replacing all occurrences { return ((m_fr.Flags & FR_REPLACEALL) != 0) ? TRUE : FALSE; } BOOL IsTerminating() const // TRUE if terminating dialog { return ((m_fr.Flags & FR_DIALOGTERM) != 0) ? TRUE : FALSE ; } }; class CFindReplaceDialog : public CFindReplaceDialogImpl { public: DECLARE_EMPTY_MSG_MAP() }; #endif // !_WIN32_WCE ///////////////////////////////////////////////////////////////////////// // CDialogBaseUnits - Dialog Units helper // class CDialogBaseUnits { public: SIZE m_sizeUnits; // Constructors CDialogBaseUnits() { // The base units of the out-dated System Font LONG nDlgBaseUnits = ::GetDialogBaseUnits(); m_sizeUnits.cx = LOWORD(nDlgBaseUnits); m_sizeUnits.cy = HIWORD(nDlgBaseUnits); } CDialogBaseUnits(HWND hWnd) { if(!InitDialogBaseUnits(hWnd)) { LONG nDlgBaseUnits = ::GetDialogBaseUnits(); m_sizeUnits.cx = LOWORD(nDlgBaseUnits); m_sizeUnits.cy = HIWORD(nDlgBaseUnits); } } CDialogBaseUnits(HFONT hFont, HWND hWnd = NULL) { if(!InitDialogBaseUnits(hFont, hWnd)) { LONG nDlgBaseUnits = ::GetDialogBaseUnits(); m_sizeUnits.cx = LOWORD(nDlgBaseUnits); m_sizeUnits.cy = HIWORD(nDlgBaseUnits); } } CDialogBaseUnits(LOGFONT lf, HWND hWnd = NULL) { if(!InitDialogBaseUnits(lf, hWnd)) { LONG nDlgBaseUnits = ::GetDialogBaseUnits(); m_sizeUnits.cx = LOWORD(nDlgBaseUnits); m_sizeUnits.cy = HIWORD(nDlgBaseUnits); } } // Operations BOOL InitDialogBaseUnits(HWND hWnd) { ATLASSERT(::IsWindow(hWnd)); RECT rc = { 0, 0, 4, 8 }; if(!::MapDialogRect(hWnd, &rc)) return FALSE; m_sizeUnits.cx = rc.right; m_sizeUnits.cy = rc.bottom; return TRUE; } BOOL InitDialogBaseUnits(LOGFONT lf, HWND hWnd = NULL) { CFont font; font.CreateFontIndirect(&lf); if(font.IsNull()) return FALSE; return InitDialogBaseUnits(font, hWnd); } BOOL InitDialogBaseUnits(HFONT hFont, HWND hWnd = NULL) { ATLASSERT(hFont != NULL); CWindowDC dc = hWnd; TEXTMETRIC tmText = { 0 }; SIZE sizeText = { 0 }; HFONT hFontOld = dc.SelectFont(hFont); dc.GetTextMetrics(&tmText); m_sizeUnits.cy = tmText.tmHeight + tmText.tmExternalLeading; dc.GetTextExtent(_T("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), 52, &sizeText); m_sizeUnits.cx = (sizeText.cx + 26) / 52; dc.SelectFont(hFontOld); return TRUE; } SIZE GetDialogBaseUnits() const { return m_sizeUnits; } INT MapDialogPixelsX(INT x) const { return ::MulDiv(x, 4, m_sizeUnits.cx); // Pixels X to DLU } INT MapDialogPixelsY(INT y) const { return ::MulDiv(y, 8, m_sizeUnits.cy); // Pixels Y to DLU } POINT MapDialogPixels(POINT pt) const { POINT out = { MapDialogPixelsX(pt.x), MapDialogPixelsY(pt.y) }; return out; } SIZE MapDialogPixels(SIZE input) const { SIZE out = { MapDialogPixelsX(input.cx), MapDialogPixelsY(input.cy) }; return out; } RECT MapDialogPixels(RECT input) const { RECT out = { MapDialogPixelsX(input.left), MapDialogPixelsY(input.top), MapDialogPixelsX(input.right), MapDialogPixelsY(input.bottom) }; return out; } INT MapDialogUnitsX(INT x) const { return ::MulDiv(x, m_sizeUnits.cx, 4); // DLU to Pixels X } INT MapDialogUnitsY(INT y) const { return ::MulDiv(y, m_sizeUnits.cy, 8); // DLU to Pixels Y } POINT MapDialogUnits(POINT pt) const { POINT out = { MapDialogUnitsX(pt.x), MapDialogUnitsY(pt.y) }; return out; } SIZE MapDialogUnits(SIZE input) const { SIZE out = { MapDialogUnitsX(input.cx), MapDialogUnitsY(input.cy) }; return out; } RECT MapDialogUnits(RECT input) const { RECT out = { MapDialogUnitsX(input.left), MapDialogUnitsY(input.top), MapDialogUnitsX(input.right), MapDialogUnitsY(input.bottom) }; return out; } }; /////////////////////////////////////////////////////////////////////////////// // CMemDlgTemplate - in-memory dialog template - DLGTEMPLATE or DLGTEMPLATEEX #if (_ATL_VER >= 0x800) typedef ATL::_DialogSplitHelper::DLGTEMPLATEEX DLGTEMPLATEEX; typedef ATL::_DialogSplitHelper::DLGITEMTEMPLATEEX DLGITEMTEMPLATEEX; #else // (_ATL_VER >= 0x800) typedef ATL::_DialogSizeHelper::_ATL_DLGTEMPLATEEX DLGTEMPLATEEX; #pragma pack(push, 4) struct DLGITEMTEMPLATEEX { DWORD helpID; DWORD exStyle; DWORD style; short x; short y; short cx; short cy; DWORD id; }; #pragma pack(pop) #endif // (_ATL_VER >= 0x800) template class CMemDlgTemplateT { public: enum StdCtrlType { CTRL_BUTTON = 0x0080, CTRL_EDIT = 0x0081, CTRL_STATIC = 0x0082, CTRL_LISTBOX = 0x0083, CTRL_SCROLLBAR = 0x0084, CTRL_COMBOBOX = 0x0085 }; HANDLE m_hData; LPBYTE m_pData; LPBYTE m_pPtr; SIZE_T m_cAllocated; CMemDlgTemplateT() : m_hData(NULL), m_pData(NULL), m_pPtr(NULL), m_cAllocated(0) { } ~CMemDlgTemplateT() { Reset(); } bool IsValid() const { return (m_pData != NULL); } bool IsTemplateEx() const { return (IsValid() && ((DLGTEMPLATEEX*)m_pData)->signature == 0xFFFF); } LPDLGTEMPLATE GetTemplatePtr() { return reinterpret_cast(m_pData); } DLGTEMPLATEEX* GetTemplateExPtr() { return reinterpret_cast(m_pData); } void Reset() { if (IsValid()) { #ifndef UNDER_CE ::GlobalUnlock(m_pData); #endif ATLVERIFY(::GlobalFree(m_hData) == NULL); } m_hData = NULL; m_pData = NULL; m_pPtr = NULL; m_cAllocated = 0; } void Create(bool bDlgEx, LPCTSTR lpszCaption, RECT rc, DWORD dwStyle = 0, DWORD dwExStyle = 0, LPCTSTR lpstrFontName = NULL, WORD wFontSize = 0, WORD wWeight = 0, BYTE bItalic = 0, BYTE bCharset = 0, DWORD dwHelpID = 0, ATL::_U_STRINGorID ClassName = 0U, ATL::_U_STRINGorID Menu = 0U) { Create(bDlgEx, lpszCaption, (short) rc.left, (short) rc.top, (short) (rc.right - rc.left), (short) (rc.bottom - rc.top), dwStyle, dwExStyle, lpstrFontName, wFontSize, wWeight, bItalic, bCharset, dwHelpID, ClassName.m_lpstr, Menu.m_lpstr); } void Create(bool bDlgEx, LPCTSTR lpszCaption, short nX, short nY, short nWidth, short nHeight, DWORD dwStyle = 0, DWORD dwExStyle = 0, LPCTSTR lpstrFontName = NULL, WORD wFontSize = 0, WORD wWeight = 0, BYTE bItalic = 0, BYTE bCharset = 0, DWORD dwHelpID = 0, ATL::_U_STRINGorID ClassName = 0U, ATL::_U_STRINGorID Menu = 0U) { // Should have DS_SETFONT style to set the dialog font name and size if (lpstrFontName != NULL) { dwStyle |= DS_SETFONT; } else { dwStyle &= ~DS_SETFONT; } if (bDlgEx) { DLGTEMPLATEEX dlg = {1, 0xFFFF, dwHelpID, dwExStyle, dwStyle, 0, nX, nY, nWidth, nHeight}; AddData(&dlg, sizeof(dlg)); } else { DLGTEMPLATE dlg = {dwStyle, dwExStyle, 0, nX, nY, nWidth, nHeight}; AddData(&dlg, sizeof(dlg)); } #ifndef _WIN32_WCE if (Menu.m_lpstr == NULL) { WORD menuData = 0; AddData(&menuData, sizeof(WORD)); } else if (IS_INTRESOURCE(Menu.m_lpstr)) { WORD menuData[] = {0xFFFF, (WORD)Menu.m_lpstr}; AddData(menuData, sizeof(menuData)); } else { AddString(Menu.m_lpstr); } #else // _WIN32_WCE // Windows CE doesn't support the addition of menus to a dialog box ATLASSERT(Menu.m_lpstr == NULL); Menu.m_lpstr; // avoid level 4 warning WORD menuData = 0; AddData(&menuData, sizeof(WORD)); #endif // _WIN32_WCE if (ClassName.m_lpstr == NULL) { WORD classData = 0; AddData(&classData, sizeof(WORD)); } else if (IS_INTRESOURCE(ClassName.m_lpstr)) { WORD classData[] = {0xFFFF, (WORD)ClassName.m_lpstr}; AddData(classData, sizeof(classData)); } else { AddString(ClassName.m_lpstr); } // Set dialog caption AddString(lpszCaption); if (lpstrFontName != NULL) { AddData(&wFontSize, sizeof(wFontSize)); if (bDlgEx) { AddData(&wWeight, sizeof(wWeight)); AddData(&bItalic, sizeof(bItalic)); AddData(&bCharset, sizeof(bCharset)); } AddString(lpstrFontName); } } void AddControl(ATL::_U_STRINGorID ClassName, WORD wId, RECT rc, DWORD dwStyle, DWORD dwExStyle, ATL::_U_STRINGorID Text, const WORD* pCreationData = NULL, WORD nCreationData = 0, DWORD dwHelpID = 0) { AddControl(ClassName.m_lpstr, wId, (short) rc.left, (short) rc.top, (short) (rc.right - rc.left), (short) (rc.bottom - rc.top), dwStyle, dwExStyle, Text.m_lpstr, pCreationData, nCreationData, dwHelpID); } void AddControl(ATL::_U_STRINGorID ClassName, WORD wId, short nX, short nY, short nWidth, short nHeight, DWORD dwStyle, DWORD dwExStyle, ATL::_U_STRINGorID Text, const WORD* pCreationData = NULL, WORD nCreationData = 0, DWORD dwHelpID = 0) { ATLASSERT(IsValid()); // DWORD align data const DWORD_PTR dwDwordAlignBits = sizeof(DWORD) - 1; m_pPtr = (LPBYTE)(((DWORD_PTR)m_pPtr + dwDwordAlignBits) & (~dwDwordAlignBits)); if (IsTemplateEx()) { DLGTEMPLATEEX* dlg = (DLGTEMPLATEEX*)m_pData; dlg->cDlgItems++; DLGITEMTEMPLATEEX item = {dwHelpID, TWinTraits::GetWndExStyle(0) | dwExStyle, TWinTraits::GetWndStyle(0) | dwStyle, nX, nY, nWidth, nHeight, wId}; AddData(&item, sizeof(item)); } else { LPDLGTEMPLATE dlg = (LPDLGTEMPLATE)m_pData; dlg->cdit++; DLGITEMTEMPLATE item = {TWinTraits::GetWndStyle(0) | dwStyle, TWinTraits::GetWndExStyle(0) | dwExStyle, nX, nY, nWidth, nHeight, wId}; AddData(&item, sizeof(item)); } ATLASSERT(ClassName.m_lpstr != NULL); if (IS_INTRESOURCE(ClassName.m_lpstr)) { WORD wData[] = {0xFFFF, (WORD)ClassName.m_lpstr}; AddData(wData, sizeof(wData)); } else { AddString(ClassName.m_lpstr); } if (Text.m_lpstr == NULL) { WORD classData = 0; AddData(&classData, sizeof(WORD)); } else if (IS_INTRESOURCE(Text.m_lpstr)) { WORD wData[] = {0xFFFF, (WORD)Text.m_lpstr}; AddData(wData, sizeof(wData)); } else { AddString(Text.m_lpstr); } AddData(&nCreationData, sizeof(nCreationData)); if ((nCreationData != 0)) { ATLASSERT(pCreationData != NULL); AddData(pCreationData, nCreationData * sizeof(WORD)); } } void AddStdControl(StdCtrlType CtrlType, WORD wId, short nX, short nY, short nWidth, short nHeight, DWORD dwStyle, DWORD dwExStyle, ATL::_U_STRINGorID Text, const WORD* pCreationData = NULL, WORD nCreationData = 0, DWORD dwHelpID = 0) { AddControl(CtrlType, wId, nX, nY, nWidth, nHeight, dwStyle, dwExStyle, Text, pCreationData, nCreationData, dwHelpID); } void AddData(LPCVOID pData, size_t nData) { ATLASSERT(pData != NULL); const SIZE_T ALLOCATION_INCREMENT = 1024; if (m_pData == NULL) { m_cAllocated = ((nData / ALLOCATION_INCREMENT) + 1) * ALLOCATION_INCREMENT; m_hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, m_cAllocated); ATLASSERT(m_hData != NULL); #ifndef UNDER_CE m_pPtr = m_pData = static_cast(::GlobalLock(m_hData)); #else m_pPtr = m_pData = static_cast(m_hData); #endif ATLASSERT(m_pData != NULL); } else if (((m_pPtr - m_pData) + nData) > m_cAllocated) { SIZE_T ptrPos = (m_pPtr - m_pData); m_cAllocated += ((nData / ALLOCATION_INCREMENT) + 1) * ALLOCATION_INCREMENT; #ifndef UNDER_CE ::GlobalUnlock(m_pData); #endif m_hData = ::GlobalReAlloc(m_hData, m_cAllocated, GMEM_MOVEABLE | GMEM_ZEROINIT); ATLASSERT(m_hData != NULL); #ifndef UNDER_CE m_pData = static_cast(::GlobalLock(m_hData)); #else m_pData = static_cast(m_hData); #endif ATLASSERT(m_pData != NULL); m_pPtr = m_pData + ptrPos; } SecureHelper::memcpy_x(m_pPtr, m_cAllocated - (m_pPtr - m_pData), pData, nData); m_pPtr += nData; } void AddString(LPCTSTR lpszStr) { if (lpszStr == NULL) { WCHAR szEmpty = 0; AddData(&szEmpty, sizeof(szEmpty)); } else { USES_CONVERSION; LPCWSTR lpstr = T2CW(lpszStr); int nSize = lstrlenW(lpstr) + 1; AddData(lpstr, nSize * sizeof(WCHAR)); } } }; typedef CMemDlgTemplateT CMemDlgTemplate; /////////////////////////////////////////////////////////////////////////////// // Dialog and control macros for indirect dialogs // for DLGTEMPLATE #define BEGIN_DIALOG(x, y, width, height) \ void DoInitTemplate() \ { \ bool bExTemplate = false; \ short nX = x, nY = y, nWidth = width, nHeight = height; \ LPCTSTR szCaption = NULL; \ DWORD dwStyle = WS_POPUP | WS_BORDER | WS_SYSMENU; \ DWORD dwExStyle = 0; \ LPCTSTR szFontName = NULL; \ WORD wFontSize = 0; \ WORD wWeight = 0; \ BYTE bItalic = 0; \ BYTE bCharset = 0; \ DWORD dwHelpID = 0; \ ATL::_U_STRINGorID Menu = 0U; \ ATL::_U_STRINGorID ClassName = 0U; // for DLGTEMPLATEEX #define BEGIN_DIALOG_EX(x, y, width, height, helpID) \ void DoInitTemplate() \ { \ bool bExTemplate = true; \ short nX = x, nY = y, nWidth = width, nHeight = height; \ LPCTSTR szCaption = NULL; \ DWORD dwStyle = WS_POPUP | WS_BORDER | WS_SYSMENU; \ DWORD dwExStyle = 0; \ LPCTSTR szFontName = NULL; \ WORD wFontSize = 0; \ WORD wWeight = 0; \ BYTE bItalic = 0; \ BYTE bCharset = 0; \ DWORD dwHelpID = helpID; \ ATL::_U_STRINGorID Menu = 0U; \ ATL::_U_STRINGorID ClassName = 0U; #define END_DIALOG() \ m_Template.Create(bExTemplate, szCaption, nX, nY, nWidth, nHeight, dwStyle, dwExStyle, szFontName, wFontSize, wWeight, bItalic, bCharset, dwHelpID, ClassName, Menu); \ }; #define DIALOG_CAPTION(caption) \ szCaption = caption; #define DIALOG_STYLE(style) \ dwStyle = style; #define DIALOG_EXSTYLE(exStyle) \ dwExStyle = exStyle; #define DIALOG_FONT(pointSize, typeFace) \ wFontSize = pointSize; \ szFontName = typeFace; #define DIALOG_FONT_EX(pointsize, typeface, weight, italic, charset) \ ATLASSERT(bExTemplate); \ wFontSize = pointsize; \ szFontName = typeface; \ wWeight = weight; \ bItalic = italic; \ bCharset = charset; #define DIALOG_MENU(menuName) \ Menu = menuName; #define DIALOG_CLASS(className) \ ClassName = className; #define BEGIN_CONTROLS_MAP() \ void DoInitControls() \ { #define END_CONTROLS_MAP() \ }; #define CONTROL_LTEXT(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_STATIC, (WORD)id, x, y, width, height, style | SS_LEFT | WS_GROUP, exStyle, text, NULL, 0); #define CONTROL_CTEXT(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_STATIC, (WORD)id, x, y, width, height, style | SS_CENTER | WS_GROUP, exStyle, text, NULL, 0); #define CONTROL_RTEXT(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_STATIC, (WORD)id, x, y, width, height, style | SS_RIGHT | WS_GROUP, exStyle, text, NULL, 0); #define CONTROL_PUSHBUTTON(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_BUTTON, (WORD)id, x, y, width, height, style | BS_PUSHBUTTON | WS_TABSTOP, exStyle, text, NULL, 0); #define CONTROL_DEFPUSHBUTTON(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_BUTTON, (WORD)id, x, y, width, height, style | BS_DEFPUSHBUTTON | WS_TABSTOP, exStyle, text, NULL, 0); #ifndef _WIN32_WCE #define CONTROL_PUSHBOX(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_BUTTON, (WORD)id, x, y, width, height, style | BS_PUSHBOX | WS_TABSTOP, exStyle, text, NULL, 0); #endif // !_WIN32_WCE #define CONTROL_STATE3(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_BUTTON, (WORD)id, x, y, width, height, style | BS_3STATE | WS_TABSTOP, exStyle, text, NULL, 0); #define CONTROL_AUTO3STATE(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_BUTTON, (WORD)id, x, y, width, height, style | BS_AUTO3STATE | WS_TABSTOP, exStyle, text, NULL, 0); #define CONTROL_CHECKBOX(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_BUTTON, (WORD)id, x, y, width, height, style | BS_CHECKBOX | WS_TABSTOP, exStyle, text, NULL, 0); #define CONTROL_AUTOCHECKBOX(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_BUTTON, (WORD)id, x, y, width, height, style | BS_AUTOCHECKBOX | WS_TABSTOP, exStyle, text, NULL, 0); #define CONTROL_RADIOBUTTON(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_BUTTON, (WORD)id, x, y, width, height, style | BS_RADIOBUTTON | WS_TABSTOP, exStyle, text, NULL, 0); #define CONTROL_AUTORADIOBUTTON(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_BUTTON, (WORD)id, x, y, width, height, style | BS_AUTORADIOBUTTON | WS_TABSTOP, exStyle, text, NULL, 0); #define CONTROL_COMBOBOX(id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_COMBOBOX, (WORD)id, x, y, width, height, style | CBS_DROPDOWN | WS_TABSTOP, exStyle, (LPCTSTR)NULL, NULL, 0); #define CONTROL_EDITTEXT(id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_EDIT, (WORD)id, x, y, width, height, style | ES_LEFT | WS_BORDER | WS_TABSTOP, exStyle, (LPCTSTR)NULL, NULL, 0); #define CONTROL_GROUPBOX(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_BUTTON, (WORD)id, x, y, width, height, style | BS_GROUPBOX, exStyle, text, NULL, 0); #define CONTROL_LISTBOX(id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_LISTBOX, (WORD)id, x, y, width, height, style | LBS_NOTIFY | WS_BORDER, exStyle, (LPCTSTR)NULL, NULL, 0); #define CONTROL_SCROLLBAR(id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_SCROLLBAR, (WORD)id, x, y, width, height, style | SBS_HORZ, exStyle, (LPCTSTR)NULL, NULL, 0); #define CONTROL_ICON(text, id, x, y, width, height, style, exStyle) \ m_Template.AddStdControl(m_Template.CTRL_STATIC, (WORD)id, x, y, width, height, style | SS_ICON, exStyle, text, NULL, 0); #define CONTROL_CONTROL(text, id, className, style, x, y, width, height, exStyle) \ m_Template.AddControl(className, (WORD)id, x, y, width, height, style, exStyle, text, NULL, 0); /////////////////////////////////////////////////////////////////////////////// // CIndirectDialogImpl - dialogs with template in memory template class ATL_NO_VTABLE CIndirectDialogImpl : public ATL::CDialogImpl< T, TBase > { public: enum { IDD = 0 }; // no dialog template resource TDlgTemplate m_Template; void CreateTemplate() { T* pT = static_cast(this); pT->DoInitTemplate(); pT->DoInitControls(); } INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow(), LPARAM dwInitParam = NULL) { T* pT = static_cast(this); ATLASSERT(pT->m_hWnd == NULL); if(!m_Template.IsValid()) CreateTemplate(); #if (_ATL_VER >= 0x0800) // Allocate the thunk structure here, where we can fail gracefully. BOOL bRet = m_thunk.Init(NULL, NULL); if(bRet == FALSE) { ::SetLastError(ERROR_OUTOFMEMORY); return -1; } #endif // (_ATL_VER >= 0x0800) ModuleHelper::AddCreateWndData(&m_thunk.cd, (ATL::CDialogImplBaseT< TBase >*)pT); #ifdef _DEBUG m_bModal = true; #endif // _DEBUG return ::DialogBoxIndirectParam(ModuleHelper::GetResourceInstance(), m_Template.GetTemplatePtr(), hWndParent, (DLGPROC)T::StartDialogProc, dwInitParam); } HWND Create(HWND hWndParent, LPARAM dwInitParam = NULL) { T* pT = static_cast(this); ATLASSERT(pT->m_hWnd == NULL); if(!m_Template.IsValid()) CreateTemplate(); #if (_ATL_VER >= 0x0800) // Allocate the thunk structure here, where we can fail gracefully. BOOL bRet = m_thunk.Init(NULL, NULL); if(bRet == FALSE) { ::SetLastError(ERROR_OUTOFMEMORY); return NULL; } #endif // (_ATL_VER >= 0x0800) ModuleHelper::AddCreateWndData(&m_thunk.cd, (ATL::CDialogImplBaseT< TBase >*)pT); #ifdef _DEBUG m_bModal = false; #endif // _DEBUG HWND hWnd = ::CreateDialogIndirectParam(ModuleHelper::GetResourceInstance(), (LPCDLGTEMPLATE)m_Template.GetTemplatePtr(), hWndParent, (DLGPROC)T::StartDialogProc, dwInitParam); ATLASSERT(m_hWnd == hWnd); return hWnd; } // for CComControl HWND Create(HWND hWndParent, RECT&, LPARAM dwInitParam = NULL) { return Create(hWndParent, dwInitParam); } void DoInitTemplate() { ATLASSERT(FALSE); // MUST be defined in derived class } void DoInitControls() { ATLASSERT(FALSE); // MUST be defined in derived class } }; /////////////////////////////////////////////////////////////////////////////// // CPropertySheetWindow - client side for a property sheet class CPropertySheetWindow : public ATL::CWindow { public: // Constructors CPropertySheetWindow(HWND hWnd = NULL) : ATL::CWindow(hWnd) { } CPropertySheetWindow& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } // Attributes int GetPageCount() const { ATLASSERT(::IsWindow(m_hWnd)); HWND hWndTabCtrl = GetTabControl(); ATLASSERT(hWndTabCtrl != NULL); return (int)::SendMessage(hWndTabCtrl, TCM_GETITEMCOUNT, 0, 0L); } HWND GetActivePage() const { ATLASSERT(::IsWindow(m_hWnd)); return (HWND)::SendMessage(m_hWnd, PSM_GETCURRENTPAGEHWND, 0, 0L); } int GetActiveIndex() const { ATLASSERT(::IsWindow(m_hWnd)); HWND hWndTabCtrl = GetTabControl(); ATLASSERT(hWndTabCtrl != NULL); return (int)::SendMessage(hWndTabCtrl, TCM_GETCURSEL, 0, 0L); } BOOL SetActivePage(int nPageIndex) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, PSM_SETCURSEL, nPageIndex, 0L); } BOOL SetActivePage(HPROPSHEETPAGE hPage) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hPage != NULL); return (BOOL)::SendMessage(m_hWnd, PSM_SETCURSEL, 0, (LPARAM)hPage); } BOOL SetActivePageByID(int nPageID) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, PSM_SETCURSELID, 0, nPageID); } void SetTitle(LPCTSTR lpszText, UINT nStyle = 0) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT((nStyle & ~PSH_PROPTITLE) == 0); // only PSH_PROPTITLE is valid ATLASSERT(lpszText != NULL); ::SendMessage(m_hWnd, PSM_SETTITLE, nStyle, (LPARAM)lpszText); } HWND GetTabControl() const { ATLASSERT(::IsWindow(m_hWnd)); return (HWND)::SendMessage(m_hWnd, PSM_GETTABCONTROL, 0, 0L); } void SetFinishText(LPCTSTR lpszText) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, PSM_SETFINISHTEXT, 0, (LPARAM)lpszText); } void SetWizardButtons(DWORD dwFlags) { ATLASSERT(::IsWindow(m_hWnd)); ::PostMessage(m_hWnd, PSM_SETWIZBUTTONS, 0, dwFlags); } // Operations BOOL AddPage(HPROPSHEETPAGE hPage) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hPage != NULL); return (BOOL)::SendMessage(m_hWnd, PSM_ADDPAGE, 0, (LPARAM)hPage); } BOOL AddPage(LPCPROPSHEETPAGE pPage) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pPage != NULL); HPROPSHEETPAGE hPage = ::CreatePropertySheetPage(pPage); if(hPage == NULL) return FALSE; return (BOOL)::SendMessage(m_hWnd, PSM_ADDPAGE, 0, (LPARAM)hPage); } #ifndef _WIN32_WCE BOOL InsertPage(int nNewPageIndex, HPROPSHEETPAGE hPage) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hPage != NULL); return (BOOL)::SendMessage(m_hWnd, PSM_INSERTPAGE, nNewPageIndex, (LPARAM)hPage); } BOOL InsertPage(int nNewPageIndex, LPCPROPSHEETPAGE pPage) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pPage != NULL); HPROPSHEETPAGE hPage = ::CreatePropertySheetPage(pPage); if(hPage == NULL) return FALSE; return (BOOL)::SendMessage(m_hWnd, PSM_INSERTPAGE, nNewPageIndex, (LPARAM)hPage); } BOOL InsertPage(HPROPSHEETPAGE hPageInsertAfter, HPROPSHEETPAGE hPage) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hPage != NULL); return (BOOL)::SendMessage(m_hWnd, PSM_INSERTPAGE, (WPARAM)hPageInsertAfter, (LPARAM)hPage); } BOOL InsertPage(HPROPSHEETPAGE hPageInsertAfter, LPCPROPSHEETPAGE pPage) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pPage != NULL); HPROPSHEETPAGE hPage = ::CreatePropertySheetPage(pPage); if(hPage == NULL) return FALSE; return (BOOL)::SendMessage(m_hWnd, PSM_INSERTPAGE, (WPARAM)hPageInsertAfter, (LPARAM)hPage); } #endif // !_WIN32_WCE void RemovePage(int nPageIndex) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, PSM_REMOVEPAGE, nPageIndex, 0L); } void RemovePage(HPROPSHEETPAGE hPage) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(hPage != NULL); ::SendMessage(m_hWnd, PSM_REMOVEPAGE, 0, (LPARAM)hPage); } BOOL PressButton(int nButton) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, PSM_PRESSBUTTON, nButton, 0L); } BOOL Apply() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, PSM_APPLY, 0, 0L); } void CancelToClose() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, PSM_CANCELTOCLOSE, 0, 0L); } void SetModified(HWND hWndPage, BOOL bChanged = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(::IsWindow(hWndPage)); UINT uMsg = bChanged ? PSM_CHANGED : PSM_UNCHANGED; ::SendMessage(m_hWnd, uMsg, (WPARAM)hWndPage, 0L); } LRESULT QuerySiblings(WPARAM wParam, LPARAM lParam) { ATLASSERT(::IsWindow(m_hWnd)); return ::SendMessage(m_hWnd, PSM_QUERYSIBLINGS, wParam, lParam); } void RebootSystem() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, PSM_REBOOTSYSTEM, 0, 0L); } void RestartWindows() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, PSM_RESTARTWINDOWS, 0, 0L); } BOOL IsDialogMessage(LPMSG lpMsg) { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, PSM_ISDIALOGMESSAGE, 0, (LPARAM)lpMsg); } #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) int HwndToIndex(HWND hWnd) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PSM_HWNDTOINDEX, (WPARAM)hWnd, 0L); } HWND IndexToHwnd(int nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (HWND)::SendMessage(m_hWnd, PSM_INDEXTOHWND, nIndex, 0L); } int PageToIndex(HPROPSHEETPAGE hPage) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PSM_PAGETOINDEX, 0, (LPARAM)hPage); } HPROPSHEETPAGE IndexToPage(int nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (HPROPSHEETPAGE)::SendMessage(m_hWnd, PSM_INDEXTOPAGE, nIndex, 0L); } int IdToIndex(int nID) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PSM_IDTOINDEX, 0, nID); } int IndexToId(int nIndex) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PSM_INDEXTOID, nIndex, 0L); } int GetResult() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, PSM_GETRESULT, 0, 0L); } BOOL RecalcPageSizes() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, PSM_RECALCPAGESIZES, 0, 0L); } void SetHeaderTitle(int nIndex, LPCTSTR lpstrHeaderTitle) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, PSM_SETHEADERTITLE, nIndex, (LPARAM)lpstrHeaderTitle); } void SetHeaderSubTitle(int nIndex, LPCTSTR lpstrHeaderSubTitle) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, PSM_SETHEADERSUBTITLE, nIndex, (LPARAM)lpstrHeaderSubTitle); } #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) // Implementation - override to prevent usage HWND Create(LPCTSTR, HWND, ATL::_U_RECT = NULL, LPCTSTR = NULL, DWORD = 0, DWORD = 0, ATL::_U_MENUorID = 0U, LPVOID = NULL) { ATLASSERT(FALSE); return NULL; } }; /////////////////////////////////////////////////////////////////////////////// // CPropertySheetImpl - implements a property sheet template class ATL_NO_VTABLE CPropertySheetImpl : public ATL::CWindowImplBaseT< TBase > { public: PROPSHEETHEADER m_psh; ATL::CSimpleArray m_arrPages; #if defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) // PPC specific #ifndef PROPSHEET_LINK_SIZE #define PROPSHEET_LINK_SIZE 128 #endif // PROPSHEET_LINK_SIZE TCHAR m_szLink[PROPSHEET_LINK_SIZE]; static LPCTSTR m_pszTitle; static LPCTSTR m_pszLink; #endif // defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) // Construction/Destruction CPropertySheetImpl(ATL::_U_STRINGorID title = (LPCTSTR)NULL, UINT uStartPage = 0, HWND hWndParent = NULL) { memset(&m_psh, 0, sizeof(PROPSHEETHEADER)); m_psh.dwSize = sizeof(PROPSHEETHEADER); m_psh.dwFlags = PSH_USECALLBACK; m_psh.hInstance = ModuleHelper::GetResourceInstance(); m_psh.phpage = NULL; // will be set later m_psh.nPages = 0; // will be set later m_psh.pszCaption = title.m_lpstr; m_psh.nStartPage = uStartPage; m_psh.hwndParent = hWndParent; // if NULL, will be set in DoModal/Create m_psh.pfnCallback = T::PropSheetCallback; #if defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) // PPC specific m_psh.dwFlags |= PSH_MAXIMIZE; m_szLink[0] = 0; #endif // defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) } ~CPropertySheetImpl() { if(m_arrPages.GetSize() > 0) // sheet never created, destroy all pages { for(int i = 0; i < m_arrPages.GetSize(); i++) ::DestroyPropertySheetPage((HPROPSHEETPAGE)m_arrPages[i]); } } // Callback function and overrideables static int CALLBACK PropSheetCallback(HWND hWnd, UINT uMsg, LPARAM lParam) { lParam; // avoid level 4 warning int nRet = 0; if(uMsg == PSCB_INITIALIZED) { ATLASSERT(hWnd != NULL); T* pT = (T*)ModuleHelper::ExtractCreateWndData(); // subclass the sheet window pT->SubclassWindow(hWnd); // remove page handles array pT->_CleanUpPages(); #if defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) // PPC specific m_pszTitle = pT->m_psh.pszCaption; if(*pT->m_szLink != 0) m_pszLink = pT->m_szLink; #endif // defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) // PPC specific pT->OnSheetInitialized(); } #if defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) // PPC specific uMsg else { switch(uMsg) { case PSCB_GETVERSION : nRet = COMCTL32_VERSION; break; case PSCB_GETTITLE : if(m_pszTitle != NULL) { lstrcpy((LPTSTR)lParam, m_pszTitle); m_pszTitle = NULL; } break; case PSCB_GETLINKTEXT: if(m_pszLink != NULL) { lstrcpy((LPTSTR)lParam, m_pszLink); m_pszLink = NULL; } break; default: break; } } #endif // defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) return nRet; } void OnSheetInitialized() { } // Create method HWND Create(HWND hWndParent = NULL) { ATLASSERT(m_hWnd == NULL); m_psh.dwFlags |= PSH_MODELESS; if(m_psh.hwndParent == NULL) m_psh.hwndParent = hWndParent; m_psh.phpage = (HPROPSHEETPAGE*)m_arrPages.GetData(); m_psh.nPages = m_arrPages.GetSize(); T* pT = static_cast(this); #if (_ATL_VER >= 0x0800) // Allocate the thunk structure here, where we can fail gracefully. BOOL bRet = pT->m_thunk.Init(NULL, NULL); if(bRet == FALSE) { ::SetLastError(ERROR_OUTOFMEMORY); return NULL; } #endif // (_ATL_VER >= 0x0800) ModuleHelper::AddCreateWndData(&pT->m_thunk.cd, pT); HWND hWnd = (HWND)::PropertySheet(&m_psh); _CleanUpPages(); // ensure clean-up, required if call failed ATLASSERT(m_hWnd == hWnd); return hWnd; } INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow()) { ATLASSERT(m_hWnd == NULL); m_psh.dwFlags &= ~PSH_MODELESS; if(m_psh.hwndParent == NULL) m_psh.hwndParent = hWndParent; m_psh.phpage = (HPROPSHEETPAGE*)m_arrPages.GetData(); m_psh.nPages = m_arrPages.GetSize(); T* pT = static_cast(this); #if (_ATL_VER >= 0x0800) // Allocate the thunk structure here, where we can fail gracefully. BOOL bRet = pT->m_thunk.Init(NULL, NULL); if(bRet == FALSE) { ::SetLastError(ERROR_OUTOFMEMORY); return -1; } #endif // (_ATL_VER >= 0x0800) ModuleHelper::AddCreateWndData(&pT->m_thunk.cd, pT); INT_PTR nRet = ::PropertySheet(&m_psh); _CleanUpPages(); // ensure clean-up, required if call failed return nRet; } // implementation helper - clean up pages array void _CleanUpPages() { m_psh.nPages = 0; m_psh.phpage = NULL; m_arrPages.RemoveAll(); } // Attributes (extended overrides of client class methods) // These now can be called before the sheet is created // Note: Calling these after the sheet is created gives unpredictable results int GetPageCount() const { if(m_hWnd == NULL) // not created yet return m_arrPages.GetSize(); return TBase::GetPageCount(); } int GetActiveIndex() const { if(m_hWnd == NULL) // not created yet return m_psh.nStartPage; return TBase::GetActiveIndex(); } HPROPSHEETPAGE GetPage(int nPageIndex) const { ATLASSERT(m_hWnd == NULL); // can't do this after it's created return (HPROPSHEETPAGE)m_arrPages[nPageIndex]; } int GetPageIndex(HPROPSHEETPAGE hPage) const { ATLASSERT(m_hWnd == NULL); // can't do this after it's created return m_arrPages.Find((HPROPSHEETPAGE&)hPage); } BOOL SetActivePage(int nPageIndex) { if(m_hWnd == NULL) // not created yet { ATLASSERT(nPageIndex >= 0 && nPageIndex < m_arrPages.GetSize()); m_psh.nStartPage = nPageIndex; return TRUE; } return TBase::SetActivePage(nPageIndex); } BOOL SetActivePage(HPROPSHEETPAGE hPage) { ATLASSERT(hPage != NULL); if (m_hWnd == NULL) // not created yet { int nPageIndex = GetPageIndex(hPage); if(nPageIndex == -1) return FALSE; return SetActivePage(nPageIndex); } return TBase::SetActivePage(hPage); } void SetTitle(LPCTSTR lpszText, UINT nStyle = 0) { ATLASSERT((nStyle & ~PSH_PROPTITLE) == 0); // only PSH_PROPTITLE is valid ATLASSERT(lpszText != NULL); if(m_hWnd == NULL) { // set internal state m_psh.pszCaption = lpszText; // must exist until sheet is created m_psh.dwFlags &= ~PSH_PROPTITLE; m_psh.dwFlags |= nStyle; } else { // set external state TBase::SetTitle(lpszText, nStyle); } } #if defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) // PPC specific Link field void SetLinkText(LPCTSTR lpszText) { ATLASSERT(lpszText != NULL); ATLASSERT(lstrlen(lpszText) < PROPSHEET_LINK_SIZE); lstrcpy(m_szLink, lpszText); } #endif // defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) void SetWizardMode() { m_psh.dwFlags |= PSH_WIZARD; } void EnableHelp() { m_psh.dwFlags |= PSH_HASHELP; } // Operations BOOL AddPage(HPROPSHEETPAGE hPage) { ATLASSERT(hPage != NULL); BOOL bRet = FALSE; if(m_hWnd != NULL) bRet = TBase::AddPage(hPage); else // sheet not created yet, use internal data bRet = m_arrPages.Add((HPROPSHEETPAGE&)hPage); return bRet; } BOOL AddPage(LPCPROPSHEETPAGE pPage) { ATLASSERT(pPage != NULL); HPROPSHEETPAGE hPage = ::CreatePropertySheetPage(pPage); if(hPage == NULL) return FALSE; BOOL bRet = AddPage(hPage); if(!bRet) ::DestroyPropertySheetPage(hPage); return bRet; } BOOL RemovePage(HPROPSHEETPAGE hPage) { ATLASSERT(hPage != NULL); if (m_hWnd == NULL) // not created yet { int nPage = GetPageIndex(hPage); if(nPage == -1) return FALSE; return RemovePage(nPage); } TBase::RemovePage(hPage); return TRUE; } BOOL RemovePage(int nPageIndex) { BOOL bRet = TRUE; if(m_hWnd != NULL) TBase::RemovePage(nPageIndex); else // sheet not created yet, use internal data bRet = m_arrPages.RemoveAt(nPageIndex); return bRet; } #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) void SetHeader(LPCTSTR szbmHeader) { ATLASSERT(m_hWnd == NULL); // can't do this after it's created m_psh.dwFlags &= ~PSH_WIZARD; m_psh.dwFlags |= (PSH_HEADER | PSH_WIZARD97); m_psh.pszbmHeader = szbmHeader; } void SetHeader(HBITMAP hbmHeader) { ATLASSERT(m_hWnd == NULL); // can't do this after it's created m_psh.dwFlags &= ~PSH_WIZARD; m_psh.dwFlags |= (PSH_HEADER | PSH_USEHBMHEADER | PSH_WIZARD97); m_psh.hbmHeader = hbmHeader; } void SetWatermark(LPCTSTR szbmWatermark, HPALETTE hplWatermark = NULL) { ATLASSERT(m_hWnd == NULL); // can't do this after it's created m_psh.dwFlags &= ~PSH_WIZARD; m_psh.dwFlags |= PSH_WATERMARK | PSH_WIZARD97; m_psh.pszbmWatermark = szbmWatermark; if (hplWatermark != NULL) { m_psh.dwFlags |= PSH_USEHPLWATERMARK; m_psh.hplWatermark = hplWatermark; } } void SetWatermark(HBITMAP hbmWatermark, HPALETTE hplWatermark = NULL) { ATLASSERT(m_hWnd == NULL); // can't do this after it's created m_psh.dwFlags &= ~PSH_WIZARD; m_psh.dwFlags |= (PSH_WATERMARK | PSH_USEHBMWATERMARK | PSH_WIZARD97); m_psh.hbmWatermark = hbmWatermark; if (hplWatermark != NULL) { m_psh.dwFlags |= PSH_USEHPLWATERMARK; m_psh.hplWatermark = hplWatermark; } } void StretchWatermark(bool bStretchWatermark) { ATLASSERT(m_hWnd == NULL); // can't do this after it's created if (bStretchWatermark) m_psh.dwFlags |= PSH_STRETCHWATERMARK; else m_psh.dwFlags &= ~PSH_STRETCHWATERMARK; } #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) // Message map and handlers BEGIN_MSG_MAP(CPropertySheetImpl) MESSAGE_HANDLER(WM_COMMAND, OnCommand) MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand) END_MSG_MAP() LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { LRESULT lRet = DefWindowProc(uMsg, wParam, lParam); if(HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) && ((m_psh.dwFlags & PSH_MODELESS) != 0) && (GetActivePage() == NULL)) DestroyWindow(); return lRet; } LRESULT OnSysCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { if(((m_psh.dwFlags & PSH_MODELESS) == PSH_MODELESS) && ((wParam & 0xFFF0) == SC_CLOSE)) SendMessage(WM_CLOSE); else bHandled = FALSE; return 0; } }; #if defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) // PPC static pointers template < class T, class TBase > LPCWSTR CPropertySheetImpl::m_pszTitle = NULL; template < class T, class TBase> LPCWSTR CPropertySheetImpl::m_pszLink = NULL; #endif // defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) // for non-customized sheets class CPropertySheet : public CPropertySheetImpl { public: CPropertySheet(ATL::_U_STRINGorID title = (LPCTSTR)NULL, UINT uStartPage = 0, HWND hWndParent = NULL) : CPropertySheetImpl(title, uStartPage, hWndParent) { } }; /////////////////////////////////////////////////////////////////////////////// // CPropertyPageWindow - client side for a property page class CPropertyPageWindow : public ATL::CWindow { public: // Constructors CPropertyPageWindow(HWND hWnd = NULL) : ATL::CWindow(hWnd) { } CPropertyPageWindow& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } // Attributes CPropertySheetWindow GetPropertySheet() const { ATLASSERT(::IsWindow(m_hWnd)); return CPropertySheetWindow(GetParent()); } // Operations BOOL Apply() { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetParent() != NULL); return GetPropertySheet().Apply(); } void CancelToClose() { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetParent() != NULL); GetPropertySheet().CancelToClose(); } void SetModified(BOOL bChanged = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetParent() != NULL); GetPropertySheet().SetModified(m_hWnd, bChanged); } LRESULT QuerySiblings(WPARAM wParam, LPARAM lParam) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetParent() != NULL); return GetPropertySheet().QuerySiblings(wParam, lParam); } void RebootSystem() { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetParent() != NULL); GetPropertySheet().RebootSystem(); } void RestartWindows() { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetParent() != NULL); GetPropertySheet().RestartWindows(); } void SetWizardButtons(DWORD dwFlags) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetParent() != NULL); GetPropertySheet().SetWizardButtons(dwFlags); } // Implementation - overrides to prevent usage HWND Create(LPCTSTR, HWND, ATL::_U_RECT = NULL, LPCTSTR = NULL, DWORD = 0, DWORD = 0, ATL::_U_MENUorID = 0U, LPVOID = NULL) { ATLASSERT(FALSE); return NULL; } }; /////////////////////////////////////////////////////////////////////////////// // CPropertyPageImpl - implements a property page template class ATL_NO_VTABLE CPropertyPageImpl : public ATL::CDialogImplBaseT< TBase > { public: PROPSHEETPAGE m_psp; operator PROPSHEETPAGE*() { return &m_psp; } // Construction CPropertyPageImpl(ATL::_U_STRINGorID title = (LPCTSTR)NULL) { // initialize PROPSHEETPAGE struct memset(&m_psp, 0, sizeof(PROPSHEETPAGE)); m_psp.dwSize = sizeof(PROPSHEETPAGE); m_psp.dwFlags = PSP_USECALLBACK; m_psp.hInstance = ModuleHelper::GetResourceInstance(); T* pT = static_cast(this); m_psp.pszTemplate = MAKEINTRESOURCE(pT->IDD); m_psp.pfnDlgProc = (DLGPROC)T::StartDialogProc; m_psp.pfnCallback = T::PropPageCallback; m_psp.lParam = (LPARAM)pT; if(title.m_lpstr != NULL) SetTitle(title); } // Callback function and overrideables static UINT CALLBACK PropPageCallback(HWND hWnd, UINT uMsg, LPPROPSHEETPAGE ppsp) { hWnd; // avoid level 4 warning ATLASSERT(hWnd == NULL); T* pT = (T*)ppsp->lParam; UINT uRet = 0; switch(uMsg) { case PSPCB_CREATE: { ATL::CDialogImplBaseT< TBase >* pPage = (ATL::CDialogImplBaseT< TBase >*)pT; ModuleHelper::AddCreateWndData(&pPage->m_thunk.cd, pPage); uRet = pT->OnPageCreate() ? 1 : 0; } break; #if (_WIN32_IE >= 0x0500) case PSPCB_ADDREF: pT->OnPageAddRef(); break; #endif // (_WIN32_IE >= 0x0500) case PSPCB_RELEASE: pT->OnPageRelease(); break; default: break; } return uRet; } bool OnPageCreate() { return true; // true - allow page to be created, false - prevent creation } #if (_WIN32_IE >= 0x0500) void OnPageAddRef() { } #endif // (_WIN32_IE >= 0x0500) void OnPageRelease() { } // Create method HPROPSHEETPAGE Create() { return ::CreatePropertySheetPage(&m_psp); } // Attributes void SetTitle(ATL::_U_STRINGorID title) { m_psp.pszTitle = title.m_lpstr; m_psp.dwFlags |= PSP_USETITLE; } #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) void SetHeaderTitle(LPCTSTR lpstrHeaderTitle) { ATLASSERT(m_hWnd == NULL); // can't do this after it's created m_psp.dwFlags |= PSP_USEHEADERTITLE; m_psp.pszHeaderTitle = lpstrHeaderTitle; } void SetHeaderSubTitle(LPCTSTR lpstrHeaderSubTitle) { ATLASSERT(m_hWnd == NULL); // can't do this after it's created m_psp.dwFlags |= PSP_USEHEADERSUBTITLE; m_psp.pszHeaderSubTitle = lpstrHeaderSubTitle; } #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) // Operations void EnableHelp() { m_psp.dwFlags |= PSP_HASHELP; } // Message map and handlers BEGIN_MSG_MAP(CPropertyPageImpl) MESSAGE_HANDLER(WM_NOTIFY, OnNotify) END_MSG_MAP() // NOTE: Define _WTL_NEW_PAGE_NOTIFY_HANDLERS to use new notification // handlers that return direct values without any restrictions LRESULT OnNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { #ifndef _WIN32_WCE // This notification is sometimes received on Windows CE after the window is already destroyed ATLASSERT(::IsWindow(m_hWnd)); #endif NMHDR* pNMHDR = (NMHDR*)lParam; // don't handle messages not from the page/sheet itself if(pNMHDR->hwndFrom != m_hWnd && pNMHDR->hwndFrom != ::GetParent(m_hWnd)) { bHandled = FALSE; return 1; } #ifdef _WIN32_WCE ATLASSERT(::IsWindow(m_hWnd)); #endif T* pT = static_cast(this); LRESULT lResult = 0; switch(pNMHDR->code) { #ifdef _WTL_NEW_PAGE_NOTIFY_HANDLERS case PSN_SETACTIVE: lResult = pT->OnSetActive(); break; case PSN_KILLACTIVE: lResult = pT->OnKillActive(); break; case PSN_APPLY: lResult = pT->OnApply(); break; case PSN_RESET: pT->OnReset(); break; case PSN_QUERYCANCEL: lResult = pT->OnQueryCancel(); break; case PSN_WIZNEXT: lResult = pT->OnWizardNext(); break; case PSN_WIZBACK: lResult = pT->OnWizardBack(); break; case PSN_WIZFINISH: lResult = pT->OnWizardFinish(); break; case PSN_HELP: pT->OnHelp(); break; #ifndef _WIN32_WCE #if (_WIN32_IE >= 0x0400) case PSN_GETOBJECT: if(!pT->OnGetObject((LPNMOBJECTNOTIFY)lParam)) bHandled = FALSE; break; #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) case PSN_TRANSLATEACCELERATOR: { LPPSHNOTIFY lpPSHNotify = (LPPSHNOTIFY)lParam; lResult = pT->OnTranslateAccelerator((LPMSG)lpPSHNotify->lParam); } break; case PSN_QUERYINITIALFOCUS: { LPPSHNOTIFY lpPSHNotify = (LPPSHNOTIFY)lParam; lResult = (LRESULT)pT->OnQueryInitialFocus((HWND)lpPSHNotify->lParam); } break; #endif // (_WIN32_IE >= 0x0500) #endif // !_WIN32_WCE #else // !_WTL_NEW_PAGE_NOTIFY_HANDLERS case PSN_SETACTIVE: lResult = pT->OnSetActive() ? 0 : -1; break; case PSN_KILLACTIVE: lResult = !pT->OnKillActive(); break; case PSN_APPLY: lResult = pT->OnApply() ? PSNRET_NOERROR : PSNRET_INVALID_NOCHANGEPAGE; break; case PSN_RESET: pT->OnReset(); break; case PSN_QUERYCANCEL: lResult = !pT->OnQueryCancel(); break; case PSN_WIZNEXT: lResult = pT->OnWizardNext(); break; case PSN_WIZBACK: lResult = pT->OnWizardBack(); break; case PSN_WIZFINISH: lResult = !pT->OnWizardFinish(); break; case PSN_HELP: pT->OnHelp(); break; #ifndef _WIN32_WCE #if (_WIN32_IE >= 0x0400) case PSN_GETOBJECT: if(!pT->OnGetObject((LPNMOBJECTNOTIFY)lParam)) bHandled = FALSE; break; #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) case PSN_TRANSLATEACCELERATOR: { LPPSHNOTIFY lpPSHNotify = (LPPSHNOTIFY)lParam; lResult = pT->OnTranslateAccelerator((LPMSG)lpPSHNotify->lParam) ? PSNRET_MESSAGEHANDLED : PSNRET_NOERROR; } break; case PSN_QUERYINITIALFOCUS: { LPPSHNOTIFY lpPSHNotify = (LPPSHNOTIFY)lParam; lResult = (LRESULT)pT->OnQueryInitialFocus((HWND)lpPSHNotify->lParam); } break; #endif // (_WIN32_IE >= 0x0500) #endif // !_WIN32_WCE #endif // !_WTL_NEW_PAGE_NOTIFY_HANDLERS default: bHandled = FALSE; // not handled } return lResult; } // Overridables // NOTE: Define _WTL_NEW_PAGE_NOTIFY_HANDLERS to use new notification // handlers that return direct values without any restrictions #ifdef _WTL_NEW_PAGE_NOTIFY_HANDLERS int OnSetActive() { // 0 = allow activate // -1 = go back that was active // page ID = jump to page return 0; } BOOL OnKillActive() { // FALSE = allow deactivate // TRUE = prevent deactivation return FALSE; } int OnApply() { // PSNRET_NOERROR = apply OK // PSNRET_INVALID = apply not OK, return to this page // PSNRET_INVALID_NOCHANGEPAGE = apply not OK, don't change focus return PSNRET_NOERROR; } void OnReset() { } BOOL OnQueryCancel() { // FALSE = allow cancel // TRUE = prevent cancel return FALSE; } int OnWizardBack() { // 0 = goto previous page // -1 = prevent page change // >0 = jump to page by dlg ID return 0; } int OnWizardNext() { // 0 = goto next page // -1 = prevent page change // >0 = jump to page by dlg ID return 0; } INT_PTR OnWizardFinish() { // FALSE = allow finish // TRUE = prevent finish // HWND = prevent finish and set focus to HWND (CommCtrl 5.80 only) return FALSE; } void OnHelp() { } #ifndef _WIN32_WCE #if (_WIN32_IE >= 0x0400) BOOL OnGetObject(LPNMOBJECTNOTIFY /*lpObjectNotify*/) { return FALSE; // not processed } #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) int OnTranslateAccelerator(LPMSG /*lpMsg*/) { // PSNRET_NOERROR - message not handled // PSNRET_MESSAGEHANDLED - message handled return PSNRET_NOERROR; } HWND OnQueryInitialFocus(HWND /*hWndFocus*/) { // NULL = set focus to default control // HWND = set focus to HWND return NULL; } #endif // (_WIN32_IE >= 0x0500) #endif // !_WIN32_WCE #else // !_WTL_NEW_PAGE_NOTIFY_HANDLERS BOOL OnSetActive() { return TRUE; } BOOL OnKillActive() { return TRUE; } BOOL OnApply() { return TRUE; } void OnReset() { } BOOL OnQueryCancel() { return TRUE; // ok to cancel } int OnWizardBack() { // 0 = goto previous page // -1 = prevent page change // >0 = jump to page by dlg ID return 0; } int OnWizardNext() { // 0 = goto next page // -1 = prevent page change // >0 = jump to page by dlg ID return 0; } BOOL OnWizardFinish() { return TRUE; } void OnHelp() { } #ifndef _WIN32_WCE #if (_WIN32_IE >= 0x0400) BOOL OnGetObject(LPNMOBJECTNOTIFY /*lpObjectNotify*/) { return FALSE; // not processed } #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) BOOL OnTranslateAccelerator(LPMSG /*lpMsg*/) { return FALSE; // not translated } HWND OnQueryInitialFocus(HWND /*hWndFocus*/) { return NULL; // default } #endif // (_WIN32_IE >= 0x0500) #endif // !_WIN32_WCE #endif // !_WTL_NEW_PAGE_NOTIFY_HANDLERS }; // for non-customized pages template class CPropertyPage : public CPropertyPageImpl > { public: enum { IDD = t_wDlgTemplateID }; CPropertyPage(ATL::_U_STRINGorID title = (LPCTSTR)NULL) : CPropertyPageImpl(title) { } DECLARE_EMPTY_MSG_MAP() }; /////////////////////////////////////////////////////////////////////////////// // CAxPropertyPageImpl - property page that hosts ActiveX controls #ifndef _ATL_NO_HOSTING // Note: You must #include to use these classes template class ATL_NO_VTABLE CAxPropertyPageImpl : public CPropertyPageImpl< T, TBase > { public: // Data members HGLOBAL m_hInitData; HGLOBAL m_hDlgRes; HGLOBAL m_hDlgResSplit; // Constructor/destructor CAxPropertyPageImpl(ATL::_U_STRINGorID title = (LPCTSTR)NULL) : CPropertyPageImpl< T, TBase >(title), m_hInitData(NULL), m_hDlgRes(NULL), m_hDlgResSplit(NULL) { T* pT = static_cast(this); pT; // avoid level 4 warning // initialize ActiveX hosting and modify dialog template ATL::AtlAxWinInit(); HINSTANCE hInstance = ModuleHelper::GetResourceInstance(); LPCTSTR lpTemplateName = MAKEINTRESOURCE(pT->IDD); HRSRC hDlg = ::FindResource(hInstance, lpTemplateName, (LPTSTR)RT_DIALOG); if(hDlg != NULL) { HRSRC hDlgInit = ::FindResource(hInstance, lpTemplateName, (LPTSTR)_ATL_RT_DLGINIT); BYTE* pInitData = NULL; if(hDlgInit != NULL) { m_hInitData = ::LoadResource(hInstance, hDlgInit); pInitData = (BYTE*)::LockResource(m_hInitData); } m_hDlgRes = ::LoadResource(hInstance, hDlg); DLGTEMPLATE* pDlg = (DLGTEMPLATE*)::LockResource(m_hDlgRes); LPCDLGTEMPLATE lpDialogTemplate = ATL::_DialogSplitHelper::SplitDialogTemplate(pDlg, pInitData); if(lpDialogTemplate != pDlg) m_hDlgResSplit = GlobalHandle(lpDialogTemplate); // set up property page to use in-memory dialog template if(lpDialogTemplate != NULL) { m_psp.dwFlags |= PSP_DLGINDIRECT; m_psp.pResource = lpDialogTemplate; } else { ATLASSERT(FALSE && _T("CAxPropertyPageImpl - ActiveX initializtion failed!")); } } else { ATLASSERT(FALSE && _T("CAxPropertyPageImpl - Cannot find dialog template!")); } } ~CAxPropertyPageImpl() { if(m_hInitData != NULL) { UnlockResource(m_hInitData); FreeResource(m_hInitData); } if(m_hDlgRes != NULL) { UnlockResource(m_hDlgRes); FreeResource(m_hDlgRes); } if(m_hDlgResSplit != NULL) { ::GlobalFree(m_hDlgResSplit); } } // Methods // call this one to handle keyboard message for ActiveX controls BOOL PreTranslateMessage(LPMSG pMsg) { if ((pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST) && (pMsg->message < WM_MOUSEFIRST || pMsg->message > WM_MOUSELAST)) return FALSE; // find a direct child of the dialog from the window that has focus HWND hWndCtl = ::GetFocus(); if (IsChild(hWndCtl) && ::GetParent(hWndCtl) != m_hWnd) { do { hWndCtl = ::GetParent(hWndCtl); } while (::GetParent(hWndCtl) != m_hWnd); } // give controls a chance to translate this message return (BOOL)::SendMessage(hWndCtl, WM_FORWARDMSG, 0, (LPARAM)pMsg); } // Overridables #if (_WIN32_IE >= 0x0500) // new default implementation for ActiveX hosting pages #ifdef _WTL_NEW_PAGE_NOTIFY_HANDLERS int OnTranslateAccelerator(LPMSG lpMsg) { T* pT = static_cast(this); return (pT->PreTranslateMessage(lpMsg) != FALSE) ? PSNRET_MESSAGEHANDLED : PSNRET_NOERROR; } #else // !_WTL_NEW_PAGE_NOTIFY_HANDLERS BOOL OnTranslateAccelerator(LPMSG lpMsg) { T* pT = static_cast(this); return pT->PreTranslateMessage(lpMsg); } #endif // !_WTL_NEW_PAGE_NOTIFY_HANDLERS #endif // (_WIN32_IE >= 0x0500) // Support for new stuff in ATL7 #if (_ATL_VER >= 0x0700) int GetIDD() { return( static_cast(this)->IDD ); } virtual DLGPROC GetDialogProc() { return DialogProc; } static INT_PTR CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CAxPropertyPageImpl< T, TBase >* pThis = (CAxPropertyPageImpl< T, TBase >*)hWnd; if (uMsg == WM_INITDIALOG) { HRESULT hr; if (FAILED(hr = pThis->CreateActiveXControls(pThis->GetIDD()))) { ATLASSERT(FALSE); return FALSE; } } return CPropertyPageImpl< T, TBase >::DialogProc(hWnd, uMsg, wParam, lParam); } // ActiveX controls creation virtual HRESULT CreateActiveXControls(UINT nID) { // Load dialog template and InitData HRSRC hDlgInit = ::FindResource(ATL::_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCE(nID), (LPTSTR)_ATL_RT_DLGINIT); BYTE* pInitData = NULL; HGLOBAL hData = NULL; HRESULT hr = S_OK; if (hDlgInit != NULL) { hData = ::LoadResource(ATL::_AtlBaseModule.GetResourceInstance(), hDlgInit); if (hData != NULL) pInitData = (BYTE*) ::LockResource(hData); } HRSRC hDlg = ::FindResource(ATL::_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCE(nID), (LPTSTR)RT_DIALOG); if (hDlg != NULL) { HGLOBAL hResource = ::LoadResource(ATL::_AtlBaseModule.GetResourceInstance(), hDlg); DLGTEMPLATE* pDlg = NULL; if (hResource != NULL) { pDlg = (DLGTEMPLATE*) ::LockResource(hResource); if (pDlg != NULL) { // Get first control on the template BOOL bDialogEx = ATL::_DialogSplitHelper::IsDialogEx(pDlg); WORD nItems = ATL::_DialogSplitHelper::DlgTemplateItemCount(pDlg); // Get first control on the dialog DLGITEMTEMPLATE* pItem = ATL::_DialogSplitHelper::FindFirstDlgItem(pDlg); HWND hWndPrev = GetWindow(GW_CHILD); // Create all ActiveX cotnrols in the dialog template and place them in the correct tab order (z-order) for (WORD nItem = 0; nItem < nItems; nItem++) { DWORD wID = bDialogEx ? ((ATL::_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->id : pItem->id; if (ATL::_DialogSplitHelper::IsActiveXControl(pItem, bDialogEx)) { BYTE* pData = NULL; DWORD dwLen = ATL::_DialogSplitHelper::FindCreateData(wID, pInitData, &pData); ATL::CComPtr spStream; if (dwLen != 0) { HGLOBAL h = GlobalAlloc(GHND, dwLen); if (h != NULL) { BYTE* pBytes = (BYTE*) GlobalLock(h); BYTE* pSource = pData; SecureHelper::memcpy_x(pBytes, dwLen, pSource, dwLen); GlobalUnlock(h); CreateStreamOnHGlobal(h, TRUE, &spStream); } else { hr = E_OUTOFMEMORY; break; } } ATL::CComBSTR bstrLicKey; hr = ATL::_DialogSplitHelper::ParseInitData(spStream, &bstrLicKey.m_str); if (SUCCEEDED(hr)) { ATL::CAxWindow2 wnd; // Get control caption. LPWSTR pszClassName = bDialogEx ? (LPWSTR)(((ATL::_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem) + 1) : (LPWSTR)(pItem + 1); // Get control rect. RECT rect = { 0 }; rect.left = bDialogEx ? ((ATL::_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->x : pItem->x; rect.top = bDialogEx ? ((ATL::_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->y : pItem->y; rect.right = rect.left + (bDialogEx ? ((ATL::_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->cx : pItem->cx); rect.bottom = rect.top + (bDialogEx ? ((ATL::_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->cy : pItem->cy); // Convert from dialog units to screen units MapDialogRect(&rect); // Create AxWindow with a NULL caption. wnd.Create(m_hWnd, &rect, NULL, (bDialogEx ? ((ATL::_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->style : pItem->style) | WS_TABSTOP, bDialogEx ? ((ATL::_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->exStyle : 0, bDialogEx ? ((ATL::_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->id : pItem->id, NULL); if (wnd != NULL) { #ifndef _WIN32_WCE // Set the Help ID if (bDialogEx && ((ATL::_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->helpID != 0) wnd.SetWindowContextHelpId(((ATL::_DialogSplitHelper::DLGITEMTEMPLATEEX*)pItem)->helpID); #endif // !_WIN32_WCE // Try to create the ActiveX control. hr = wnd.CreateControlLic(pszClassName, spStream, NULL, bstrLicKey); if (FAILED(hr)) break; // Set the correct tab position. if (nItem == 0) hWndPrev = HWND_TOP; wnd.SetWindowPos(hWndPrev, 0,0,0,0,SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); hWndPrev = wnd; } else { hr = ATL::AtlHresultFromLastError(); } } } else { if (nItem != 0) hWndPrev = ::GetWindow(hWndPrev, GW_HWNDNEXT); } pItem = ATL::_DialogSplitHelper::FindNextDlgItem(pItem, bDialogEx); } } else hr = ATL::AtlHresultFromLastError(); } else hr = ATL::AtlHresultFromLastError(); } return hr; } // Event handling support HRESULT AdviseSinkMap(bool bAdvise) { if(!bAdvise && m_hWnd == NULL) { // window is gone, controls are already unadvised ATLTRACE2(atlTraceUI, 0, _T("CAxPropertyPageImpl::AdviseSinkMap called after the window was destroyed\n")); return S_OK; } HRESULT hRet = E_NOTIMPL; __if_exists(T::_GetSinkMapFinder) { T* pT = static_cast(this); hRet = AtlAdviseSinkMap(pT, bAdvise); } return hRet; } // Message map and handlers typedef CPropertyPageImpl< T, TBase> _baseClass; BEGIN_MSG_MAP(CAxPropertyPageImpl) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) CHAIN_MSG_MAP(_baseClass) END_MSG_MAP() LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { // initialize controls in dialog with DLGINIT resource section ExecuteDlgInit(static_cast(this)->IDD); AdviseSinkMap(true); bHandled = FALSE; return 1; } LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { AdviseSinkMap(false); bHandled = FALSE; return 1; } #endif // (_ATL_VER >= 0x0700) }; // for non-customized pages template class CAxPropertyPage : public CAxPropertyPageImpl > { public: enum { IDD = t_wDlgTemplateID }; CAxPropertyPage(ATL::_U_STRINGorID title = (LPCTSTR)NULL) : CAxPropertyPageImpl(title) { } #if (_WIN32_IE >= 0x0500) || (_ATL_VER >= 0x0700) // not empty so we handle accelerators/create controls BEGIN_MSG_MAP(CAxPropertyPage) CHAIN_MSG_MAP(CAxPropertyPageImpl >) END_MSG_MAP() #else // !((_WIN32_IE >= 0x0500) || (_ATL_VER >= 0x0700)) DECLARE_EMPTY_MSG_MAP() #endif // !((_WIN32_IE >= 0x0500) || (_ATL_VER >= 0x0700)) }; #endif // _ATL_NO_HOSTING /////////////////////////////////////////////////////////////////////////////// // Wizard97 Support #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) // Sample wizard dialog resources: // // IDD_WIZ97_INTERIOR_BLANK DIALOG 0, 0, 317, 143 // STYLE DS_SETFONT | WS_CHILD | WS_DISABLED | WS_CAPTION // CAPTION "Wizard97 Property Page - Interior" // FONT 8, "MS Shell Dlg" // BEGIN // END // // IDD_WIZ97_EXTERIOR_BLANK DIALOGEX 0, 0, 317, 193 // STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION // CAPTION "Wizard97 Property Page - Welcome/Complete" // FONT 8, "MS Shell Dlg", 0, 0, 0x0 // BEGIN // LTEXT "Welcome to the X Wizard",IDC_WIZ97_EXTERIOR_TITLE,115,8, // 195,24 // LTEXT "Wizard Explanation\r\n(The height of the static text should be in multiples of 8 dlus)", // IDC_STATIC,115,40,195,16 // LTEXT "h",IDC_WIZ97_BULLET1,118,64,8,8 // LTEXT "List Item 1 (the h is turned into a bullet)",IDC_STATIC, // 127,63,122,8 // LTEXT "h",IDC_WIZ97_BULLET2,118,79,8,8 // LTEXT "List Item 2. Keep 7 dlus between paragraphs",IDC_STATIC, // 127,78,33,8 // CONTROL "&Do not show this Welcome page again", // IDC_WIZ97_WELCOME_NOTAGAIN,"Button",BS_AUTOCHECKBOX | // WS_TABSTOP,115,169,138,10 // END // // GUIDELINES DESIGNINFO // BEGIN // IDD_WIZ97_INTERIOR_BLANK, DIALOG // BEGIN // LEFTMARGIN, 7 // RIGHTMARGIN, 310 // VERTGUIDE, 21 // VERTGUIDE, 31 // VERTGUIDE, 286 // VERTGUIDE, 296 // TOPMARGIN, 7 // BOTTOMMARGIN, 136 // HORZGUIDE, 8 // END // // IDD_WIZ97_EXTERIOR_BLANK, DIALOG // BEGIN // RIGHTMARGIN, 310 // VERTGUIDE, 115 // VERTGUIDE, 118 // VERTGUIDE, 127 // TOPMARGIN, 7 // BOTTOMMARGIN, 186 // HORZGUIDE, 8 // HORZGUIDE, 32 // HORZGUIDE, 40 // HORZGUIDE, 169 // END // END /////////////////////////////////////////////////////////////////////////////// // CWizard97SheetWindow - client side for a Wizard 97 style wizard sheet class CWizard97SheetWindow : public CPropertySheetWindow { public: // Constructors CWizard97SheetWindow(HWND hWnd = NULL) : CPropertySheetWindow(hWnd) { } CWizard97SheetWindow& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } // Operations HFONT GetExteriorPageTitleFont(void) { ATLASSERT(::IsWindow(m_hWnd)); return (HFONT)::SendMessage(m_hWnd, GetMessage_GetExteriorPageTitleFont(), 0, 0L); } HFONT GetBulletFont(void) { ATLASSERT(::IsWindow(m_hWnd)); return (HFONT)::SendMessage(m_hWnd, GetMessage_GetBulletFont(), 0, 0L); } // Helpers static UINT GetMessage_GetExteriorPageTitleFont() { static UINT uGetExteriorPageTitleFont = 0; if(uGetExteriorPageTitleFont == 0) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CWizard97SheetWindow::GetMessage_GetExteriorPageTitleFont().\n")); ATLASSERT(FALSE); return 0; } if(uGetExteriorPageTitleFont == 0) uGetExteriorPageTitleFont = ::RegisterWindowMessage(_T("GetExteriorPageTitleFont_531AF056-B8BE-4c4c-B786-AC608DF0DF12")); lock.Unlock(); } ATLASSERT(uGetExteriorPageTitleFont != 0); return uGetExteriorPageTitleFont; } static UINT GetMessage_GetBulletFont() { static UINT uGetBulletFont = 0; if(uGetBulletFont == 0) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CWizard97SheetWindow::GetMessage_GetBulletFont().\n")); ATLASSERT(FALSE); return 0; } if(uGetBulletFont == 0) uGetBulletFont = ::RegisterWindowMessage(_T("GetBulletFont_AD347D08-8F65-45ef-982E-6352E8218AD5")); lock.Unlock(); } ATLASSERT(uGetBulletFont != 0); return uGetBulletFont; } // Implementation - override to prevent usage HWND Create(LPCTSTR, HWND, ATL::_U_RECT = NULL, LPCTSTR = NULL, DWORD = 0, DWORD = 0, ATL::_U_MENUorID = 0U, LPVOID = NULL) { ATLASSERT(FALSE); return NULL; } }; /////////////////////////////////////////////////////////////////////////////// // CWizard97SheetImpl - implements a Wizard 97 style wizard sheet template class ATL_NO_VTABLE CWizard97SheetImpl : public CPropertySheetImpl< T, TBase > { protected: // Typedefs typedef CWizard97SheetImpl< T, TBase > thisClass; typedef CPropertySheetImpl< T, TBase > baseClass; // Member variables CFont m_fontExteriorPageTitle; // Welcome and Completion page title font CFont m_fontBullet; // Bullet font (used on static text 'h' to produce a small bullet) bool m_bReceivedFirstSizeMessage; public: CWizard97SheetImpl(ATL::_U_STRINGorID title, ATL::_U_STRINGorID headerBitmap, ATL::_U_STRINGorID watermarkBitmap, UINT uStartPage = 0, HWND hWndParent = NULL) : baseClass(title, uStartPage, hWndParent), m_bReceivedFirstSizeMessage(false) { m_psh.dwFlags &= ~(PSH_NOCONTEXTHELP); m_psh.dwFlags &= ~(PSH_WIZARD | PSH_WIZARD_LITE); m_psh.dwFlags |= (PSH_HASHELP | PSH_WIZARDCONTEXTHELP); m_psh.dwFlags |= PSH_WIZARD97; baseClass::SetHeader(headerBitmap.m_lpstr); baseClass::SetWatermark(watermarkBitmap.m_lpstr); } // Overrides from base class void OnSheetInitialized() { T* pT = static_cast(this); pT->_InitializeFonts(); // We'd like to center the wizard here, but its too early. // Instead, we'll do CenterWindow upon our first WM_SIZE message } // Initialization void _InitializeFonts() { // Setup the Title and Bullet Font // (Property pages can send the "get external page title font" and "get bullet font" messages) // The derived class needs to do the actual SetFont for the dialog items) CFontHandle fontThisDialog = this->GetFont(); CClientDC dcScreen(NULL); LOGFONT titleLogFont = {0}; LOGFONT bulletLogFont = {0}; fontThisDialog.GetLogFont(&titleLogFont); fontThisDialog.GetLogFont(&bulletLogFont); // The Wizard 97 Spec recommends to do the Title Font // as Verdana Bold, 12pt. titleLogFont.lfCharSet = DEFAULT_CHARSET; titleLogFont.lfWeight = FW_BOLD; SecureHelper::strcpy_x(titleLogFont.lfFaceName, _countof(titleLogFont.lfFaceName), _T("Verdana Bold")); INT titleFontPointSize = 12; titleLogFont.lfHeight = -::MulDiv(titleFontPointSize, dcScreen.GetDeviceCaps(LOGPIXELSY), 72); m_fontExteriorPageTitle.CreateFontIndirect(&titleLogFont); // The Wizard 97 Spec recommends to do Bullets by having // static text of "h" in the Marlett font. bulletLogFont.lfCharSet = DEFAULT_CHARSET; bulletLogFont.lfWeight = FW_NORMAL; SecureHelper::strcpy_x(bulletLogFont.lfFaceName, _countof(bulletLogFont.lfFaceName), _T("Marlett")); INT bulletFontSize = 8; bulletLogFont.lfHeight = -::MulDiv(bulletFontSize, dcScreen.GetDeviceCaps(LOGPIXELSY), 72); m_fontBullet.CreateFontIndirect(&bulletLogFont); } // Message Handling BEGIN_MSG_MAP(thisClass) MESSAGE_HANDLER(CWizard97SheetWindow::GetMessage_GetExteriorPageTitleFont(), OnGetExteriorPageTitleFont) MESSAGE_HANDLER(CWizard97SheetWindow::GetMessage_GetBulletFont(), OnGetBulletFont) MESSAGE_HANDLER(WM_SIZE, OnSize) CHAIN_MSG_MAP(baseClass) END_MSG_MAP() LRESULT OnGetExteriorPageTitleFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return (LRESULT)(HFONT)m_fontExteriorPageTitle; } LRESULT OnGetBulletFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return (LRESULT)(HFONT)m_fontBullet; } LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(!m_bReceivedFirstSizeMessage) { m_bReceivedFirstSizeMessage = true; this->CenterWindow(); } bHandled = FALSE; return 0; } }; // for non-customized sheets class CWizard97Sheet : public CWizard97SheetImpl { protected: // Typedefs typedef CWizard97Sheet thisClass; typedef CWizard97SheetImpl baseClass; public: CWizard97Sheet(ATL::_U_STRINGorID title, ATL::_U_STRINGorID headerBitmap, ATL::_U_STRINGorID watermarkBitmap, UINT uStartPage = 0, HWND hWndParent = NULL) : baseClass(title, headerBitmap, watermarkBitmap, uStartPage, hWndParent) { } BEGIN_MSG_MAP(thisClass) CHAIN_MSG_MAP(baseClass) END_MSG_MAP() }; /////////////////////////////////////////////////////////////////////////////// // CWizard97PageWindow - client side for a Wizard 97 style wizard page #define WIZARD97_EXTERIOR_CXDLG 317 #define WIZARD97_EXTERIOR_CYDLG 193 #define WIZARD97_INTERIOR_CXDLG 317 #define WIZARD97_INTERIOR_CYDLG 143 class CWizard97PageWindow : public CPropertyPageWindow { public: // Constructors CWizard97PageWindow(HWND hWnd = NULL) : CPropertyPageWindow(hWnd) { } CWizard97PageWindow& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } // Attributes CWizard97SheetWindow GetPropertySheet() const { ATLASSERT(::IsWindow(m_hWnd)); return CWizard97SheetWindow(GetParent()); } // Operations HFONT GetExteriorPageTitleFont(void) { ATLASSERT(::IsWindow(m_hWnd)); return GetPropertySheet().GetExteriorPageTitleFont(); } HFONT GetBulletFont(void) { ATLASSERT(::IsWindow(m_hWnd)); return GetPropertySheet().GetBulletFont(); } // Implementation - overrides to prevent usage HWND Create(LPCTSTR, HWND, ATL::_U_RECT = NULL, LPCTSTR = NULL, DWORD = 0, DWORD = 0, ATL::_U_MENUorID = 0U, LPVOID = NULL) { ATLASSERT(FALSE); return NULL; } }; /////////////////////////////////////////////////////////////////////////////// // CWizard97PageImpl - implements a Wizard 97 style wizard page template class ATL_NO_VTABLE CWizard97PageImpl : public CPropertyPageImpl< T, TBase > { protected: // Typedefs typedef CWizard97PageImpl< T, TBase > thisClass; typedef CPropertyPageImpl< T, TBase > baseClass; public: CWizard97PageImpl(ATL::_U_STRINGorID title = (LPCTSTR)NULL) : baseClass(title) { } // Message Handling BEGIN_MSG_MAP(thisClass) CHAIN_MSG_MAP(baseClass) END_MSG_MAP() }; /////////////////////////////////////////////////////////////////////////////// // CWizard97ExteriorPageImpl - implements a Wizard 97 style exterior wizard page template class ATL_NO_VTABLE CWizard97ExteriorPageImpl : public CPropertyPageImpl< T, TBase > { protected: // Typedefs typedef CWizard97ExteriorPageImpl< T, TBase > thisClass; typedef CPropertyPageImpl< T, TBase > baseClass; public: // Constructors CWizard97ExteriorPageImpl(ATL::_U_STRINGorID title = (LPCTSTR)NULL) : baseClass(title) { m_psp.dwFlags |= PSP_HASHELP; m_psp.dwFlags |= PSP_HIDEHEADER; } // Message Handling BEGIN_MSG_MAP(thisClass) CHAIN_MSG_MAP(baseClass) END_MSG_MAP() }; /////////////////////////////////////////////////////////////////////////////// // CWizard97InteriorPageImpl - implements a Wizard 97 style interior wizard page template class ATL_NO_VTABLE CWizard97InteriorPageImpl : public CPropertyPageImpl< T, TBase > { protected: // Typedefs typedef CWizard97InteriorPageImpl< T, TBase > thisClass; typedef CPropertyPageImpl< T, TBase > baseClass; public: // Constructors CWizard97InteriorPageImpl(ATL::_U_STRINGorID title = (LPCTSTR)NULL) : baseClass(title) { m_psp.dwFlags |= PSP_HASHELP; m_psp.dwFlags &= ~PSP_HIDEHEADER; m_psp.dwFlags |= PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; // Be sure to have the derived class define this in the constructor. // We'll default it to something obvious in case its forgotten. baseClass::SetHeaderTitle(_T("Call SetHeaderTitle in Derived Class")); baseClass::SetHeaderSubTitle(_T("Call SetHeaderSubTitle in the constructor of the Derived Class.")); } // Message Handling BEGIN_MSG_MAP(thisClass) CHAIN_MSG_MAP(baseClass) END_MSG_MAP() }; #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) /////////////////////////////////////////////////////////////////////////////// // Aero Wizard support #if (_WIN32_WINNT >= 0x0600) && !defined(_WIN32_WCE) /////////////////////////////////////////////////////////////////////////////// // CAeroWizardFrameWindow - client side for an Aero Wizard frame window class CAeroWizardFrameWindow : public CPropertySheetWindow { public: // Constructors CAeroWizardFrameWindow(HWND hWnd = NULL) : CPropertySheetWindow(hWnd) { } CAeroWizardFrameWindow& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } // Operations - new, Aero Wizard only void SetNextText(LPCWSTR lpszText) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, PSM_SETNEXTTEXT, 0, (LPARAM)lpszText); } void ShowWizardButtons(DWORD dwButtons, DWORD dwStates) { ATLASSERT(::IsWindow(m_hWnd)); ::PostMessage(m_hWnd, PSM_SHOWWIZBUTTONS, (WPARAM)dwStates, (LPARAM)dwButtons); } void EnableWizardButtons(DWORD dwButtons, DWORD dwStates) { ATLASSERT(::IsWindow(m_hWnd)); ::PostMessage(m_hWnd, PSM_ENABLEWIZBUTTONS, (WPARAM)dwStates, (LPARAM)dwButtons); } void SetButtonText(DWORD dwButton, LPCWSTR lpszText) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, PSM_SETBUTTONTEXT, (WPARAM)dwButton, (LPARAM)lpszText); } }; /////////////////////////////////////////////////////////////////////////////// // CAeroWizardFrameImpl - implements an Aero Wizard frame template class ATL_NO_VTABLE CAeroWizardFrameImpl : public CPropertySheetImpl { public: // Constructor CAeroWizardFrameImpl(ATL::_U_STRINGorID title = (LPCTSTR)NULL, UINT uStartPage = 0, HWND hWndParent = NULL) : CPropertySheetImpl(title, uStartPage, hWndParent) { m_psh.dwFlags |= PSH_WIZARD | PSH_AEROWIZARD; } // Operations void EnableResizing() { ATLASSERT(m_hWnd == NULL); // can't do this after it's created m_psh.dwFlags |= PSH_RESIZABLE; } void UseHeaderBitmap() { ATLASSERT(m_hWnd == NULL); // can't do this after it's created m_psh.dwFlags |= PSH_HEADERBITMAP; } void SetNoMargin() { ATLASSERT(m_hWnd == NULL); // can't do this after it's created m_psh.dwFlags |= PSH_NOMARGIN; } // Override to prevent use HWND Create(HWND /*hWndParent*/ = NULL) { ATLASSERT(FALSE); // not supported for Aero Wizard return NULL; } }; /////////////////////////////////////////////////////////////////////////////// // CAeroWizardFrame - for non-customized frames class CAeroWizardFrame : public CAeroWizardFrameImpl { public: CAeroWizardFrame(ATL::_U_STRINGorID title = (LPCTSTR)NULL, UINT uStartPage = 0, HWND hWndParent = NULL) : CAeroWizardFrameImpl(title, uStartPage, hWndParent) { } BEGIN_MSG_MAP(CAeroWizardFrame) MESSAGE_HANDLER(WM_COMMAND, CAeroWizardFrameImpl::OnCommand) END_MSG_MAP() }; /////////////////////////////////////////////////////////////////////////////// // CAeroWizardPageWindow - client side for an Aero Wizard page class CAeroWizardPageWindow : public CPropertyPageWindow { public: // Constructors CAeroWizardPageWindow(HWND hWnd = NULL) : CPropertyPageWindow(hWnd) { } CAeroWizardPageWindow& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } // Attributes CAeroWizardFrameWindow GetAeroWizardFrame() const { ATLASSERT(::IsWindow(m_hWnd)); // This is not really top-level frame window, but it processes all frame messages return CAeroWizardFrameWindow(GetParent()); } // Operations - new, Aero Wizard only void SetNextText(LPCWSTR lpszText) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetParent() != NULL); GetAeroWizardFrame().SetNextText(lpszText); } void ShowWizardButtons(DWORD dwButtons, DWORD dwStates) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetParent() != NULL); GetAeroWizardFrame().ShowWizardButtons(dwButtons, dwStates); } void EnableWizardButtons(DWORD dwButtons, DWORD dwStates) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetParent() != NULL); GetAeroWizardFrame().EnableWizardButtons(dwButtons, dwStates); } void SetButtonText(DWORD dwButton, LPCWSTR lpszText) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetParent() != NULL); GetAeroWizardFrame().SetButtonText(dwButton, lpszText); } }; /////////////////////////////////////////////////////////////////////////////// // CAeroWizardPageImpl - implements an Aero Wizard page template class ATL_NO_VTABLE CAeroWizardPageImpl : public CPropertyPageImpl { public: CAeroWizardPageImpl(ATL::_U_STRINGorID title = (LPCTSTR)NULL) : CPropertyPageImpl(title) { } }; /////////////////////////////////////////////////////////////////////////////// // CAeroWizardPage - for non-customized pages template class CAeroWizardPage : public CAeroWizardPageImpl > { public: enum { IDD = t_wDlgTemplateID }; CAeroWizardPage(ATL::_U_STRINGorID title = (LPCTSTR)NULL) : CAeroWizardPageImpl(title) { } DECLARE_EMPTY_MSG_MAP() }; #ifndef _ATL_NO_HOSTING // Note: You must #include to use these classes /////////////////////////////////////////////////////////////////////////////// // CAeroWizardAxPageImpl - Aero Wizard page that hosts ActiveX controls template class ATL_NO_VTABLE CAeroWizardAxPageImpl : public CAxPropertyPageImpl< T, TBase > { public: CAeroWizardAxPageImpl(ATL::_U_STRINGorID title = (LPCTSTR)NULL) : CAxPropertyPageImpl< T, TBase >(title) { } }; /////////////////////////////////////////////////////////////////////////////// // CAeroWizardAxPage - for non-customized pages template class CAeroWizardAxPage : public CAeroWizardAxPageImpl > { public: enum { IDD = t_wDlgTemplateID }; CAeroWizardAxPage(ATL::_U_STRINGorID title = (LPCTSTR)NULL) : CAeroWizardAxPageImpl(title) { } #if (_WIN32_IE >= 0x0500) || (_ATL_VER >= 0x0700) // not empty so we handle accelerators/create controls BEGIN_MSG_MAP(CAeroWizardAxPage) CHAIN_MSG_MAP(CAeroWizardAxPageImpl >) END_MSG_MAP() #else // !((_WIN32_IE >= 0x0500) || (_ATL_VER >= 0x0700)) DECLARE_EMPTY_MSG_MAP() #endif // !((_WIN32_IE >= 0x0500) || (_ATL_VER >= 0x0700)) }; #endif // _ATL_NO_HOSTING #endif // (_WIN32_WINNT >= 0x0600) && !defined(_WIN32_WCE) /////////////////////////////////////////////////////////////////////////////// // TaskDialog support #if ((_WIN32_WINNT >= 0x0600) || defined(_WTL_TASKDIALOG)) && !defined(_WIN32_WCE) /////////////////////////////////////////////////////////////////////////////// // AtlTaskDialog - support for TaskDialog() function inline int AtlTaskDialog(HWND hWndParent, ATL::_U_STRINGorID WindowTitle, ATL::_U_STRINGorID MainInstructionText, ATL::_U_STRINGorID ContentText, TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons = 0U, ATL::_U_STRINGorID Icon = (LPCTSTR)NULL) { int nRet = -1; #ifdef _WTL_TASKDIALOG_DIRECT USES_CONVERSION; HRESULT hRet = ::TaskDialog(hWndParent, ModuleHelper::GetResourceInstance(), IS_INTRESOURCE(WindowTitle.m_lpstr) ? (LPCWSTR) WindowTitle.m_lpstr : T2CW(WindowTitle.m_lpstr), IS_INTRESOURCE(MainInstructionText.m_lpstr) ? (LPCWSTR) MainInstructionText.m_lpstr : T2CW(MainInstructionText.m_lpstr), IS_INTRESOURCE(ContentText.m_lpstr) ? (LPCWSTR) ContentText.m_lpstr : T2CW(ContentText.m_lpstr), dwCommonButtons, IS_INTRESOURCE(Icon.m_lpstr) ? (LPCWSTR) Icon.m_lpstr : T2CW(Icon.m_lpstr), &nRet); ATLVERIFY(SUCCEEDED(hRet)); #else // This allows apps to run on older versions of Windows typedef HRESULT (STDAPICALLTYPE *PFN_TaskDialog)(HWND hwndParent, HINSTANCE hInstance, PCWSTR pszWindowTitle, PCWSTR pszMainInstruction, PCWSTR pszContent, TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons, PCWSTR pszIcon, int* pnButton); HMODULE m_hCommCtrlDLL = ::LoadLibrary(_T("comctl32.dll")); if(m_hCommCtrlDLL != NULL) { PFN_TaskDialog pfnTaskDialog = (PFN_TaskDialog)::GetProcAddress(m_hCommCtrlDLL, "TaskDialog"); if(pfnTaskDialog != NULL) { USES_CONVERSION; HRESULT hRet = pfnTaskDialog(hWndParent, ModuleHelper::GetResourceInstance(), IS_INTRESOURCE(WindowTitle.m_lpstr) ? (LPCWSTR) WindowTitle.m_lpstr : T2CW(WindowTitle.m_lpstr), IS_INTRESOURCE(MainInstructionText.m_lpstr) ? (LPCWSTR) MainInstructionText.m_lpstr : T2CW(MainInstructionText.m_lpstr), IS_INTRESOURCE(ContentText.m_lpstr) ? (LPCWSTR) ContentText.m_lpstr : T2CW(ContentText.m_lpstr), dwCommonButtons, IS_INTRESOURCE(Icon.m_lpstr) ? (LPCWSTR) Icon.m_lpstr : T2CW(Icon.m_lpstr), &nRet); ATLVERIFY(SUCCEEDED(hRet)); } ::FreeLibrary(m_hCommCtrlDLL); } #endif return nRet; } /////////////////////////////////////////////////////////////////////////////// // CTaskDialogConfig - TASKDIALOGCONFIG wrapper class CTaskDialogConfig : public TASKDIALOGCONFIG { public: // Constructor CTaskDialogConfig() { Init(); } void Init() { memset(this, 0, sizeof(TASKDIALOGCONFIG)); // initialize structure to 0/NULL this->cbSize = sizeof(TASKDIALOGCONFIG); this->hInstance = ModuleHelper::GetResourceInstance(); } // Operations - setting values // common buttons void SetCommonButtons(TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons) { this->dwCommonButtons = dwCommonButtons; } // window title text void SetWindowTitle(UINT nID) { this->pszWindowTitle = MAKEINTRESOURCEW(nID); } void SetWindowTitle(LPCWSTR lpstrWindowTitle) { this->pszWindowTitle = lpstrWindowTitle; } // main icon void SetMainIcon(HICON hIcon) { this->dwFlags |= TDF_USE_HICON_MAIN; this->hMainIcon = hIcon; } void SetMainIcon(UINT nID) { this->dwFlags &= ~TDF_USE_HICON_MAIN; this->pszMainIcon = MAKEINTRESOURCEW(nID); } void SetMainIcon(LPCWSTR lpstrMainIcon) { this->dwFlags &= ~TDF_USE_HICON_MAIN; this->pszMainIcon = lpstrMainIcon; } // main instruction text void SetMainInstructionText(UINT nID) { this->pszMainInstruction = MAKEINTRESOURCEW(nID); } void SetMainInstructionText(LPCWSTR lpstrMainInstruction) { this->pszMainInstruction = lpstrMainInstruction; } // content text void SetContentText(UINT nID) { this->pszContent = MAKEINTRESOURCEW(nID); } void SetContentText(LPCWSTR lpstrContent) { this->pszContent = lpstrContent; } // buttons void SetButtons(const TASKDIALOG_BUTTON* pButtons, UINT cButtons, int nDefaultButton = 0) { this->pButtons = pButtons; this->cButtons = cButtons; if(nDefaultButton != 0) this->nDefaultButton = nDefaultButton; } void SetDefaultButton(int nDefaultButton) { this->nDefaultButton = nDefaultButton; } // radio buttons void SetRadioButtons(const TASKDIALOG_BUTTON* pRadioButtons, UINT cRadioButtons, int nDefaultRadioButton = 0) { this->pRadioButtons = pRadioButtons; this->cRadioButtons = cRadioButtons; if(nDefaultRadioButton != 0) this->nDefaultRadioButton = nDefaultRadioButton; } void SetDefaultRadioButton(int nDefaultRadioButton) { this->nDefaultRadioButton = nDefaultRadioButton; } // verification text void SetVerificationText(UINT nID) { this->pszVerificationText = MAKEINTRESOURCEW(nID); } void SetVerificationText(LPCWSTR lpstrVerificationText) { this->pszVerificationText = lpstrVerificationText; } // expanded information text void SetExpandedInformationText(UINT nID) { this->pszExpandedInformation = MAKEINTRESOURCEW(nID); } void SetExpandedInformationText(LPCWSTR lpstrExpandedInformation) { this->pszExpandedInformation = lpstrExpandedInformation; } // expanded control text void SetExpandedControlText(UINT nID) { this->pszExpandedControlText = MAKEINTRESOURCEW(nID); } void SetExpandedControlText(LPCWSTR lpstrExpandedControlText) { this->pszExpandedControlText = lpstrExpandedControlText; } // collapsed control text void SetCollapsedControlText(UINT nID) { this->pszCollapsedControlText = MAKEINTRESOURCEW(nID); } void SetCollapsedControlText(LPCWSTR lpstrCollapsedControlText) { this->pszCollapsedControlText = lpstrCollapsedControlText; } // footer icon void SetFooterIcon(HICON hIcon) { this->dwFlags |= TDF_USE_HICON_FOOTER; this->hFooterIcon = hIcon; } void SetFooterIcon(UINT nID) { this->dwFlags &= ~TDF_USE_HICON_FOOTER; this->pszFooterIcon = MAKEINTRESOURCEW(nID); } void SetFooterIcon(LPCWSTR lpstrFooterIcon) { this->dwFlags &= ~TDF_USE_HICON_FOOTER; this->pszFooterIcon = lpstrFooterIcon; } // footer text void SetFooterText(UINT nID) { this->pszFooter = MAKEINTRESOURCEW(nID); } void SetFooterText(LPCWSTR lpstrFooterText) { this->pszFooter = lpstrFooterText; } // width (in DLUs) void SetWidth(UINT cxWidth) { this->cxWidth = cxWidth; } // modify flags void ModifyFlags(DWORD dwRemove, DWORD dwAdd) { this->dwFlags = (this->dwFlags & ~dwRemove) | dwAdd; } }; /////////////////////////////////////////////////////////////////////////////// // CTaskDialogImpl - implements a Task Dialog template class ATL_NO_VTABLE CTaskDialogImpl { public: CTaskDialogConfig m_tdc; HWND m_hWnd; // used only in callback functions // Constructor CTaskDialogImpl(HWND hWndParent = NULL) : m_hWnd(NULL) { m_tdc.hwndParent = hWndParent; m_tdc.pfCallback = T::TaskDialogCallback; m_tdc.lpCallbackData = (LONG_PTR)static_cast(this); } // Operations HRESULT DoModal(HWND hWndParent = ::GetActiveWindow(), int* pnButton = NULL, int* pnRadioButton = NULL, BOOL* pfVerificationFlagChecked = NULL) { if(m_tdc.hwndParent == NULL) m_tdc.hwndParent = hWndParent; #ifdef _WTL_TASKDIALOG_DIRECT return ::TaskDialogIndirect(&m_tdc, pnButton, pnRadioButton, pfVerificationFlagChecked); #else // This allows apps to run on older versions of Windows typedef HRESULT (STDAPICALLTYPE *PFN_TaskDialogIndirect)(const TASKDIALOGCONFIG* pTaskConfig, int* pnButton, int* pnRadioButton, BOOL* pfVerificationFlagChecked); HRESULT hRet = E_UNEXPECTED; HMODULE m_hCommCtrlDLL = ::LoadLibrary(_T("comctl32.dll")); if(m_hCommCtrlDLL != NULL) { PFN_TaskDialogIndirect pfnTaskDialogIndirect = (PFN_TaskDialogIndirect)::GetProcAddress(m_hCommCtrlDLL, "TaskDialogIndirect"); if(pfnTaskDialogIndirect != NULL) hRet = pfnTaskDialogIndirect(&m_tdc, pnButton, pnRadioButton, pfVerificationFlagChecked); ::FreeLibrary(m_hCommCtrlDLL); } return hRet; #endif } // Operations - setting values of TASKDIALOGCONFIG // common buttons void SetCommonButtons(TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons) { m_tdc.SetCommonButtons(dwCommonButtons); } // window title text void SetWindowTitle(UINT nID) { m_tdc.SetWindowTitle(nID); } void SetWindowTitle(LPCWSTR lpstrWindowTitle) { m_tdc.SetWindowTitle(lpstrWindowTitle); } // main icon void SetMainIcon(HICON hIcon) { m_tdc.SetMainIcon(hIcon); } void SetMainIcon(UINT nID) { m_tdc.SetMainIcon(nID); } void SetMainIcon(LPCWSTR lpstrMainIcon) { m_tdc.SetMainIcon(lpstrMainIcon); } // main instruction text void SetMainInstructionText(UINT nID) { m_tdc.SetMainInstructionText(nID); } void SetMainInstructionText(LPCWSTR lpstrMainInstruction) { m_tdc.SetMainInstructionText(lpstrMainInstruction); } // content text void SetContentText(UINT nID) { m_tdc.SetContentText(nID); } void SetContentText(LPCWSTR lpstrContent) { m_tdc.SetContentText(lpstrContent); } // buttons void SetButtons(const TASKDIALOG_BUTTON* pButtons, UINT cButtons, int nDefaultButton = 0) { m_tdc.SetButtons(pButtons, cButtons, nDefaultButton); } void SetDefaultButton(int nDefaultButton) { m_tdc.SetDefaultButton(nDefaultButton); } // radio buttons void SetRadioButtons(const TASKDIALOG_BUTTON* pRadioButtons, UINT cRadioButtons, int nDefaultRadioButton = 0) { m_tdc.SetRadioButtons(pRadioButtons, cRadioButtons, nDefaultRadioButton); } void SetDefaultRadioButton(int nDefaultRadioButton) { m_tdc.SetDefaultRadioButton(nDefaultRadioButton); } // verification text void SetVerificationText(UINT nID) { m_tdc.SetVerificationText(nID); } void SetVerificationText(LPCWSTR lpstrVerificationText) { m_tdc.SetVerificationText(lpstrVerificationText); } // expanded information text void SetExpandedInformationText(UINT nID) { m_tdc.SetExpandedInformationText(nID); } void SetExpandedInformationText(LPCWSTR lpstrExpandedInformation) { m_tdc.SetExpandedInformationText(lpstrExpandedInformation); } // expanded control text void SetExpandedControlText(UINT nID) { m_tdc.SetExpandedControlText(nID); } void SetExpandedControlText(LPCWSTR lpstrExpandedControlText) { m_tdc.SetExpandedControlText(lpstrExpandedControlText); } // collapsed control text void SetCollapsedControlText(UINT nID) { m_tdc.SetCollapsedControlText(nID); } void SetCollapsedControlText(LPCWSTR lpstrCollapsedControlText) { m_tdc.SetCollapsedControlText(lpstrCollapsedControlText); } // footer icon void SetFooterIcon(HICON hIcon) { m_tdc.SetFooterIcon(hIcon); } void SetFooterIcon(UINT nID) { m_tdc.SetFooterIcon(nID); } void SetFooterIcon(LPCWSTR lpstrFooterIcon) { m_tdc.SetFooterIcon(lpstrFooterIcon); } // footer text void SetFooterText(UINT nID) { m_tdc.SetFooterText(nID); } void SetFooterText(LPCWSTR lpstrFooterText) { m_tdc.SetFooterText(lpstrFooterText); } // width (in DLUs) void SetWidth(UINT cxWidth) { m_tdc.SetWidth(cxWidth); } // modify flags void ModifyFlags(DWORD dwRemove, DWORD dwAdd) { m_tdc.ModifyFlags(dwRemove, dwAdd); } // Implementation static HRESULT CALLBACK TaskDialogCallback(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData) { T* pT = (T*)lpRefData; ATLASSERT(pT->m_hWnd == NULL || pT->m_hWnd == hWnd); BOOL bRet = FALSE; switch(uMsg) { case TDN_DIALOG_CONSTRUCTED: pT->m_hWnd = hWnd; pT->OnDialogConstructed(); break; case TDN_CREATED: pT->OnCreated(); break; case TDN_BUTTON_CLICKED: bRet = pT->OnButtonClicked((int)wParam); break; case TDN_RADIO_BUTTON_CLICKED: pT->OnRadioButtonClicked((int)wParam); break; case TDN_HYPERLINK_CLICKED: pT->OnHyperlinkClicked((LPCWSTR)lParam); break; case TDN_EXPANDO_BUTTON_CLICKED: pT->OnExpandoButtonClicked((wParam != 0)); break; case TDN_VERIFICATION_CLICKED: pT->OnVerificationClicked((wParam != 0)); break; case TDN_HELP: pT->OnHelp(); break; case TDN_TIMER: bRet = pT->OnTimer((DWORD)wParam); break; case TDN_NAVIGATED: pT->OnNavigated(); break; case TDN_DESTROYED: pT->OnDestroyed(); pT->m_hWnd = NULL; break; default: ATLTRACE2(atlTraceUI, 0, _T("Unknown notification received in CTaskDialogImpl::TaskDialogCallback\n")); break; } return (bRet != FALSE) ? S_OK : S_FALSE; } // Overrideables - notification handlers void OnDialogConstructed() { } void OnCreated() { } BOOL OnButtonClicked(int /*nButton*/) { return FALSE; // don't prevent dialog to close } void OnRadioButtonClicked(int /*nRadioButton*/) { } void OnHyperlinkClicked(LPCWSTR /*pszHREF*/) { } void OnExpandoButtonClicked(bool /*bExpanded*/) { } void OnVerificationClicked(bool /*bChecked*/) { } void OnHelp() { } BOOL OnTimer(DWORD /*dwTickCount*/) { return FALSE; // don't reset counter } void OnNavigated() { } void OnDestroyed() { } // Commands - valid to call only from handlers void NavigatePage(TASKDIALOGCONFIG& tdc) { ATLASSERT(m_hWnd != NULL); tdc.cbSize = sizeof(TASKDIALOGCONFIG); if(tdc.hwndParent == NULL) tdc.hwndParent = m_tdc.hwndParent; tdc.pfCallback = m_tdc.pfCallback; tdc.lpCallbackData = m_tdc.lpCallbackData; (TASKDIALOGCONFIG)m_tdc = tdc; ::SendMessage(m_hWnd, TDM_NAVIGATE_PAGE, 0, (LPARAM)&tdc); } // modify TASKDIALOGCONFIG values, then call this to update task dialog void NavigatePage() { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, TDM_NAVIGATE_PAGE, 0, (LPARAM)&m_tdc); } void ClickButton(int nButton) { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, TDM_CLICK_BUTTON, nButton, 0L); } void SetMarqueeProgressBar(BOOL bMarquee) { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, TDM_SET_MARQUEE_PROGRESS_BAR, bMarquee, 0L); } BOOL SetProgressBarState(int nNewState) { ATLASSERT(m_hWnd != NULL); return (BOOL)::SendMessage(m_hWnd, TDM_SET_PROGRESS_BAR_STATE, nNewState, 0L); } DWORD SetProgressBarRange(int nMinRange, int nMaxRange) { ATLASSERT(m_hWnd != NULL); return (DWORD)::SendMessage(m_hWnd, TDM_SET_PROGRESS_BAR_RANGE, 0, MAKELPARAM(nMinRange, nMaxRange)); } int SetProgressBarPos(int nNewPos) { ATLASSERT(m_hWnd != NULL); return (int)::SendMessage(m_hWnd, TDM_SET_PROGRESS_BAR_POS, nNewPos, 0L); } BOOL SetProgressBarMarquee(BOOL bMarquee, UINT uSpeed) { ATLASSERT(m_hWnd != NULL); return (BOOL)::SendMessage(m_hWnd, TDM_SET_PROGRESS_BAR_MARQUEE, bMarquee, uSpeed); } void SetElementText(TASKDIALOG_ELEMENTS element, LPCWSTR lpstrText) { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, TDM_SET_ELEMENT_TEXT, element, (LPARAM)lpstrText); } void ClickRadioButton(int nRadioButton) { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, TDM_CLICK_RADIO_BUTTON, nRadioButton, 0L); } void EnableButton(int nButton, BOOL bEnable) { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, TDM_ENABLE_BUTTON, nButton, bEnable); } void EnableRadioButton(int nButton, BOOL bEnable) { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, TDM_ENABLE_RADIO_BUTTON, nButton, bEnable); } void ClickVerification(BOOL bCheck, BOOL bFocus) { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, TDM_CLICK_VERIFICATION, bCheck, bFocus); } void UpdateElementText(TASKDIALOG_ELEMENTS element, LPCWSTR lpstrText) { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, TDM_UPDATE_ELEMENT_TEXT, element, (LPARAM)lpstrText); } void SetButtonElevationRequiredState(int nButton, BOOL bElevation) { ATLASSERT(m_hWnd != NULL); ::SendMessage(m_hWnd, TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, nButton, bElevation); } void UpdateIcon(TASKDIALOG_ICON_ELEMENTS element, HICON hIcon) { ATLASSERT(m_hWnd != NULL); #ifdef _DEBUG if(element == TDIE_ICON_MAIN) ATLASSERT((m_tdc.dwFlags & TDF_USE_HICON_MAIN) != 0); else if(element == TDIE_ICON_FOOTER) ATLASSERT((m_tdc.dwFlags & TDF_USE_HICON_FOOTER) != 0); #endif // _DEBUG ::SendMessage(m_hWnd, TDM_UPDATE_ICON, element, (LPARAM)hIcon); } void UpdateIcon(TASKDIALOG_ICON_ELEMENTS element, LPCWSTR lpstrIcon) { ATLASSERT(m_hWnd != NULL); #ifdef _DEBUG if(element == TDIE_ICON_MAIN) ATLASSERT((m_tdc.dwFlags & TDF_USE_HICON_MAIN) == 0); else if(element == TDIE_ICON_FOOTER) ATLASSERT((m_tdc.dwFlags & TDF_USE_HICON_FOOTER) == 0); #endif // _DEBUG ::SendMessage(m_hWnd, TDM_UPDATE_ICON, element, (LPARAM)lpstrIcon); } }; /////////////////////////////////////////////////////////////////////////////// // CTaskDialog - for non-customized task dialogs class CTaskDialog : public CTaskDialogImpl { public: CTaskDialog(HWND hWndParent = NULL) : CTaskDialogImpl(hWndParent) { m_tdc.pfCallback = NULL; } }; #endif // ((_WIN32_WINNT >= 0x0600) || defined(_WTL_TASKDIALOG)) && !defined(_WIN32_WCE) }; // namespace WTL #endif // __ATLDLGS_H__ ================================================ FILE: src/Setup/wtl90/atldwm.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLDWM_H__ #define __ATLDWM_H__ #pragma once #ifdef _WIN32_WCE #error atldwm.h is not supported on Windows CE #endif #ifndef __ATLAPP_H__ #error atldwm.h requires atlapp.h to be included first #endif #ifndef __ATLWIN_H__ #error atldwm.h requires atlwin.h to be included first #endif #if (_WIN32_WINNT < 0x0600) #error atldwm.h requires _WIN32_WINNT >= 0x0600 #endif #ifndef _DWMAPI_H_ #include #endif #pragma comment(lib, "dwmapi.lib") // Note: To create an application that also runs on older versions of Windows, // use delay load of dwmapi.dll and ensure that no calls to the DWM API are // Delay load is NOT AUTOMATIC for VC++ 7, you have to link to delayimp.lib, // and add dwmapi.dll in the Linker.Input.Delay Loaded DLLs section of the // project properties. #if (_MSC_VER < 1300) && !defined(_WTL_NO_DWMAPI_DELAYLOAD) #pragma comment(lib, "delayimp.lib") #pragma comment(linker, "/delayload:dwmapi.dll") #endif // (_MSC_VER < 1300) && !defined(_WTL_NO_DWMAPI_DELAYLOAD) /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CDwm // CDwmImpl // CDwmWindowT - CDwmWindow // CDwmThumbnailT // CDwmThumbnail // CDwmThumbnailHandle // CAeroControlImpl namespace WTL { /////////////////////////////////////////////////////////////////////////////// // CDwm - wrapper for DWM handle class CDwm { public: // Data members static int m_nIsDwmSupported; // Constructor CDwm() { IsDwmSupported(); } // Dwm support helper static bool IsDwmSupported() { if(m_nIsDwmSupported == -1) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CDwm::IsDwmSupported.\n")); ATLASSERT(FALSE); return false; } if(m_nIsDwmSupported == -1) { HMODULE hDwmDLL = ::LoadLibrary(_T("dwmapi.dll")); m_nIsDwmSupported = (hDwmDLL != NULL) ? 1 : 0; if(hDwmDLL != NULL) ::FreeLibrary(hDwmDLL); } lock.Unlock(); } ATLASSERT(m_nIsDwmSupported != -1); return (m_nIsDwmSupported == 1); } // Operations BOOL DwmIsCompositionEnabled() const { if(!IsDwmSupported()) return FALSE; BOOL bRes = FALSE; return (SUCCEEDED(::DwmIsCompositionEnabled(&bRes)) && bRes) ? TRUE : FALSE; } BOOL DwmEnableComposition(UINT fEnable) { if(!IsDwmSupported()) return FALSE; return SUCCEEDED(::DwmEnableComposition(fEnable)) ? TRUE : FALSE; } BOOL DwmEnableMMCSS(BOOL fEnableMMCSS) { if(!IsDwmSupported()) return FALSE; return SUCCEEDED(::DwmEnableMMCSS(fEnableMMCSS)) ? TRUE : FALSE; } HRESULT DwmGetColorizationColor(DWORD* pcrColorization, BOOL* pfOpaqueBlend) { if(!IsDwmSupported()) return E_NOTIMPL; return ::DwmGetColorizationColor(pcrColorization, pfOpaqueBlend); } HRESULT DwmFlush() { if(!IsDwmSupported()) return E_NOTIMPL; return ::DwmFlush(); } }; __declspec(selectany) int CDwm::m_nIsDwmSupported = -1; /////////////////////////////////////////////////////////////////////////////// // CDwmImpl - DWM window support template class CDwmImpl : public TBase { public: HRESULT DwmEnableBlurBehindWindow(const DWM_BLURBEHIND* pBB) { if(!IsDwmSupported()) return E_NOTIMPL; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::DwmEnableBlurBehindWindow(pT->m_hWnd, pBB); } HRESULT DwmExtendFrameIntoClientArea(const MARGINS* pMargins) { if(!IsDwmSupported()) return E_NOTIMPL; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::DwmExtendFrameIntoClientArea(pT->m_hWnd, pMargins); } HRESULT DwmExtendFrameIntoEntireClientArea() { MARGINS margins = { -1 }; return DwmExtendFrameIntoClientArea(&margins); } HRESULT DwmGetCompositionTimingInfo(DWM_TIMING_INFO* pTimingInfo) { if(!IsDwmSupported()) return E_NOTIMPL; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::DwmGetCompositionTimingInfo(pT->m_hWnd, pTimingInfo); } HRESULT DwmGetWindowAttribute(DWORD dwAttribute, PVOID pvAttribute, DWORD cbAttribute) { if(!IsDwmSupported()) return E_NOTIMPL; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::DwmGetWindowAttribute(pT->m_hWnd, dwAttribute, pvAttribute, cbAttribute); } HRESULT DwmModifyPreviousDxFrameDuration(INT cRefreshes, BOOL fRelative) { if(!IsDwmSupported()) return E_NOTIMPL; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::DwmModifyPreviousDxFrameDuration(pT->m_hWnd, cRefreshes, fRelative); } HRESULT DwmSetDxFrameDuration(INT cRefreshes) { if(!IsDwmSupported()) return E_NOTIMPL; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::DwmSetDxFrameDuration(pT->m_hWnd, cRefreshes); } HRESULT DwmSetPresentParameters(DWM_PRESENT_PARAMETERS* pPresentParams) { if(!IsDwmSupported()) return E_NOTIMPL; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::DwmSetPresentParameters(pT->m_hWnd, pPresentParams); } HRESULT DwmSetWindowAttribute(DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute) { if(!IsDwmSupported()) return E_NOTIMPL; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::DwmSetWindowAttribute(pT->m_hWnd, dwAttribute, pvAttribute, cbAttribute); } HRESULT DwmAttachMilContent() { if(!IsDwmSupported()) return E_NOTIMPL; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::DwmAttachMilContent(pT->m_hWnd); } HRESULT DwmDetachMilContent() { if(!IsDwmSupported()) return E_NOTIMPL; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::DwmDetachMilContent(pT->m_hWnd); } }; template class CDwmWindowT : public TBase, public CDwmImpl > { public: CDwmWindowT(HWND hWnd = NULL) : TBase(hWnd) { } CDwmWindowT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } }; typedef CDwmWindowT CDwmWindow; /////////////////////////////////////////////////////////////////////////////// // CDwmThumbnail - provides DWM thumbnail support template class CDwmThumbnailT : public TBase { public: // Data members HTHUMBNAIL m_hThumbnail; // Constructor CDwmThumbnailT(HTHUMBNAIL hThumbnail = NULL) : m_hThumbnail(hThumbnail) { } ~CDwmThumbnailT() { if(t_bManaged && (m_hThumbnail != NULL)) Unregister(); } // Operations CDwmThumbnailT& operator =(HTHUMBNAIL hThumbnail) { Attach(hThumbnail); return *this; } void Attach(HTHUMBNAIL hThumbnailNew) { if(t_bManaged && m_hThumbnail != NULL && m_hThumbnail != hThumbnailNew) Unregister(); m_hThumbnail = hThumbnailNew; } HTHUMBNAIL Detach() { HTHUMBNAIL hThumbnail = m_hThumbnail; m_hThumbnail = NULL; return hThumbnail; } HRESULT Register(HWND hwndDestination, HWND hwndSource) { ATLASSERT(::IsWindow(hwndDestination)); ATLASSERT(::IsWindow(hwndSource)); ATLASSERT(m_hThumbnail==NULL); if(!IsDwmSupported()) return E_NOTIMPL; return ::DwmRegisterThumbnail(hwndDestination, hwndSource, &m_hThumbnail); } HRESULT Unregister() { if(!IsDwmSupported()) return E_NOTIMPL; if(m_hThumbnail == NULL) return S_FALSE; HRESULT Hr = ::DwmUnregisterThumbnail(m_hThumbnail); if(SUCCEEDED(Hr)) m_hThumbnail = NULL; return Hr; } operator HTHUMBNAIL() const { return m_hThumbnail; } bool IsNull() const { return (m_hThumbnail == NULL); } HRESULT UpdateProperties(const DWM_THUMBNAIL_PROPERTIES* ptnProperties) { if(!IsDwmSupported()) return E_NOTIMPL; ATLASSERT(m_hThumbnail != NULL); return ::DwmUpdateThumbnailProperties(m_hThumbnail, ptnProperties); } // Attributes HRESULT QuerySourceSize(PSIZE pSize) { if(!IsDwmSupported()) return E_NOTIMPL; ATLASSERT(m_hThumbnail != NULL); return ::DwmQueryThumbnailSourceSize(m_hThumbnail, pSize); } }; typedef CDwmThumbnailT CDwmThumbnail; typedef CDwmThumbnailT CDwmThumbnailHandle; #ifdef __ATLTHEME_H__ /////////////////////////////////////////////////////////////////////////////// // CAeroControlImpl - Base class for controls on Glass template class CAeroControlImpl : public CThemeImpl, public CBufferedPaintImpl, public ATL::CWindowImpl { public: typedef CThemeImpl _themeClass; typedef CBufferedPaintImpl _baseClass; typedef ATL::CWindowImpl _windowClass; CAeroControlImpl() { m_PaintParams.dwFlags = BPPF_ERASE; } static LPCWSTR GetThemeName() { #ifdef _UNICODE return TBase::GetWndClassName(); #else ATLASSERT(!_T("Return UNICODE string of window classname / theme class")); return NULL; #endif // _UNICODE } // Message map and handlers BEGIN_MSG_MAP(CAeroControlImpl) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_ACTIVATE, OnActivate) CHAIN_MSG_MAP(_themeClass) CHAIN_MSG_MAP(_baseClass) END_MSG_MAP() LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); pT->Init(); bHandled = FALSE; return 0; } LRESULT OnActivate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(IsThemingSupported()) Invalidate(FALSE); bHandled = FALSE; return 0; } // Operations BOOL SubclassWindow(HWND hWnd) { ATLASSERT(m_hWnd == NULL); ATLASSERT(::IsWindow(hWnd)); BOOL bRet = _windowClass::SubclassWindow(hWnd); if(bRet) { T* pT = static_cast(this); pT->Init(); } return bRet; } // Implementation LRESULT DefWindowProc() { const ATL::_ATL_MSG* pMsg = m_pCurrentMsg; LRESULT lRes = 0; if(pMsg != NULL) lRes = DefWindowProc(pMsg->message, pMsg->wParam, pMsg->lParam); return lRes; } LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) { T* pT = static_cast(this); LRESULT lRes = 0; if(::DwmDefWindowProc(pT->m_hWnd, uMsg, wParam, lParam, &lRes) != FALSE) return lRes; return _windowClass::DefWindowProc(uMsg, wParam, lParam); } void DoBufferedPaint(HDC hDC, RECT& rcPaint) { T* pT = static_cast(this); HDC hDCPaint = NULL; RECT rcClient = { 0 }; GetClientRect(&rcClient); m_BufferedPaint.Begin(hDC, &rcClient, m_dwFormat, &m_PaintParams, &hDCPaint); ATLASSERT(hDCPaint != NULL); pT->DoAeroPaint(hDCPaint, rcClient, rcPaint); m_BufferedPaint.End(); } void DoPaint(HDC /*hdc*/, RECT& /*rcClient*/) { DefWindowProc(); } // Overridables void Init() { T* pT = static_cast(this); pT; // avoid level 4 warning SetThemeClassList(pT->GetThemeName()); if(m_lpstrThemeClassList != NULL) OpenThemeData(); } void DoAeroPaint(HDC hDC, RECT& /*rcClient*/, RECT& rcPaint) { DefWindowProc(WM_PAINT, (WPARAM) hDC, 0L); m_BufferedPaint.MakeOpaque(&rcPaint); } }; #endif // __ATLTHEME_H__ }; // namespace WTL #endif // __ATLDWM_H__ ================================================ FILE: src/Setup/wtl90/atlfind.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLFIND_H__ #define __ATLFIND_H__ #pragma once #ifdef _WIN32_WCE #error atlfind.h is not supported on Windows CE #endif #ifndef __ATLCTRLS_H__ #error atlfind.h requires atlctrls.h to be included first #endif #ifndef __ATLDLGS_H__ #error atlfind.h requires atldlgs.h to be included first #endif #if !((defined(__ATLMISC_H__) && defined(_WTL_USE_CSTRING)) || defined(__ATLSTR_H__)) #error atlfind.h requires CString (either from ATL's atlstr.h or WTL's atlmisc.h with _WTL_USE_CSTRING) #endif /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CEditFindReplaceImplBase // CEditFindReplaceImpl // CRichEditFindReplaceImpl namespace WTL { /////////////////////////////////////////////////////////////////////////////// // CEditFindReplaceImplBase - Base class for mixin classes that // help implement Find/Replace for CEdit or CRichEditCtrl based window classes. template class CEditFindReplaceImplBase { protected: // Typedefs typedef CEditFindReplaceImplBase thisClass; // Data members TFindReplaceDialog* m_pFindReplaceDialog; _CSTRING_NS::CString m_sFindNext, m_sReplaceWith; BOOL m_bFindOnly, m_bFirstSearch, m_bMatchCase, m_bWholeWord, m_bFindDown; LONG m_nInitialSearchPos; HCURSOR m_hOldCursor; // Enumerations enum TranslationTextItem { eText_OnReplaceAllMessage = 0, eText_OnReplaceAllTitle = 1, eText_OnTextNotFoundMessage = 2, eText_OnTextNotFoundTitle = 3 }; public: // Constructors CEditFindReplaceImplBase() : m_pFindReplaceDialog(NULL), m_bFindOnly(TRUE), m_bFirstSearch(TRUE), m_bMatchCase(FALSE), m_bWholeWord(FALSE), m_bFindDown(TRUE), m_nInitialSearchPos(0), m_hOldCursor(NULL) { } // Message Handlers BEGIN_MSG_MAP(thisClass) ALT_MSG_MAP(1) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) MESSAGE_HANDLER(TFindReplaceDialog::GetFindReplaceMsg(), OnFindReplaceCmd) COMMAND_ID_HANDLER(ID_EDIT_FIND, OnEditFind) COMMAND_ID_HANDLER(ID_EDIT_REPEAT, OnEditRepeat) COMMAND_ID_HANDLER(ID_EDIT_REPLACE, OnEditReplace) END_MSG_MAP() LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(m_pFindReplaceDialog != NULL) { m_pFindReplaceDialog->SendMessage(WM_CLOSE); ATLASSERT(m_pFindReplaceDialog == NULL); } bHandled = FALSE; return 0; } LRESULT OnFindReplaceCmd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/) { T* pT = static_cast(this); TFindReplaceDialog* pDialog = TFindReplaceDialog::GetNotifier(lParam); if(pDialog == NULL) { ATLASSERT(FALSE); ::MessageBeep(MB_ICONERROR); return 1; } ATLASSERT(pDialog == m_pFindReplaceDialog); LPFINDREPLACE findReplace = (LPFINDREPLACE)lParam; if((m_pFindReplaceDialog != NULL) && (findReplace != NULL)) { if(pDialog->FindNext()) { pT->OnFindNext(pDialog->GetFindString(), pDialog->SearchDown(), pDialog->MatchCase(), pDialog->MatchWholeWord()); } else if(pDialog->ReplaceCurrent()) { pT->OnReplaceSel(pDialog->GetFindString(), pDialog->SearchDown(), pDialog->MatchCase(), pDialog->MatchWholeWord(), pDialog->GetReplaceString()); } else if(pDialog->ReplaceAll()) { pT->OnReplaceAll(pDialog->GetFindString(), pDialog->GetReplaceString(), pDialog->MatchCase(), pDialog->MatchWholeWord()); } else if(pDialog->IsTerminating()) { // Dialog is going away (but hasn't gone away yet) // OnFinalMessage will "delete this" pT->OnTerminatingFindReplaceDialog(m_pFindReplaceDialog); m_pFindReplaceDialog = NULL; } } return 0; } LRESULT OnEditFind(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->FindReplace(TRUE); return 0; } LRESULT OnEditRepeat(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); // If the user is holding down SHIFT when hitting F3, we'll // search in reverse. Otherwise, we'll search forward. // (be sure to have an accelerator mapped to ID_EDIT_REPEAT // for both F3 and Shift+F3) m_bFindDown = !((::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000); if(m_sFindNext.IsEmpty()) { pT->FindReplace(TRUE); } else { if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)) pT->TextNotFound(m_sFindNext); } return 0; } LRESULT OnEditReplace(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& bHandled) { T* pT = static_cast(this); DWORD style = pT->GetStyle(); if((style & ES_READONLY) != ES_READONLY) { pT->FindReplace(FALSE); } else { // Don't allow replace when the edit control is read only bHandled = FALSE; } return 0; } // Operations (overrideable) TFindReplaceDialog* CreateFindReplaceDialog(BOOL bFindOnly, // TRUE for Find, FALSE for FindReplace LPCTSTR lpszFindWhat, LPCTSTR lpszReplaceWith = NULL, DWORD dwFlags = FR_DOWN, HWND hWndParent = NULL) { // You can override all of this in a derived class TFindReplaceDialog* findReplaceDialog = new TFindReplaceDialog(); if(findReplaceDialog == NULL) { ::MessageBeep(MB_ICONHAND); } else { HWND hWndFindReplace = findReplaceDialog->Create(bFindOnly, lpszFindWhat, lpszReplaceWith, dwFlags, hWndParent); if(hWndFindReplace == NULL) { delete findReplaceDialog; findReplaceDialog = NULL; } else { findReplaceDialog->SetActiveWindow(); findReplaceDialog->ShowWindow(SW_SHOW); } } return findReplaceDialog; } void AdjustDialogPosition(HWND hWndDialog) { ATLASSERT((hWndDialog != NULL) && ::IsWindow(hWndDialog)); T* pT = static_cast(this); LONG nStartChar = 0, nEndChar = 0; // Send EM_GETSEL so we can use both Edit and RichEdit // (CEdit::GetSel uses int&, and CRichEditCtrlT::GetSel uses LONG&) ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar); POINT point = pT->PosFromChar(nStartChar); ::ClientToScreen(pT->GetParent(), &point); CRect rect; ::GetWindowRect(hWndDialog, &rect); if(rect.PtInRect(point)) { if(point.y > rect.Height()) { rect.OffsetRect(0, point.y - rect.bottom - 20); } else { int nVertExt = GetSystemMetrics(SM_CYSCREEN); if(point.y + rect.Height() < nVertExt) rect.OffsetRect(0, 40 + point.y - rect.top); } ::MoveWindow(hWndDialog, rect.left, rect.top, rect.Width(), rect.Height(), TRUE); } } DWORD GetFindReplaceDialogFlags(void) const { DWORD dwFlags = 0; if(m_bFindDown) dwFlags |= FR_DOWN; if(m_bMatchCase) dwFlags |= FR_MATCHCASE; if(m_bWholeWord) dwFlags |= FR_WHOLEWORD; return dwFlags; } void FindReplace(BOOL bFindOnly) { T* pT = static_cast(this); m_bFirstSearch = TRUE; if(m_pFindReplaceDialog != NULL) { if(m_bFindOnly == bFindOnly) { m_pFindReplaceDialog->SetActiveWindow(); m_pFindReplaceDialog->ShowWindow(SW_SHOW); return; } else { m_pFindReplaceDialog->SendMessage(WM_CLOSE); ATLASSERT(m_pFindReplaceDialog == NULL); } } ATLASSERT(m_pFindReplaceDialog == NULL); _CSTRING_NS::CString findNext; pT->GetSelText(findNext); // if selection is empty or spans multiple lines use old find text if(findNext.IsEmpty() || (findNext.FindOneOf(_T("\n\r")) != -1)) findNext = m_sFindNext; _CSTRING_NS::CString replaceWith = m_sReplaceWith; DWORD dwFlags = pT->GetFindReplaceDialogFlags(); m_pFindReplaceDialog = pT->CreateFindReplaceDialog(bFindOnly, findNext, replaceWith, dwFlags, pT->operator HWND()); ATLASSERT(m_pFindReplaceDialog != NULL); if(m_pFindReplaceDialog != NULL) m_bFindOnly = bFindOnly; } BOOL SameAsSelected(LPCTSTR lpszCompare, BOOL bMatchCase, BOOL /*bWholeWord*/) { T* pT = static_cast(this); // check length first size_t nLen = lstrlen(lpszCompare); LONG nStartChar = 0, nEndChar = 0; // Send EM_GETSEL so we can use both Edit and RichEdit // (CEdit::GetSel uses int&, and CRichEditCtrlT::GetSel uses LONG&) ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar); if(nLen != (size_t)(nEndChar - nStartChar)) return FALSE; // length is the same, check contents _CSTRING_NS::CString selectedText; pT->GetSelText(selectedText); return (bMatchCase && selectedText.Compare(lpszCompare) == 0) || (!bMatchCase && selectedText.CompareNoCase(lpszCompare) == 0); } void TextNotFound(LPCTSTR lpszFind) { T* pT = static_cast(this); m_bFirstSearch = TRUE; pT->OnTextNotFound(lpszFind); } _CSTRING_NS::CString GetTranslationText(enum TranslationTextItem eItem) const { _CSTRING_NS::CString text; switch(eItem) { case eText_OnReplaceAllMessage: text = _T("Replaced %d occurances of \"%s\" with \"%s\""); break; case eText_OnReplaceAllTitle: text = _T("Replace All"); break; case eText_OnTextNotFoundMessage: text = _T("Unable to find the text \"%s\""); break; case eText_OnTextNotFoundTitle: text = _T("Text not found"); break; } return text; } // Overrideable Handlers void OnFindNext(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord) { T* pT = static_cast(this); m_sFindNext = lpszFind; m_bMatchCase = bMatchCase; m_bWholeWord = bWholeWord; m_bFindDown = bFindDown; if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)) pT->TextNotFound(m_sFindNext); else pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND()); } void OnReplaceSel(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord, LPCTSTR lpszReplace) { T* pT = static_cast(this); m_sFindNext = lpszFind; m_sReplaceWith = lpszReplace; m_bMatchCase = bMatchCase; m_bWholeWord = bWholeWord; m_bFindDown = bFindDown; if(pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord)) pT->ReplaceSel(m_sReplaceWith); if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)) pT->TextNotFound(m_sFindNext); else pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND()); } void OnReplaceAll(LPCTSTR lpszFind, LPCTSTR lpszReplace, BOOL bMatchCase, BOOL bWholeWord) { T* pT = static_cast(this); m_sFindNext = lpszFind; m_sReplaceWith = lpszReplace; m_bMatchCase = bMatchCase; m_bWholeWord = bWholeWord; m_bFindDown = TRUE; // no selection or different than what looking for if(!pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord)) { if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)) { pT->TextNotFound(m_sFindNext); return; } } pT->OnReplaceAllCoreBegin(); int replaceCount=0; do { ++replaceCount; pT->ReplaceSel(m_sReplaceWith); } while(pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)); pT->OnReplaceAllCoreEnd(replaceCount); } void OnReplaceAllCoreBegin() { T* pT = static_cast(this); m_hOldCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT)); pT->HideSelection(TRUE, FALSE); } void OnReplaceAllCoreEnd(int replaceCount) { T* pT = static_cast(this); pT->HideSelection(FALSE, FALSE); ::SetCursor(m_hOldCursor); _CSTRING_NS::CString message = pT->GetTranslationText(eText_OnReplaceAllMessage); if(message.GetLength() > 0) { _CSTRING_NS::CString formattedMessage; formattedMessage.Format(message, replaceCount, m_sFindNext, m_sReplaceWith); if(m_pFindReplaceDialog != NULL) { m_pFindReplaceDialog->MessageBox(formattedMessage, pT->GetTranslationText(eText_OnReplaceAllTitle), MB_OK | MB_ICONINFORMATION | MB_APPLMODAL); } else { pT->MessageBox(formattedMessage, pT->GetTranslationText(eText_OnReplaceAllTitle), MB_OK | MB_ICONINFORMATION | MB_APPLMODAL); } } } void OnTextNotFound(LPCTSTR lpszFind) { T* pT = static_cast(this); _CSTRING_NS::CString message = pT->GetTranslationText(eText_OnTextNotFoundMessage); if(message.GetLength() > 0) { _CSTRING_NS::CString formattedMessage; formattedMessage.Format(message, lpszFind); if(m_pFindReplaceDialog != NULL) { m_pFindReplaceDialog->MessageBox(formattedMessage, pT->GetTranslationText(eText_OnTextNotFoundTitle), MB_OK | MB_ICONINFORMATION | MB_APPLMODAL); } else { pT->MessageBox(formattedMessage, pT->GetTranslationText(eText_OnTextNotFoundTitle), MB_OK | MB_ICONINFORMATION | MB_APPLMODAL); } } else { ::MessageBeep(MB_ICONHAND); } } void OnTerminatingFindReplaceDialog(TFindReplaceDialog*& /*findReplaceDialog*/) { } }; /////////////////////////////////////////////////////////////////////////////// // CEditFindReplaceImpl - Mixin class for implementing Find/Replace for CEdit // based window classes. // Chain to CEditFindReplaceImpl message map. Your class must also derive from CEdit. // Example: // class CMyEdit : public CWindowImpl, // public CEditFindReplaceImpl // { // public: // BEGIN_MSG_MAP(CMyEdit) // // your handlers... // CHAIN_MSG_MAP_ALT(CEditFindReplaceImpl, 1) // END_MSG_MAP() // // other stuff... // }; template class CEditFindReplaceImpl : public CEditFindReplaceImplBase { protected: typedef CEditFindReplaceImpl thisClass; typedef CEditFindReplaceImplBase baseClass; // Data members LPTSTR m_pShadowBuffer; // Special shadow buffer only used in some cases. UINT m_nShadowSize; int m_bShadowBufferNeeded; // TRUE, FALSE, < 0 => Need to check public: // Constructors CEditFindReplaceImpl() : m_pShadowBuffer(NULL), m_nShadowSize(0), m_bShadowBufferNeeded(-1) { } virtual ~CEditFindReplaceImpl() { if(m_pShadowBuffer != NULL) { delete [] m_pShadowBuffer; m_pShadowBuffer = NULL; } } // Message Handlers BEGIN_MSG_MAP(thisClass) ALT_MSG_MAP(1) CHAIN_MSG_MAP_ALT(baseClass, 1) END_MSG_MAP() // Operations // Supported only for RichEdit, so this does nothing for Edit void HideSelection(BOOL /*bHide*/ = TRUE, BOOL /*bChangeStyle*/ = FALSE) { } // Operations (overrideable) BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE) { T* pT = static_cast(this); ATLASSERT(lpszFind != NULL); ATLASSERT(*lpszFind != _T('\0')); UINT nLen = pT->GetBufferLength(); int nStartChar = 0, nEndChar = 0; pT->GetSel(nStartChar, nEndChar); UINT nStart = nStartChar; int iDir = bFindDown ? +1 : -1; // can't find a match before the first character if((nStart == 0) && (iDir < 0)) return FALSE; LPCTSTR lpszText = pT->LockBuffer(); bool isDBCS = false; #ifdef _MBCS CPINFO info = { 0 }; ::GetCPInfo(::GetOEMCP(), &info); isDBCS = (info.MaxCharSize > 1); #endif if(iDir < 0) { // always go back one for search backwards nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart)); } else if((nStartChar != nEndChar) && (pT->SameAsSelected(lpszFind, bMatchCase, bWholeWord))) { // easy to go backward/forward with SBCS #ifndef _UNICODE if(::IsDBCSLeadByte(lpszText[nStart])) nStart++; #endif nStart += iDir; } // handle search with nStart past end of buffer UINT nLenFind = ::lstrlen(lpszFind); if((nStart + nLenFind - 1) >= nLen) { if((iDir < 0) && (nLen >= nLenFind)) { if(isDBCS) { // walk back to previous character n times nStart = nLen; int n = nLenFind; while(n--) { nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart)); } } else { // single-byte character set is easy and fast nStart = nLen - nLenFind; } ATLASSERT((nStart + nLenFind - 1) <= nLen); } else { pT->UnlockBuffer(); return FALSE; } } // start the search at nStart LPCTSTR lpsz = lpszText + nStart; typedef int (WINAPI* CompareProc)(LPCTSTR str1, LPCTSTR str2); CompareProc pfnCompare = bMatchCase ? lstrcmp : lstrcmpi; if(isDBCS) { // double-byte string search LPCTSTR lpszStop = NULL; if(iDir > 0) { // start at current and find _first_ occurrance lpszStop = lpszText + nLen - nLenFind + 1; } else { // start at top and find _last_ occurrance lpszStop = lpsz; lpsz = lpszText; } LPCTSTR lpszFound = NULL; while(lpsz <= lpszStop) { #ifndef _UNICODE if(!bMatchCase || (*lpsz == *lpszFind && (!::IsDBCSLeadByte(*lpsz) || lpsz[1] == lpszFind[1]))) #else if(!bMatchCase || (*lpsz == *lpszFind && lpsz[1] == lpszFind[1])) #endif { LPTSTR lpch = (LPTSTR)(lpsz + nLenFind); TCHAR chSave = *lpch; *lpch = _T('\0'); int nResult = (*pfnCompare)(lpsz, lpszFind); *lpch = chSave; if(nResult == 0) { lpszFound = lpsz; if(iDir > 0) break; } } lpsz = ::CharNext(lpsz); } pT->UnlockBuffer(); if(lpszFound != NULL) { int n = (int)(lpszFound - lpszText); pT->SetSel(n, n + nLenFind); return TRUE; } } else { // single-byte string search UINT nCompare = 0; if(iDir < 0) nCompare = (UINT)(lpsz - lpszText) + 1; else nCompare = nLen - (UINT)(lpsz - lpszText) - nLenFind + 1; while(nCompare > 0) { ATLASSERT(lpsz >= lpszText); ATLASSERT((lpsz + nLenFind - 1) <= (lpszText + nLen - 1)); LPSTR lpch = (LPSTR)(lpsz + nLenFind); char chSave = *lpch; *lpch = '\0'; int nResult = (*pfnCompare)(lpsz, lpszFind); *lpch = chSave; if(nResult == 0) { pT->UnlockBuffer(); int n = (int)(lpsz - lpszText); pT->SetSel(n, n + nLenFind); return TRUE; } // restore character at end of search *lpch = chSave; // move on to next substring nCompare--; lpsz += iDir; } pT->UnlockBuffer(); } return FALSE; } LPCTSTR LockBuffer() const { const T* pT = static_cast(this); ATLASSERT(pT->m_hWnd != NULL); BOOL useShadowBuffer = pT->UseShadowBuffer(); if(useShadowBuffer) { if((m_pShadowBuffer == NULL) || pT->GetModify()) { ATLASSERT((m_pShadowBuffer != NULL) || (m_nShadowSize == 0)); UINT nSize = pT->GetWindowTextLength() + 1; if(nSize > m_nShadowSize) { // need more room for shadow buffer T* pThisNoConst = const_cast(pT); delete[] m_pShadowBuffer; pThisNoConst->m_pShadowBuffer = NULL; pThisNoConst->m_nShadowSize = 0; pThisNoConst->m_pShadowBuffer = new TCHAR[nSize]; pThisNoConst->m_nShadowSize = nSize; } // update the shadow buffer with GetWindowText ATLASSERT(m_nShadowSize >= nSize); ATLASSERT(m_pShadowBuffer != NULL); pT->GetWindowText(m_pShadowBuffer, nSize); } return m_pShadowBuffer; } HLOCAL hLocal = pT->GetHandle(); ATLASSERT(hLocal != NULL); LPCTSTR lpszText = (LPCTSTR)::LocalLock(hLocal); ATLASSERT(lpszText != NULL); return lpszText; } void UnlockBuffer() const { const T* pT = static_cast(this); ATLASSERT(pT->m_hWnd != NULL); BOOL useShadowBuffer = pT->UseShadowBuffer(); if(!useShadowBuffer) { HLOCAL hLocal = pT->GetHandle(); ATLASSERT(hLocal != NULL); ::LocalUnlock(hLocal); } } UINT GetBufferLength() const { const T* pT = static_cast(this); ATLASSERT(pT->m_hWnd != NULL); UINT nLen = 0; LPCTSTR lpszText = pT->LockBuffer(); if(lpszText != NULL) nLen = ::lstrlen(lpszText); pT->UnlockBuffer(); return nLen; } LONG EndOfLine(LPCTSTR lpszText, UINT nLen, UINT nIndex) const { LPCTSTR lpsz = lpszText + nIndex; LPCTSTR lpszStop = lpszText + nLen; while(lpsz < lpszStop && *lpsz != _T('\r')) ++lpsz; return LONG(lpsz - lpszText); } LONG GetSelText(_CSTRING_NS::CString& strText) const { const T* pT = static_cast(this); int nStartChar = 0, nEndChar = 0; pT->GetSel(nStartChar, nEndChar); ATLASSERT((UINT)nEndChar <= pT->GetBufferLength()); LPCTSTR lpszText = pT->LockBuffer(); LONG nLen = pT->EndOfLine(lpszText, nEndChar, nStartChar) - nStartChar; SecureHelper::memcpy_x(strText.GetBuffer(nLen), nLen * sizeof(TCHAR), lpszText + nStartChar, nLen * sizeof(TCHAR)); strText.ReleaseBuffer(nLen); pT->UnlockBuffer(); return nLen; } BOOL UseShadowBuffer(void) const { const T* pT = static_cast(this); if(pT->m_bShadowBufferNeeded < 0) { T* pThisNoConst = const_cast(pT); #ifdef _versionhelpers_H_INCLUDED_ OSVERSIONINFOEX ovi = { sizeof(OSVERSIONINFOEX) }; ovi.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS; DWORDLONG const dwlConditionMask = ::VerSetConditionMask(0, VER_PLATFORMID, VER_EQUAL); bool bWin9x = (::VerifyVersionInfo(&ovi, VER_PLATFORMID, dwlConditionMask) != FALSE); #else // !_versionhelpers_H_INCLUDED_ OSVERSIONINFO ovi = { sizeof(OSVERSIONINFO) }; ::GetVersionEx(&ovi); bool bWin9x = (ovi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); #endif // _versionhelpers_H_INCLUDED_ if(bWin9x) { // Windows 95, 98, ME // Under Win9x, it is necessary to maintain a shadow buffer. // It is only updated when the control contents have been changed. pThisNoConst->m_bShadowBufferNeeded = TRUE; } else { // Windows NT, 2000, XP, etc. pThisNoConst->m_bShadowBufferNeeded = FALSE; #ifndef _UNICODE // On Windows XP (or later), if common controls version 6 is in use // (such as via theming), then EM_GETHANDLE will always return a UNICODE string. // If theming is enabled and Common Controls version 6 is in use, // you're really not suppose to superclass or subclass common controls // with an ANSI windows procedure (so its best to only theme if you use UNICODE). // Using a shadow buffer uses GetWindowText instead, so it solves // this problem for us (although it makes it a little less efficient). #ifdef _versionhelpers_H_INCLUDED_ if(::IsWindowsXPOrGreater()) #else // !_versionhelpers_H_INCLUDED_ if ((ovi.dwMajorVersion == 5 && ovi.dwMinorVersion >= 1) || (ovi.dwMajorVersion > 5)) #endif // _versionhelpers_H_INCLUDED_ { // We use DLLVERSIONINFO_private so we don't have to depend on shlwapi.h typedef struct _DLLVERSIONINFO_private { DWORD cbSize; DWORD dwMajorVersion; DWORD dwMinorVersion; DWORD dwBuildNumber; DWORD dwPlatformID; } DLLVERSIONINFO_private; HMODULE hModule = ::LoadLibrary("comctl32.dll"); if(hModule != NULL) { typedef HRESULT (CALLBACK *LPFN_DllGetVersion)(DLLVERSIONINFO_private *); LPFN_DllGetVersion fnDllGetVersion = (LPFN_DllGetVersion)::GetProcAddress(hModule, "DllGetVersion"); if(fnDllGetVersion != NULL) { DLLVERSIONINFO_private version = { sizeof(DLLVERSIONINFO_private) }; if(SUCCEEDED(fnDllGetVersion(&version))) { if(version.dwMajorVersion >= 6) { pThisNoConst->m_bShadowBufferNeeded = TRUE; ATLTRACE2(atlTraceUI, 0, _T("Warning: You have compiled for MBCS/ANSI but are using common controls version 6 or later (likely through a manifest file).\r\n")); ATLTRACE2(atlTraceUI, 0, _T("If you use common controls version 6 or later, you should only do so for UNICODE builds.\r\n")); } } } ::FreeLibrary(hModule); hModule = NULL; } } #endif // !_UNICODE } } return (pT->m_bShadowBufferNeeded != FALSE); } }; /////////////////////////////////////////////////////////////////////////////// // CRichEditFindReplaceImpl - Mixin class for implementing Find/Replace for CRichEditCtrl // based window classes. // Chain to CRichEditFindReplaceImpl message map. Your class must also derive from CRichEditCtrl. // Example: // class CMyRichEdit : public CWindowImpl, // public CRichEditFindReplaceImpl // { // public: // BEGIN_MSG_MAP(CMyRichEdit) // // your handlers... // CHAIN_MSG_MAP_ALT(CRichEditFindReplaceImpl, 1) // END_MSG_MAP() // // other stuff... // }; template class CRichEditFindReplaceImpl : public CEditFindReplaceImplBase { protected: typedef CRichEditFindReplaceImpl thisClass; typedef CEditFindReplaceImplBase baseClass; public: BEGIN_MSG_MAP(thisClass) ALT_MSG_MAP(1) CHAIN_MSG_MAP_ALT(baseClass, 1) END_MSG_MAP() // Operations (overrideable) BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE) { T* pT = static_cast(this); ATLASSERT(lpszFind != NULL); FINDTEXTEX ft = { 0 }; pT->GetSel(ft.chrg); if(m_bFirstSearch) { if(bFindDown) m_nInitialSearchPos = ft.chrg.cpMin; else m_nInitialSearchPos = ft.chrg.cpMax; m_bFirstSearch = FALSE; } #if (_RICHEDIT_VER >= 0x0200) ft.lpstrText = (LPTSTR)lpszFind; #else // !(_RICHEDIT_VER >= 0x0200) USES_CONVERSION; ft.lpstrText = T2A((LPTSTR)lpszFind); #endif // !(_RICHEDIT_VER >= 0x0200) if(ft.chrg.cpMin != ft.chrg.cpMax) // i.e. there is a selection { if(bFindDown) { ft.chrg.cpMin++; } else { // won't wraparound backwards ft.chrg.cpMin = __max(ft.chrg.cpMin, 0); } } DWORD dwFlags = bMatchCase ? FR_MATCHCASE : 0; dwFlags |= bWholeWord ? FR_WHOLEWORD : 0; ft.chrg.cpMax = pT->GetTextLength() + m_nInitialSearchPos; if(bFindDown) { if(m_nInitialSearchPos >= 0) ft.chrg.cpMax = pT->GetTextLength(); dwFlags |= FR_DOWN; ATLASSERT(ft.chrg.cpMax >= ft.chrg.cpMin); } else { if(m_nInitialSearchPos >= 0) ft.chrg.cpMax = 0; dwFlags &= ~FR_DOWN; ATLASSERT(ft.chrg.cpMax <= ft.chrg.cpMin); } BOOL bRet = FALSE; if(pT->FindAndSelect(dwFlags, ft) != -1) { bRet = TRUE; // we found the text } else if(m_nInitialSearchPos > 0) { // if the original starting point was not the beginning // of the buffer and we haven't already been here if(bFindDown) { ft.chrg.cpMin = 0; ft.chrg.cpMax = m_nInitialSearchPos; } else { ft.chrg.cpMin = pT->GetTextLength(); ft.chrg.cpMax = m_nInitialSearchPos; } m_nInitialSearchPos = m_nInitialSearchPos - pT->GetTextLength(); bRet = (pT->FindAndSelect(dwFlags, ft) != -1) ? TRUE : FALSE; } return bRet; } long FindAndSelect(DWORD dwFlags, FINDTEXTEX& ft) { T* pT = static_cast(this); LONG index = pT->FindText(dwFlags, ft); if(index != -1) // i.e. we found something pT->SetSel(ft.chrgText); return index; } }; }; // namespace WTL #endif // __ATLFIND_H__ ================================================ FILE: src/Setup/wtl90/atlframe.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLFRAME_H__ #define __ATLFRAME_H__ #pragma once #ifndef __ATLAPP_H__ #error atlframe.h requires atlapp.h to be included first #endif #ifndef __ATLWIN_H__ #error atlframe.h requires atlwin.h to be included first #endif /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CFrameWindowImpl // CMDIWindow // CMDIFrameWindowImpl // CMDIChildWindowImpl // COwnerDraw // CUpdateUIBase // CUpdateUI // CDynamicUpdateUI // CAutoUpdateUI // CDialogResize // CDoubleBufferImpl // CDoubleBufferWindowImpl // // Global functions: // AtlCreateSimpleToolBar() namespace WTL { /////////////////////////////////////////////////////////////////////////////// // CFrameWndClassInfo - Manages frame window Windows class information class CFrameWndClassInfo { public: #ifndef _WIN32_WCE enum { cchAutoName = 5 + sizeof(void*) * 2 }; // sizeof(void*) * 2 is the number of digits %p outputs WNDCLASSEX m_wc; #else // CE specific enum { cchAutoName = MAX_PATH }; // MAX_PATH because this can be set in the wizard generated CMainFrame::ActivatePreviousInstance to a user defined string. WNDCLASS m_wc; #endif // !_WIN32_WCE LPCTSTR m_lpszOrigName; WNDPROC pWndProc; LPCTSTR m_lpszCursorID; BOOL m_bSystemCursor; ATOM m_atom; TCHAR m_szAutoName[cchAutoName]; UINT m_uCommonResourceID; #ifndef _WIN32_WCE ATOM Register(WNDPROC* pProc) { if (m_atom == 0) { CWindowCreateCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CFrameWndClassInfo::Register.\n")); ATLASSERT(FALSE); return 0; } if(m_atom == 0) { HINSTANCE hInst = ModuleHelper::GetModuleInstance(); if (m_lpszOrigName != NULL) { ATLASSERT(pProc != NULL); LPCTSTR lpsz = m_wc.lpszClassName; WNDPROC proc = m_wc.lpfnWndProc; WNDCLASSEX wc = { sizeof(WNDCLASSEX) }; // try process local class first if(!::GetClassInfoEx(ModuleHelper::GetModuleInstance(), m_lpszOrigName, &wc)) { // try global class if(!::GetClassInfoEx(NULL, m_lpszOrigName, &wc)) { lock.Unlock(); return 0; } } m_wc = wc; pWndProc = m_wc.lpfnWndProc; m_wc.lpszClassName = lpsz; m_wc.lpfnWndProc = proc; } else { m_wc.hCursor = ::LoadCursor(m_bSystemCursor ? NULL : hInst, m_lpszCursorID); } m_wc.hInstance = hInst; m_wc.style &= ~CS_GLOBALCLASS; // we don't register global classes if (m_wc.lpszClassName == NULL) { #if (_WIN32_WINNT >= 0x0500) || defined(_WIN64) SecureHelper::wsprintf_x(m_szAutoName, cchAutoName, _T("ATL:%p"), &m_wc); #else // !((_WIN32_WINNT >= 0x0500) || defined(_WIN64)) SecureHelper::wsprintf_x(m_szAutoName, cchAutoName, _T("ATL:%8.8X"), (DWORD_PTR)&m_wc); #endif // !((_WIN32_WINNT >= 0x0500) || defined(_WIN64)) m_wc.lpszClassName = m_szAutoName; } WNDCLASSEX wcTemp = m_wc; m_atom = (ATOM)::GetClassInfoEx(m_wc.hInstance, m_wc.lpszClassName, &wcTemp); if (m_atom == 0) { if(m_uCommonResourceID != 0) // use it if not zero { m_wc.hIcon = (HICON)::LoadImage(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(m_uCommonResourceID), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR); m_wc.hIconSm = (HICON)::LoadImage(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(m_uCommonResourceID), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); } m_atom = ::RegisterClassEx(&m_wc); } } lock.Unlock(); } if (m_lpszOrigName != NULL) { ATLASSERT(pProc != NULL); ATLASSERT(pWndProc != NULL); *pProc = pWndProc; } return m_atom; } #else // CE specific ATOM Register(WNDPROC* pProc) { if (m_atom == 0) { CWindowCreateCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CFrameWndClassInfo::Register.\n")); ATLASSERT(FALSE); return 0; } if(m_atom == 0) { HINSTANCE hInst = ModuleHelper::GetModuleInstance(); if (m_lpszOrigName != NULL) { ATLASSERT(pProc != NULL); LPCTSTR lpsz = m_wc.lpszClassName; WNDPROC proc = m_wc.lpfnWndProc; WNDCLASS wc = { 0 }; // try process local class first if(!::GetClassInfo(ModuleHelper::GetModuleInstance(), m_lpszOrigName, &wc)) { // try global class if(!::GetClassInfo(NULL, m_lpszOrigName, &wc)) { lock.Unlock(); return 0; } } m_wc = wc; pWndProc = m_wc.lpfnWndProc; m_wc.lpszClassName = lpsz; m_wc.lpfnWndProc = proc; } else { #if defined(GWES_CURSOR) || defined(GWES_MCURSOR) m_wc.hCursor = ::LoadCursor(m_bSystemCursor ? NULL : hInst, m_lpszCursorID); #else // !(defined(GWES_CURSOR) || defined(GWES_MCURSOR)) m_wc.hCursor = NULL; #endif // !(defined(GWES_CURSOR) || defined(GWES_MCURSOR)) } m_wc.hInstance = hInst; m_wc.style &= ~CS_GLOBALCLASS; // we don't register global classes if (m_wc.lpszClassName == NULL) { wsprintf(m_szAutoName, _T("ATL:%8.8X"), (DWORD_PTR)&m_wc); m_wc.lpszClassName = m_szAutoName; } WNDCLASS wcTemp = m_wc; m_atom = (ATOM)::GetClassInfo(m_wc.hInstance, m_wc.lpszClassName, &wcTemp); if (m_atom == 0) { if(m_uCommonResourceID != 0) // use it if not zero m_wc.hIcon = (HICON)::LoadImage(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(m_uCommonResourceID), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR); m_atom = ::RegisterClass(&m_wc); } } lock.Unlock(); } if (m_lpszOrigName != NULL) { ATLASSERT(pProc != NULL); ATLASSERT(pWndProc != NULL); *pProc = pWndProc; } return m_atom; } #endif // _WIN32_WCE }; /////////////////////////////////////////////////////////////////////////////// // Macros for declaring frame window WNDCLASS #ifndef _WIN32_WCE #define DECLARE_FRAME_WND_CLASS(WndClassName, uCommonResourceID) \ static WTL::CFrameWndClassInfo& GetWndClassInfo() \ { \ static WTL::CFrameWndClassInfo wc = \ { \ { sizeof(WNDCLASSEX), 0, StartWindowProc, \ 0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName, NULL }, \ NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \ }; \ return wc; \ } #define DECLARE_FRAME_WND_CLASS_EX(WndClassName, uCommonResourceID, style, bkgnd) \ static WTL::CFrameWndClassInfo& GetWndClassInfo() \ { \ static WTL::CFrameWndClassInfo wc = \ { \ { sizeof(WNDCLASSEX), style, StartWindowProc, \ 0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd + 1), NULL, WndClassName, NULL }, \ NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \ }; \ return wc; \ } #define DECLARE_FRAME_WND_SUPERCLASS(WndClassName, OrigWndClassName, uCommonResourceID) \ static WTL::CFrameWndClassInfo& GetWndClassInfo() \ { \ static WTL::CFrameWndClassInfo wc = \ { \ { sizeof(WNDCLASSEX), 0, StartWindowProc, \ 0, 0, NULL, NULL, NULL, NULL, NULL, WndClassName, NULL }, \ OrigWndClassName, NULL, NULL, TRUE, 0, _T(""), uCommonResourceID \ }; \ return wc; \ } #else // CE specific #define DECLARE_FRAME_WND_CLASS(WndClassName, uCommonResourceID) \ static WTL::CFrameWndClassInfo& GetWndClassInfo() \ { \ static WTL::CFrameWndClassInfo wc = \ { \ { 0, StartWindowProc, \ 0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName }, \ NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \ }; \ return wc; \ } #define DECLARE_FRAME_WND_CLASS_EX(WndClassName, uCommonResourceID, style, bkgnd) \ static WTL::CFrameWndClassInfo& GetWndClassInfo() \ { \ static WTL::CFrameWndClassInfo wc = \ { \ { style, StartWindowProc, \ 0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd + 1), NULL, WndClassName }, \ NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \ }; \ return wc; \ } #define DECLARE_FRAME_WND_SUPERCLASS(WndClassName, OrigWndClassName, uCommonResourceID) \ static WTL::CFrameWndClassInfo& GetWndClassInfo() \ { \ static WTL::CFrameWndClassInfo wc = \ { \ { NULL, StartWindowProc, \ 0, 0, NULL, NULL, NULL, NULL, NULL, WndClassName }, \ OrigWndClassName, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \ }; \ return wc; \ } #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CFrameWindowImpl // Client window command chaining macro (only for frame windows) #define CHAIN_CLIENT_COMMANDS() \ if(uMsg == WM_COMMAND && m_hWndClient != NULL) \ ::SendMessage(m_hWndClient, uMsg, wParam, lParam); // standard toolbar styles #define ATL_SIMPLE_TOOLBAR_STYLE \ (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS) // toolbar in a rebar pane #define ATL_SIMPLE_TOOLBAR_PANE_STYLE \ (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT) // standard rebar styles #if (_WIN32_IE >= 0x0400) #define ATL_SIMPLE_REBAR_STYLE \ (WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | RBS_VARHEIGHT | RBS_BANDBORDERS | RBS_AUTOSIZE) #else #define ATL_SIMPLE_REBAR_STYLE \ (WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | RBS_VARHEIGHT | RBS_BANDBORDERS) #endif // !(_WIN32_IE >= 0x0400) // rebar without borders #if (_WIN32_IE >= 0x0400) #define ATL_SIMPLE_REBAR_NOBORDER_STYLE \ (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | RBS_VARHEIGHT | RBS_BANDBORDERS | RBS_AUTOSIZE | CCS_NODIVIDER) #else #define ATL_SIMPLE_REBAR_NOBORDER_STYLE \ (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | RBS_VARHEIGHT | RBS_BANDBORDERS | CCS_NODIVIDER) #endif // !(_WIN32_IE >= 0x0400) // command bar support #if !defined(__ATLCTRLW_H__) && !defined(_WIN32_WCE) #define CBRM_GETCMDBAR (WM_USER + 301) // returns command bar HWND #define CBRM_GETMENU (WM_USER + 302) // returns loaded or attached menu #define CBRM_TRACKPOPUPMENU (WM_USER + 303) // displays a popup menu struct _AtlFrameWnd_CmdBarPopupMenu { int cbSize; HMENU hMenu; UINT uFlags; int x; int y; LPTPMPARAMS lptpm; }; #define CBRPOPUPMENU _AtlFrameWnd_CmdBarPopupMenu #endif // !defined(__ATLCTRLW_H__) && !defined(_WIN32_WCE) template class ATL_NO_VTABLE CFrameWindowImplBase : public ATL::CWindowImplBaseT< TBase, TWinTraits > { public: DECLARE_FRAME_WND_CLASS(NULL, 0) #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) struct _ChevronMenuInfo { HMENU hMenu; LPNMREBARCHEVRON lpnm; bool bCmdBar; }; #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) // Data members HWND m_hWndToolBar; HWND m_hWndStatusBar; HWND m_hWndClient; #ifdef _WIN32_WCE HWND m_hWndCECommandBar; #endif // _WIN32_WCE HACCEL m_hAccel; // Constructor CFrameWindowImplBase() : m_hWndToolBar(NULL), m_hWndStatusBar(NULL), m_hWndClient(NULL), #ifdef _WIN32_WCE m_hWndCECommandBar(NULL), #endif // _WIN32_WCE m_hAccel(NULL) { } // Methods HWND Create(HWND hWndParent, ATL::_U_RECT rect, LPCTSTR szWindowName, DWORD dwStyle, DWORD dwExStyle, ATL::_U_MENUorID MenuOrID, ATOM atom, LPVOID lpCreateParam) { ATLASSERT(m_hWnd == NULL); #if (_ATL_VER >= 0x0800) // Allocate the thunk structure here, where we can fail gracefully. BOOL bRet = m_thunk.Init(NULL, NULL); if(bRet == FALSE) { ::SetLastError(ERROR_OUTOFMEMORY); return NULL; } #endif // (_ATL_VER >= 0x0800) if(atom == 0) return NULL; ModuleHelper::AddCreateWndData(&m_thunk.cd, this); if(MenuOrID.m_hMenu == NULL && (dwStyle & WS_CHILD)) MenuOrID.m_hMenu = (HMENU)(UINT_PTR)this; if(rect.m_lpRect == NULL) rect.m_lpRect = &TBase::rcDefault; HWND hWnd = ::CreateWindowEx(dwExStyle, MAKEINTATOM(atom), szWindowName, dwStyle, rect.m_lpRect->left, rect.m_lpRect->top, rect.m_lpRect->right - rect.m_lpRect->left, rect.m_lpRect->bottom - rect.m_lpRect->top, hWndParent, MenuOrID.m_hMenu, ModuleHelper::GetModuleInstance(), lpCreateParam); ATLASSERT(hWnd == NULL || m_hWnd == hWnd); return hWnd; } static HWND CreateSimpleToolBarCtrl(HWND hWndParent, UINT nResourceID, BOOL bInitialSeparator = FALSE, DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR) { HINSTANCE hInst = ModuleHelper::GetResourceInstance(); HRSRC hRsrc = ::FindResource(hInst, MAKEINTRESOURCE(nResourceID), RT_TOOLBAR); if (hRsrc == NULL) return NULL; HGLOBAL hGlobal = ::LoadResource(hInst, hRsrc); if (hGlobal == NULL) return NULL; _AtlToolBarData* pData = (_AtlToolBarData*)::LockResource(hGlobal); if (pData == NULL) return NULL; ATLASSERT(pData->wVersion == 1); WORD* pItems = pData->items(); int nItems = pData->wItemCount + (bInitialSeparator ? 1 : 0); CTempBuffer buff; TBBUTTON* pTBBtn = buff.Allocate(nItems); ATLASSERT(pTBBtn != NULL); if(pTBBtn == NULL) return NULL; const int cxSeparator = 8; // set initial separator (half width) if(bInitialSeparator) { pTBBtn[0].iBitmap = cxSeparator / 2; pTBBtn[0].idCommand = 0; pTBBtn[0].fsState = 0; pTBBtn[0].fsStyle = BTNS_SEP; pTBBtn[0].dwData = 0; pTBBtn[0].iString = 0; } int nBmp = 0; for(int i = 0, j = bInitialSeparator ? 1 : 0; i < pData->wItemCount; i++, j++) { if(pItems[i] != 0) { pTBBtn[j].iBitmap = nBmp++; pTBBtn[j].idCommand = pItems[i]; pTBBtn[j].fsState = TBSTATE_ENABLED; pTBBtn[j].fsStyle = BTNS_BUTTON; pTBBtn[j].dwData = 0; pTBBtn[j].iString = 0; } else { pTBBtn[j].iBitmap = cxSeparator; pTBBtn[j].idCommand = 0; pTBBtn[j].fsState = 0; pTBBtn[j].fsStyle = BTNS_SEP; pTBBtn[j].dwData = 0; pTBBtn[j].iString = 0; } } #ifndef _WIN32_WCE HWND hWnd = ::CreateWindowEx(0, TOOLBARCLASSNAME, NULL, dwStyle, 0, 0, 100, 100, hWndParent, (HMENU)LongToHandle(nID), ModuleHelper::GetModuleInstance(), NULL); if(hWnd == NULL) { ATLASSERT(FALSE); return NULL; } #else // CE specific dwStyle; nID; // The toolbar must go onto the existing CommandBar or MenuBar HWND hWnd = hWndParent; #endif // _WIN32_WCE ::SendMessage(hWnd, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0L); // check if font is taller than our bitmaps CFontHandle font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0L); if(font.IsNull()) font = (HFONT)::GetStockObject(SYSTEM_FONT); LOGFONT lf = { 0 }; font.GetLogFont(lf); WORD cyFontHeight = (WORD)abs(lf.lfHeight); #ifndef _WIN32_WCE WORD bitsPerPixel = AtlGetBitmapResourceBitsPerPixel(nResourceID); if(bitsPerPixel > 4) { COLORREF crMask = CLR_DEFAULT; if(bitsPerPixel == 32) { // 32-bit color bitmap with alpha channel (valid for Windows XP and later) crMask = CLR_NONE; } HIMAGELIST hImageList = ImageList_LoadImage(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(nResourceID), pData->wWidth, 1, crMask, IMAGE_BITMAP, LR_CREATEDIBSECTION | LR_DEFAULTSIZE); ATLASSERT(hImageList != NULL); ::SendMessage(hWnd, TB_SETIMAGELIST, 0, (LPARAM)hImageList); } else #endif // !_WIN32_WCE { TBADDBITMAP tbab = { 0 }; tbab.hInst = hInst; tbab.nID = nResourceID; ::SendMessage(hWnd, TB_ADDBITMAP, nBmp, (LPARAM)&tbab); } ::SendMessage(hWnd, TB_ADDBUTTONS, nItems, (LPARAM)pTBBtn); ::SendMessage(hWnd, TB_SETBITMAPSIZE, 0, MAKELONG(pData->wWidth, __max(pData->wHeight, cyFontHeight))); const int cxyButtonMargin = 7; ::SendMessage(hWnd, TB_SETBUTTONSIZE, 0, MAKELONG(pData->wWidth + cxyButtonMargin, __max(pData->wHeight, cyFontHeight) + cxyButtonMargin)); return hWnd; } #ifndef _WIN32_WCE static HWND CreateSimpleReBarCtrl(HWND hWndParent, DWORD dwStyle = ATL_SIMPLE_REBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR) { // Ensure style combinations for proper rebar painting if(dwStyle & CCS_NODIVIDER && dwStyle & WS_BORDER) dwStyle &= ~WS_BORDER; else if(!(dwStyle & WS_BORDER) && !(dwStyle & CCS_NODIVIDER)) dwStyle |= CCS_NODIVIDER; // Create rebar window HWND hWndReBar = ::CreateWindowEx(0, REBARCLASSNAME, NULL, dwStyle, 0, 0, 100, 100, hWndParent, (HMENU)LongToHandle(nID), ModuleHelper::GetModuleInstance(), NULL); if(hWndReBar == NULL) { ATLTRACE2(atlTraceUI, 0, _T("Failed to create rebar.\n")); return NULL; } // Initialize and send the REBARINFO structure REBARINFO rbi = { sizeof(REBARINFO), 0 }; if(::SendMessage(hWndReBar, RB_SETBARINFO, 0, (LPARAM)&rbi) == 0) { ATLTRACE2(atlTraceUI, 0, _T("Failed to initialize rebar.\n")); ::DestroyWindow(hWndReBar); return NULL; } return hWndReBar; } BOOL CreateSimpleReBar(DWORD dwStyle = ATL_SIMPLE_REBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR) { ATLASSERT(!::IsWindow(m_hWndToolBar)); m_hWndToolBar = CreateSimpleReBarCtrl(m_hWnd, dwStyle, nID); return (m_hWndToolBar != NULL); } static BOOL AddSimpleReBarBandCtrl(HWND hWndReBar, HWND hWndBand, int nID = 0, LPCTSTR lpstrTitle = NULL, BOOL bNewRow = FALSE, int cxWidth = 0, BOOL bFullWidthAlways = FALSE) { ATLASSERT(::IsWindow(hWndReBar)); // must be already created #ifdef _DEBUG // block - check if this is really a rebar { TCHAR lpszClassName[sizeof(REBARCLASSNAME)] = { 0 }; ::GetClassName(hWndReBar, lpszClassName, sizeof(REBARCLASSNAME)); ATLASSERT(lstrcmp(lpszClassName, REBARCLASSNAME) == 0); } #endif // _DEBUG ATLASSERT(::IsWindow(hWndBand)); // must be already created // Get number of buttons on the toolbar int nBtnCount = (int)::SendMessage(hWndBand, TB_BUTTONCOUNT, 0, 0L); // Set band info structure REBARBANDINFO rbBand = { RunTimeHelper::SizeOf_REBARBANDINFO() }; #if (_WIN32_IE >= 0x0400) rbBand.fMask = RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_ID | RBBIM_SIZE | RBBIM_IDEALSIZE; #else rbBand.fMask = RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_ID | RBBIM_SIZE; #endif // !(_WIN32_IE >= 0x0400) if(lpstrTitle != NULL) rbBand.fMask |= RBBIM_TEXT; rbBand.fStyle = RBBS_CHILDEDGE; #if (_WIN32_IE >= 0x0500) if(nBtnCount > 0) // add chevron style for toolbar with buttons rbBand.fStyle |= RBBS_USECHEVRON; #endif // (_WIN32_IE >= 0x0500) if(bNewRow) rbBand.fStyle |= RBBS_BREAK; rbBand.lpText = (LPTSTR)lpstrTitle; rbBand.hwndChild = hWndBand; if(nID == 0) // calc band ID nID = ATL_IDW_BAND_FIRST + (int)::SendMessage(hWndReBar, RB_GETBANDCOUNT, 0, 0L); rbBand.wID = nID; // Calculate the size of the band BOOL bRet = FALSE; RECT rcTmp = { 0 }; if(nBtnCount > 0) { bRet = (BOOL)::SendMessage(hWndBand, TB_GETITEMRECT, nBtnCount - 1, (LPARAM)&rcTmp); ATLASSERT(bRet); rbBand.cx = (cxWidth != 0) ? cxWidth : rcTmp.right; rbBand.cyMinChild = rcTmp.bottom - rcTmp.top; if(bFullWidthAlways) { rbBand.cxMinChild = rbBand.cx; } else if(lpstrTitle == NULL) { bRet = (BOOL)::SendMessage(hWndBand, TB_GETITEMRECT, 0, (LPARAM)&rcTmp); ATLASSERT(bRet); rbBand.cxMinChild = rcTmp.right; } else { rbBand.cxMinChild = 0; } } else // no buttons, either not a toolbar or really has no buttons { bRet = ::GetWindowRect(hWndBand, &rcTmp); ATLASSERT(bRet); rbBand.cx = (cxWidth != 0) ? cxWidth : (rcTmp.right - rcTmp.left); rbBand.cxMinChild = bFullWidthAlways ? rbBand.cx : 0; rbBand.cyMinChild = rcTmp.bottom - rcTmp.top; } #if (_WIN32_IE >= 0x0400) rbBand.cxIdeal = rbBand.cx; #endif // (_WIN32_IE >= 0x0400) // Add the band LRESULT lRes = ::SendMessage(hWndReBar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand); if(lRes == 0) { ATLTRACE2(atlTraceUI, 0, _T("Failed to add a band to the rebar.\n")); return FALSE; } #if (_WIN32_IE >= 0x0501) DWORD dwExStyle = (DWORD)::SendMessage(hWndBand, TB_GETEXTENDEDSTYLE, 0, 0L); ::SendMessage(hWndBand, TB_SETEXTENDEDSTYLE, 0, dwExStyle | TBSTYLE_EX_HIDECLIPPEDBUTTONS); #endif // (_WIN32_IE >= 0x0501) return TRUE; } BOOL AddSimpleReBarBand(HWND hWndBand, LPCTSTR lpstrTitle = NULL, BOOL bNewRow = FALSE, int cxWidth = 0, BOOL bFullWidthAlways = FALSE) { ATLASSERT(::IsWindow(m_hWndToolBar)); // must be an existing rebar ATLASSERT(::IsWindow(hWndBand)); // must be created return AddSimpleReBarBandCtrl(m_hWndToolBar, hWndBand, 0, lpstrTitle, bNewRow, cxWidth, bFullWidthAlways); } #if (_WIN32_IE >= 0x0400) void SizeSimpleReBarBands() { ATLASSERT(::IsWindow(m_hWndToolBar)); // must be an existing rebar int nCount = (int)::SendMessage(m_hWndToolBar, RB_GETBANDCOUNT, 0, 0L); for(int i = 0; i < nCount; i++) { REBARBANDINFO rbBand = { RunTimeHelper::SizeOf_REBARBANDINFO() }; rbBand.fMask = RBBIM_SIZE; BOOL bRet = (BOOL)::SendMessage(m_hWndToolBar, RB_GETBANDINFO, i, (LPARAM)&rbBand); ATLASSERT(bRet); RECT rect = { 0 }; ::SendMessage(m_hWndToolBar, RB_GETBANDBORDERS, i, (LPARAM)&rect); rbBand.cx += rect.left + rect.right; bRet = (BOOL)::SendMessage(m_hWndToolBar, RB_SETBANDINFO, i, (LPARAM)&rbBand); ATLASSERT(bRet); } } #endif // (_WIN32_IE >= 0x0400) #endif // _WIN32_WCE #ifndef _WIN32_WCE BOOL CreateSimpleStatusBar(LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR) #else // CE specific BOOL CreateSimpleStatusBar(LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, UINT nID = ATL_IDW_STATUS_BAR) #endif // _WIN32_WCE { ATLASSERT(!::IsWindow(m_hWndStatusBar)); m_hWndStatusBar = ::CreateStatusWindow(dwStyle, lpstrText, m_hWnd, nID); return (m_hWndStatusBar != NULL); } #ifndef _WIN32_WCE BOOL CreateSimpleStatusBar(UINT nTextID = ATL_IDS_IDLEMESSAGE, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR) #else // CE specific BOOL CreateSimpleStatusBar(UINT nTextID = ATL_IDS_IDLEMESSAGE, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, UINT nID = ATL_IDW_STATUS_BAR) #endif // _WIN32_WCE { const int cchMax = 128; // max text length is 127 for status bars (+1 for null) TCHAR szText[cchMax] = { 0 }; ::LoadString(ModuleHelper::GetResourceInstance(), nTextID, szText, cchMax); return CreateSimpleStatusBar(szText, dwStyle, nID); } #ifdef _WIN32_WCE BOOL CreateSimpleCECommandBar(LPTSTR pszMenu = NULL, WORD iButton = 0, DWORD dwFlags = 0, int nCmdBarID = 1) { ATLASSERT(m_hWndCECommandBar == NULL); ATLASSERT(m_hWndToolBar == NULL); m_hWndCECommandBar = ::CommandBar_Create(ModuleHelper::GetModuleInstance(), m_hWnd, nCmdBarID); if(m_hWndCECommandBar == NULL) return FALSE; m_hWndToolBar = m_hWndCECommandBar; BOOL bRet = TRUE; if(pszMenu != NULL) bRet &= ::CommandBar_InsertMenubarEx(m_hWndCECommandBar, IS_INTRESOURCE(pszMenu) ? ModuleHelper::GetResourceInstance() : NULL, pszMenu, iButton); bRet &= ::CommandBar_AddAdornments(m_hWndCECommandBar, dwFlags, 0); return bRet; } #if defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) BOOL CreateSimpleCEMenuBar(UINT nToolBarId = ATL_IDW_MENU_BAR, DWORD dwFlags = 0, int nBmpId = 0, int cBmpImages = 0) { ATLASSERT(m_hWndCECommandBar == NULL); SHMENUBARINFO mbi = { 0 }; mbi.cbSize = sizeof(mbi); mbi.hwndParent = m_hWnd; mbi.dwFlags = dwFlags; mbi.nToolBarId = nToolBarId; mbi.hInstRes = ModuleHelper::GetResourceInstance(); mbi.nBmpId = nBmpId; mbi.cBmpImages = cBmpImages; mbi.hwndMB = NULL; // This gets set by SHCreateMenuBar BOOL bRet = ::SHCreateMenuBar(&mbi); if(bRet != FALSE) { m_hWndCECommandBar = mbi.hwndMB; SizeToMenuBar(); } return bRet; } void SizeToMenuBar() // for menu bar only { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(::IsWindow(m_hWndCECommandBar)); RECT rect = { 0 }; GetWindowRect(&rect); RECT rectMB = { 0 }; ::GetWindowRect(m_hWndCECommandBar, &rectMB); int cy = ::IsWindowVisible(m_hWndCECommandBar) ? rectMB.top - rect.top : rectMB.bottom - rect.top; SetWindowPos(NULL, 0, 0, rect.right - rect.left, cy, SWP_NOZORDER | SWP_NOMOVE); } #endif // defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__) #endif // _WIN32_WCE void UpdateLayout(BOOL bResizeBars = TRUE) { RECT rect = { 0 }; GetClientRect(&rect); // position bars and offset their dimensions UpdateBarsPosition(rect, bResizeBars); // resize client window if(m_hWndClient != NULL) ::SetWindowPos(m_hWndClient, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOACTIVATE); } void UpdateBarsPosition(RECT& rect, BOOL bResizeBars = TRUE) { // resize toolbar if(m_hWndToolBar != NULL && ((DWORD)::GetWindowLong(m_hWndToolBar, GWL_STYLE) & WS_VISIBLE)) { if(bResizeBars != FALSE) { ::SendMessage(m_hWndToolBar, WM_SIZE, 0, 0); ::InvalidateRect(m_hWndToolBar, NULL, TRUE); } RECT rectTB = { 0 }; ::GetWindowRect(m_hWndToolBar, &rectTB); rect.top += rectTB.bottom - rectTB.top; } // resize status bar if(m_hWndStatusBar != NULL && ((DWORD)::GetWindowLong(m_hWndStatusBar, GWL_STYLE) & WS_VISIBLE)) { if(bResizeBars != FALSE) ::SendMessage(m_hWndStatusBar, WM_SIZE, 0, 0); RECT rectSB = { 0 }; ::GetWindowRect(m_hWndStatusBar, &rectSB); rect.bottom -= rectSB.bottom - rectSB.top; } } BOOL PreTranslateMessage(MSG* pMsg) { if(m_hAccel != NULL && ::TranslateAccelerator(m_hWnd, m_hAccel, pMsg)) return TRUE; return FALSE; } BEGIN_MSG_MAP(CFrameWindowImplBase) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) #ifndef _WIN32_WCE MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect) #endif // !_WIN32_WCE MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) #ifndef _WIN32_WCE NOTIFY_CODE_HANDLER(TTN_GETDISPINFOA, OnToolTipTextA) NOTIFY_CODE_HANDLER(TTN_GETDISPINFOW, OnToolTipTextW) #endif // !_WIN32_WCE END_MSG_MAP() LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(m_hWndClient != NULL) // view will paint itself instead return 1; bHandled = FALSE; return 0; } #ifndef _WIN32_WCE LRESULT OnMenuSelect(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { bHandled = FALSE; if(m_hWndStatusBar == NULL) return 1; WORD wFlags = HIWORD(wParam); if(wFlags == 0xFFFF && lParam == NULL) // menu closing { ::SendMessage(m_hWndStatusBar, SB_SIMPLE, FALSE, 0L); } else { const int cchBuff = 256; TCHAR szBuff[cchBuff] = { 0 }; if(!(wFlags & MF_POPUP)) { WORD wID = LOWORD(wParam); // check for special cases if(wID >= 0xF000 && wID < 0xF1F0) // system menu IDs wID = (WORD)(((wID - 0xF000) >> 4) + ATL_IDS_SCFIRST); else if(wID >= ID_FILE_MRU_FIRST && wID <= ID_FILE_MRU_LAST) // MRU items wID = ATL_IDS_MRU_FILE; else if(wID >= ATL_IDM_FIRST_MDICHILD && wID <= ATL_IDM_LAST_MDICHILD) // MDI child windows wID = ATL_IDS_MDICHILD; int nRet = ::LoadString(ModuleHelper::GetResourceInstance(), wID, szBuff, cchBuff); for(int i = 0; i < nRet; i++) { if(szBuff[i] == _T('\n')) { szBuff[i] = 0; break; } } } ::SendMessage(m_hWndStatusBar, SB_SIMPLE, TRUE, 0L); ::SendMessage(m_hWndStatusBar, SB_SETTEXT, (255 | SBT_NOBORDERS), (LPARAM)szBuff); } return 1; } #endif // !_WIN32_WCE LRESULT OnSetFocus(UINT, WPARAM, LPARAM, BOOL& bHandled) { if(m_hWndClient != NULL) ::SetFocus(m_hWndClient); bHandled = FALSE; return 1; } LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL& bHandled) { if((GetStyle() & (WS_CHILD | WS_POPUP)) == 0) ::PostQuitMessage(1); bHandled = FALSE; return 1; } #ifndef _WIN32_WCE LRESULT OnToolTipTextA(int idCtrl, LPNMHDR pnmh, BOOL& /*bHandled*/) { LPNMTTDISPINFOA pDispInfo = (LPNMTTDISPINFOA)pnmh; if((idCtrl != 0) && !(pDispInfo->uFlags & TTF_IDISHWND)) { const int cchBuff = 256; char szBuff[cchBuff] = { 0 }; int nRet = ::LoadStringA(ModuleHelper::GetResourceInstance(), idCtrl, szBuff, cchBuff); for(int i = 0; i < nRet; i++) { if(szBuff[i] == '\n') { SecureHelper::strncpyA_x(pDispInfo->szText, _countof(pDispInfo->szText), &szBuff[i + 1], _TRUNCATE); break; } } #if (_WIN32_IE >= 0x0300) if(nRet > 0) // string was loaded, save it pDispInfo->uFlags |= TTF_DI_SETITEM; #endif // (_WIN32_IE >= 0x0300) } return 0; } LRESULT OnToolTipTextW(int idCtrl, LPNMHDR pnmh, BOOL& /*bHandled*/) { LPNMTTDISPINFOW pDispInfo = (LPNMTTDISPINFOW)pnmh; if((idCtrl != 0) && !(pDispInfo->uFlags & TTF_IDISHWND)) { const int cchBuff = 256; wchar_t szBuff[cchBuff] = { 0 }; int nRet = ::LoadStringW(ModuleHelper::GetResourceInstance(), idCtrl, szBuff, cchBuff); for(int i = 0; i < nRet; i++) { if(szBuff[i] == L'\n') { SecureHelper::strncpyW_x(pDispInfo->szText, _countof(pDispInfo->szText), &szBuff[i + 1], _TRUNCATE); break; } } #if (_WIN32_IE >= 0x0300) if(nRet > 0) // string was loaded, save it pDispInfo->uFlags |= TTF_DI_SETITEM; #endif // (_WIN32_IE >= 0x0300) } return 0; } #endif // !_WIN32_WCE // Implementation - chevron menu support #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) bool PrepareChevronMenu(_ChevronMenuInfo& cmi) { // get rebar and toolbar REBARBANDINFO rbbi = { RunTimeHelper::SizeOf_REBARBANDINFO() }; rbbi.fMask = RBBIM_CHILD; BOOL bRet = (BOOL)::SendMessage(cmi.lpnm->hdr.hwndFrom, RB_GETBANDINFO, cmi.lpnm->uBand, (LPARAM)&rbbi); ATLASSERT(bRet); // assume the band is a toolbar ATL::CWindow wnd = rbbi.hwndChild; int nCount = (int)wnd.SendMessage(TB_BUTTONCOUNT); if(nCount <= 0) // probably not a toolbar return false; // check if it's a command bar CMenuHandle menuCmdBar = (HMENU)wnd.SendMessage(CBRM_GETMENU); cmi.bCmdBar = (menuCmdBar.m_hMenu != NULL); // build a menu from hidden items CMenuHandle menu; bRet = menu.CreatePopupMenu(); ATLASSERT(bRet); RECT rcClient = { 0 }; bRet = wnd.GetClientRect(&rcClient); ATLASSERT(bRet); for(int i = 0; i < nCount; i++) { TBBUTTON tbb = { 0 }; bRet = (BOOL)wnd.SendMessage(TB_GETBUTTON, i, (LPARAM)&tbb); ATLASSERT(bRet); // skip hidden buttons if((tbb.fsState & TBSTATE_HIDDEN) != 0) continue; RECT rcButton = { 0 }; bRet = (BOOL)wnd.SendMessage(TB_GETITEMRECT, i, (LPARAM)&rcButton); ATLASSERT(bRet); bool bEnabled = ((tbb.fsState & TBSTATE_ENABLED) != 0); if((rcButton.right > rcClient.right) || (rcButton.bottom > rcClient.bottom)) { if(tbb.fsStyle & BTNS_SEP) { if(menu.GetMenuItemCount() > 0) menu.AppendMenu(MF_SEPARATOR); } else if(cmi.bCmdBar) { const int cchBuff = 200; TCHAR szBuff[cchBuff] = { 0 }; CMenuItemInfo mii; mii.fMask = MIIM_TYPE | MIIM_SUBMENU; mii.dwTypeData = szBuff; mii.cch = cchBuff; bRet = menuCmdBar.GetMenuItemInfo(i, TRUE, &mii); ATLASSERT(bRet); // Note: CmdBar currently supports only drop-down items ATLASSERT(::IsMenu(mii.hSubMenu)); bRet = menu.AppendMenu(MF_STRING | MF_POPUP | (bEnabled ? MF_ENABLED : MF_GRAYED), (UINT_PTR)mii.hSubMenu, mii.dwTypeData); ATLASSERT(bRet); } else { // get button's text const int cchBuff = 200; TCHAR szBuff[cchBuff] = { 0 }; LPTSTR lpstrText = szBuff; TBBUTTONINFO tbbi = { 0 }; tbbi.cbSize = sizeof(TBBUTTONINFO); tbbi.dwMask = TBIF_TEXT; tbbi.pszText = szBuff; tbbi.cchText = cchBuff; if(wnd.SendMessage(TB_GETBUTTONINFO, tbb.idCommand, (LPARAM)&tbbi) == -1 || lstrlen(szBuff) == 0) { // no text for this button, try a resource string lpstrText = _T(""); int nRet = ::LoadString(ModuleHelper::GetResourceInstance(), tbb.idCommand, szBuff, cchBuff); for(int n = 0; n < nRet; n++) { if(szBuff[n] == _T('\n')) { lpstrText = &szBuff[n + 1]; break; } } } bRet = menu.AppendMenu(MF_STRING | (bEnabled ? MF_ENABLED : MF_GRAYED), tbb.idCommand, lpstrText); ATLASSERT(bRet); } } } if(menu.GetMenuItemCount() == 0) // no hidden buttons after all { menu.DestroyMenu(); ::MessageBeep((UINT)-1); return false; } cmi.hMenu = menu; return true; } void DisplayChevronMenu(_ChevronMenuInfo& cmi) { #ifndef TPM_VERPOSANIMATION const UINT TPM_VERPOSANIMATION = 0x1000L; // Menu animation flag #endif // convert chevron rect to screen coordinates ATL::CWindow wndFrom = cmi.lpnm->hdr.hwndFrom; POINT pt = { cmi.lpnm->rc.left, cmi.lpnm->rc.bottom }; wndFrom.MapWindowPoints(NULL, &pt, 1); RECT rc = cmi.lpnm->rc; wndFrom.MapWindowPoints(NULL, &rc); // set up flags and rect UINT uMenuFlags = TPM_LEFTBUTTON | TPM_VERTICAL | TPM_LEFTALIGN | TPM_TOPALIGN | (!AtlIsOldWindows() ? TPM_VERPOSANIMATION : 0); TPMPARAMS TPMParams = { 0 }; TPMParams.cbSize = sizeof(TPMPARAMS); TPMParams.rcExclude = rc; // check if this window has a command bar HWND hWndCmdBar = (HWND)::SendMessage(m_hWnd, CBRM_GETCMDBAR, 0, 0L); if(::IsWindow(hWndCmdBar)) { CBRPOPUPMENU CBRPopupMenu = { sizeof(CBRPOPUPMENU), cmi.hMenu, uMenuFlags, pt.x, pt.y, &TPMParams }; ::SendMessage(hWndCmdBar, CBRM_TRACKPOPUPMENU, 0, (LPARAM)&CBRPopupMenu); } else { CMenuHandle menu = cmi.hMenu; menu.TrackPopupMenuEx(uMenuFlags, pt.x, pt.y, m_hWnd, &TPMParams); } } void CleanupChevronMenu(_ChevronMenuInfo& cmi) { CMenuHandle menu = cmi.hMenu; // if menu is from a command bar, detach submenus so they are not destroyed if(cmi.bCmdBar) { for(int i = menu.GetMenuItemCount() - 1; i >=0; i--) menu.RemoveMenu(i, MF_BYPOSITION); } // destroy menu menu.DestroyMenu(); // convert chevron rect to screen coordinates ATL::CWindow wndFrom = cmi.lpnm->hdr.hwndFrom; RECT rc = cmi.lpnm->rc; wndFrom.MapWindowPoints(NULL, &rc); // eat next message if click is on the same button MSG msg = { 0 }; if(::PeekMessage(&msg, m_hWnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_NOREMOVE) && ::PtInRect(&rc, msg.pt)) ::PeekMessage(&msg, m_hWnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE); } #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) }; template class ATL_NO_VTABLE CFrameWindowImpl : public CFrameWindowImplBase< TBase, TWinTraits > { public: HWND Create(HWND hWndParent = NULL, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, HMENU hMenu = NULL, LPVOID lpCreateParam = NULL) { ATOM atom = T::GetWndClassInfo().Register(&m_pfnSuperWindowProc); dwStyle = T::GetWndStyle(dwStyle); dwExStyle = T::GetWndExStyle(dwExStyle); if(rect.m_lpRect == NULL) rect.m_lpRect = &TBase::rcDefault; return CFrameWindowImplBase< TBase, TWinTraits >::Create(hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, hMenu, atom, lpCreateParam); } HWND CreateEx(HWND hWndParent = NULL, ATL::_U_RECT rect = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, LPVOID lpCreateParam = NULL) { const int cchName = 256; TCHAR szWindowName[cchName] = { 0 }; #ifndef _WIN32_WCE ::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName); HMENU hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID)); #else // CE specific ::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName); // This always needs to be NULL for Windows CE. // Frame Window menus have to go onto the CommandBar. // Use CreateSimpleCECommandBar HMENU hMenu = NULL; #endif // _WIN32_WCE T* pT = static_cast(this); HWND hWnd = pT->Create(hWndParent, rect, szWindowName, dwStyle, dwExStyle, hMenu, lpCreateParam); if(hWnd != NULL) m_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID)); return hWnd; } BOOL CreateSimpleToolBar(UINT nResourceID = 0, DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR) { if(nResourceID == 0) nResourceID = T::GetWndClassInfo().m_uCommonResourceID; #ifndef _WIN32_WCE ATLASSERT(!::IsWindow(m_hWndToolBar)); m_hWndToolBar = T::CreateSimpleToolBarCtrl(m_hWnd, nResourceID, TRUE, dwStyle, nID); return (m_hWndToolBar != NULL); #else // CE specific HWND hWnd= T::CreateSimpleToolBarCtrl(m_hWndCECommandBar, nResourceID, TRUE, dwStyle, nID); return (hWnd != NULL); #endif // _WIN32_WCE } #ifdef _WIN32_WCE // CE specific variant that returns the handle of the toolbar HWND CreateSimpleCEToolBar(UINT nResourceID = 0, DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR) { if(nResourceID == 0) nResourceID = T::GetWndClassInfo().m_uCommonResourceID; return T::CreateSimpleToolBarCtrl(m_hWndCECommandBar, nResourceID, TRUE, dwStyle, nID); } #endif // _WIN32_WCE // message map and handlers typedef CFrameWindowImplBase< TBase, TWinTraits > _baseClass; BEGIN_MSG_MAP(CFrameWindowImpl) MESSAGE_HANDLER(WM_SIZE, OnSize) #ifndef _ATL_NO_REBAR_SUPPORT #if (_WIN32_IE >= 0x0400) NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnReBarAutoSize) #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) NOTIFY_CODE_HANDLER(RBN_CHEVRONPUSHED, OnChevronPushed) #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) #endif // !_ATL_NO_REBAR_SUPPORT CHAIN_MSG_MAP(_baseClass) END_MSG_MAP() LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { if(wParam != SIZE_MINIMIZED) { T* pT = static_cast(this); pT->UpdateLayout(); } bHandled = FALSE; return 1; } #ifndef _ATL_NO_REBAR_SUPPORT #if (_WIN32_IE >= 0x0400) LRESULT OnReBarAutoSize(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->UpdateLayout(FALSE); return 0; } #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) LRESULT OnChevronPushed(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled) { T* pT = static_cast(this); _ChevronMenuInfo cmi = { NULL, (LPNMREBARCHEVRON)pnmh, false }; if(!pT->PrepareChevronMenu(cmi)) { bHandled = FALSE; return 1; } // display a popup menu with hidden items pT->DisplayChevronMenu(cmi); // cleanup pT->CleanupChevronMenu(cmi); return 0; } #endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE) #endif // !_ATL_NO_REBAR_SUPPORT }; /////////////////////////////////////////////////////////////////////////////// // AtlCreateSimpleToolBar - helper for creating simple toolbars #ifndef _WIN32_WCE inline HWND AtlCreateSimpleToolBar(HWND hWndParent, UINT nResourceID, BOOL bInitialSeparator = FALSE, DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR) { return CFrameWindowImplBase<>::CreateSimpleToolBarCtrl(hWndParent, nResourceID, bInitialSeparator, dwStyle, nID); } #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CMDIWindow #ifndef _WIN32_WCE #ifndef _WTL_MDIWINDOWMENU_TEXT #define _WTL_MDIWINDOWMENU_TEXT _T("&Window") #endif class CMDIWindow : public ATL::CWindow { public: // Data members HWND m_hWndMDIClient; HMENU m_hMenu; // Constructors CMDIWindow(HWND hWnd = NULL) : ATL::CWindow(hWnd), m_hWndMDIClient(NULL), m_hMenu(NULL) { } CMDIWindow& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } // Operations HWND MDIGetActive(BOOL* lpbMaximized = NULL) { ATLASSERT(::IsWindow(m_hWndMDIClient)); return (HWND)::SendMessage(m_hWndMDIClient, WM_MDIGETACTIVE, 0, (LPARAM)lpbMaximized); } void MDIActivate(HWND hWndChildToActivate) { ATLASSERT(::IsWindow(m_hWndMDIClient)); ATLASSERT(::IsWindow(hWndChildToActivate)); ::SendMessage(m_hWndMDIClient, WM_MDIACTIVATE, (WPARAM)hWndChildToActivate, 0); } void MDINext(HWND hWndChild, BOOL bPrevious = FALSE) { ATLASSERT(::IsWindow(m_hWndMDIClient)); ATLASSERT(hWndChild == NULL || ::IsWindow(hWndChild)); ::SendMessage(m_hWndMDIClient, WM_MDINEXT, (WPARAM)hWndChild, (LPARAM)bPrevious); } void MDIMaximize(HWND hWndChildToMaximize) { ATLASSERT(::IsWindow(m_hWndMDIClient)); ATLASSERT(::IsWindow(hWndChildToMaximize)); ::SendMessage(m_hWndMDIClient, WM_MDIMAXIMIZE, (WPARAM)hWndChildToMaximize, 0); } void MDIRestore(HWND hWndChildToRestore) { ATLASSERT(::IsWindow(m_hWndMDIClient)); ATLASSERT(::IsWindow(hWndChildToRestore)); ::SendMessage(m_hWndMDIClient, WM_MDIRESTORE, (WPARAM)hWndChildToRestore, 0); } void MDIDestroy(HWND hWndChildToDestroy) { ATLASSERT(::IsWindow(m_hWndMDIClient)); ATLASSERT(::IsWindow(hWndChildToDestroy)); ::SendMessage(m_hWndMDIClient, WM_MDIDESTROY, (WPARAM)hWndChildToDestroy, 0); } BOOL MDICascade(UINT uFlags = 0) { ATLASSERT(::IsWindow(m_hWndMDIClient)); return (BOOL)::SendMessage(m_hWndMDIClient, WM_MDICASCADE, (WPARAM)uFlags, 0); } BOOL MDITile(UINT uFlags = MDITILE_HORIZONTAL) { ATLASSERT(::IsWindow(m_hWndMDIClient)); return (BOOL)::SendMessage(m_hWndMDIClient, WM_MDITILE, (WPARAM)uFlags, 0); } void MDIIconArrange() { ATLASSERT(::IsWindow(m_hWndMDIClient)); ::SendMessage(m_hWndMDIClient, WM_MDIICONARRANGE, 0, 0); } HMENU MDISetMenu(HMENU hMenuFrame, HMENU hMenuWindow) { ATLASSERT(::IsWindow(m_hWndMDIClient)); return (HMENU)::SendMessage(m_hWndMDIClient, WM_MDISETMENU, (WPARAM)hMenuFrame, (LPARAM)hMenuWindow); } HMENU MDIRefreshMenu() { ATLASSERT(::IsWindow(m_hWndMDIClient)); return (HMENU)::SendMessage(m_hWndMDIClient, WM_MDIREFRESHMENU, 0, 0); } // Additional operations static HMENU GetStandardWindowMenu(HMENU hMenu) { int nCount = ::GetMenuItemCount(hMenu); if(nCount == -1) return NULL; int nLen = ::GetMenuString(hMenu, nCount - 2, NULL, 0, MF_BYPOSITION); if(nLen == 0) return NULL; CTempBuffer buff; LPTSTR lpszText = buff.Allocate(nLen + 1); if(lpszText == NULL) return NULL; if(::GetMenuString(hMenu, nCount - 2, lpszText, nLen + 1, MF_BYPOSITION) != nLen) return NULL; if(lstrcmp(lpszText, _WTL_MDIWINDOWMENU_TEXT) != 0) return NULL; return ::GetSubMenu(hMenu, nCount - 2); } void SetMDIFrameMenu() { HMENU hWindowMenu = GetStandardWindowMenu(m_hMenu); MDISetMenu(m_hMenu, hWindowMenu); MDIRefreshMenu(); ::DrawMenuBar(GetMDIFrame()); } HWND GetMDIFrame() const { return ::GetParent(m_hWndMDIClient); } }; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CMDIFrameWindowImpl #ifndef _WIN32_WCE // MDI child command chaining macro (only for MDI frame windows) #define CHAIN_MDI_CHILD_COMMANDS() \ if(uMsg == WM_COMMAND) \ { \ HWND hWndChild = MDIGetActive(); \ if(hWndChild != NULL) \ ::SendMessage(hWndChild, uMsg, wParam, lParam); \ } template class ATL_NO_VTABLE CMDIFrameWindowImpl : public CFrameWindowImplBase { public: HWND Create(HWND hWndParent = NULL, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, HMENU hMenu = NULL, LPVOID lpCreateParam = NULL) { m_hMenu = hMenu; ATOM atom = T::GetWndClassInfo().Register(&m_pfnSuperWindowProc); dwStyle = T::GetWndStyle(dwStyle); dwExStyle = T::GetWndExStyle(dwExStyle); if(rect.m_lpRect == NULL) rect.m_lpRect = &TBase::rcDefault; return CFrameWindowImplBase::Create(hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, hMenu, atom, lpCreateParam); } HWND CreateEx(HWND hWndParent = NULL, ATL::_U_RECT rect = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, LPVOID lpCreateParam = NULL) { const int cchName = 256; TCHAR szWindowName[cchName] = { 0 }; ::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName); HMENU hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID)); T* pT = static_cast(this); HWND hWnd = pT->Create(hWndParent, rect, szWindowName, dwStyle, dwExStyle, hMenu, lpCreateParam); if(hWnd != NULL) m_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID)); return hWnd; } BOOL CreateSimpleToolBar(UINT nResourceID = 0, DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR) { ATLASSERT(!::IsWindow(m_hWndToolBar)); if(nResourceID == 0) nResourceID = T::GetWndClassInfo().m_uCommonResourceID; m_hWndToolBar = T::CreateSimpleToolBarCtrl(m_hWnd, nResourceID, TRUE, dwStyle, nID); return (m_hWndToolBar != NULL); } virtual WNDPROC GetWindowProc() { return MDIFrameWindowProc; } static LRESULT CALLBACK MDIFrameWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CMDIFrameWindowImpl< T, TBase, TWinTraits >* pThis = (CMDIFrameWindowImpl< T, TBase, TWinTraits >*)hWnd; // set a ptr to this message and save the old value #if (_ATL_VER >= 0x0700) ATL::_ATL_MSG msg(pThis->m_hWnd, uMsg, wParam, lParam); const ATL::_ATL_MSG* pOldMsg = pThis->m_pCurrentMsg; #else // !(_ATL_VER >= 0x0700) MSG msg = { pThis->m_hWnd, uMsg, wParam, lParam, 0, { 0, 0 } }; const MSG* pOldMsg = pThis->m_pCurrentMsg; #endif // !(_ATL_VER >= 0x0700) pThis->m_pCurrentMsg = &msg; // pass to the message map to process LRESULT lRes = 0; BOOL bRet = pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, 0); // restore saved value for the current message ATLASSERT(pThis->m_pCurrentMsg == &msg); pThis->m_pCurrentMsg = pOldMsg; // do the default processing if message was not handled if(!bRet) { if(uMsg != WM_NCDESTROY) { lRes = pThis->DefWindowProc(uMsg, wParam, lParam); } else { // unsubclass, if needed LONG_PTR pfnWndProc = ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC); lRes = pThis->DefWindowProc(uMsg, wParam, lParam); if(pThis->m_pfnSuperWindowProc != ::DefWindowProc && ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC) == pfnWndProc) ::SetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC, (LONG_PTR)pThis->m_pfnSuperWindowProc); #if (_ATL_VER >= 0x0700) // mark window as destryed pThis->m_dwState |= WINSTATE_DESTROYED; #else // !(_ATL_VER >= 0x0700) // clear out window handle HWND hWnd = pThis->m_hWnd; pThis->m_hWnd = NULL; // clean up after window is destroyed pThis->OnFinalMessage(hWnd); #endif // !(_ATL_VER >= 0x0700) } } #if (_ATL_VER >= 0x0700) if(pThis->m_dwState & WINSTATE_DESTROYED && pThis->m_pCurrentMsg == NULL) { // clear out window handle HWND hWndThis = pThis->m_hWnd; pThis->m_hWnd = NULL; pThis->m_dwState &= ~WINSTATE_DESTROYED; // clean up after window is destroyed pThis->OnFinalMessage(hWndThis); } #endif // (_ATL_VER >= 0x0700) return lRes; } // Overriden to call DefWindowProc which uses DefFrameProc LRESULT DefWindowProc() { const MSG* pMsg = m_pCurrentMsg; LRESULT lRes = 0; if (pMsg != NULL) lRes = DefWindowProc(pMsg->message, pMsg->wParam, pMsg->lParam); return lRes; } LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) { return ::DefFrameProc(m_hWnd, m_hWndMDIClient, uMsg, wParam, lParam); } BOOL PreTranslateMessage(MSG* pMsg) { if(CFrameWindowImplBase::PreTranslateMessage(pMsg)) return TRUE; return ::TranslateMDISysAccel(m_hWndMDIClient, pMsg); } HWND CreateMDIClient(HMENU hWindowMenu = NULL, UINT nID = ATL_IDW_CLIENT, UINT nFirstChildID = ATL_IDM_FIRST_MDICHILD) { DWORD dwStyle = WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | MDIS_ALLCHILDSTYLES; DWORD dwExStyle = WS_EX_CLIENTEDGE; CLIENTCREATESTRUCT ccs = { 0 }; ccs.hWindowMenu = hWindowMenu; ccs.idFirstChild = nFirstChildID; if((GetStyle() & (WS_HSCROLL | WS_VSCROLL)) != 0) { // parent MDI frame's scroll styles move to the MDICLIENT dwStyle |= (GetStyle() & (WS_HSCROLL | WS_VSCROLL)); // fast way to turn off the scrollbar bits (without a resize) ModifyStyle(WS_HSCROLL | WS_VSCROLL, 0, SWP_NOREDRAW | SWP_FRAMECHANGED); } // Create MDICLIENT window m_hWndClient = ::CreateWindowEx(dwExStyle, _T("MDIClient"), NULL, dwStyle, 0, 0, 1, 1, m_hWnd, (HMENU)LongToHandle(nID), ModuleHelper::GetModuleInstance(), (LPVOID)&ccs); if (m_hWndClient == NULL) { ATLTRACE2(atlTraceUI, 0, _T("MDI Frame failed to create MDICLIENT.\n")); return NULL; } // Move it to the top of z-order ::BringWindowToTop(m_hWndClient); // set as MDI client window m_hWndMDIClient = m_hWndClient; // update to proper size T* pT = static_cast(this); pT->UpdateLayout(); return m_hWndClient; } typedef CFrameWindowImplBase _baseClass; BEGIN_MSG_MAP(CMDIFrameWindowImpl) MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) MESSAGE_HANDLER(WM_MDISETMENU, OnMDISetMenu) #ifndef _ATL_NO_REBAR_SUPPORT #if (_WIN32_IE >= 0x0400) NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnReBarAutoSize) #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) NOTIFY_CODE_HANDLER(RBN_CHEVRONPUSHED, OnChevronPushed) #endif // (_WIN32_IE >= 0x0500) #endif // !_ATL_NO_REBAR_SUPPORT CHAIN_MSG_MAP(_baseClass) END_MSG_MAP() LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { if(wParam != SIZE_MINIMIZED) { T* pT = static_cast(this); pT->UpdateLayout(); } // message must be handled, otherwise DefFrameProc would resize the client again return 0; } LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { // don't allow CFrameWindowImplBase to handle this one return DefWindowProc(uMsg, wParam, lParam); } LRESULT OnMDISetMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { SetMDIFrameMenu(); return 0; } #ifndef _ATL_NO_REBAR_SUPPORT #if (_WIN32_IE >= 0x0400) LRESULT OnReBarAutoSize(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->UpdateLayout(FALSE); return 0; } #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) LRESULT OnChevronPushed(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled) { T* pT = static_cast(this); _ChevronMenuInfo cmi = { NULL, (LPNMREBARCHEVRON)pnmh, false }; if(!pT->PrepareChevronMenu(cmi)) { bHandled = FALSE; return 1; } // display a popup menu with hidden items pT->DisplayChevronMenu(cmi); // cleanup pT->CleanupChevronMenu(cmi); return 0; } #endif // (_WIN32_IE >= 0x0500) #endif // !_ATL_NO_REBAR_SUPPORT }; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CMDIChildWindowImpl #ifndef _WIN32_WCE template class ATL_NO_VTABLE CMDIChildWindowImpl : public CFrameWindowImplBase { public: HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, UINT nMenuID = 0, LPVOID lpCreateParam = NULL) { ATOM atom = T::GetWndClassInfo().Register(&m_pfnSuperWindowProc); if(nMenuID != 0) m_hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(nMenuID)); dwStyle = T::GetWndStyle(dwStyle); dwExStyle = T::GetWndExStyle(dwExStyle); dwExStyle |= WS_EX_MDICHILD; // force this one m_pfnSuperWindowProc = ::DefMDIChildProc; m_hWndMDIClient = hWndParent; ATLASSERT(::IsWindow(m_hWndMDIClient)); if(rect.m_lpRect == NULL) rect.m_lpRect = &TBase::rcDefault; // If the currently active MDI child is maximized, we want to create this one maximized too ATL::CWindow wndParent = hWndParent; BOOL bMaximized = FALSE; wndParent.SendMessage(WM_MDIGETACTIVE, 0, (LPARAM)&bMaximized); if(bMaximized) wndParent.SetRedraw(FALSE); HWND hWnd = CFrameWindowImplBase::Create(hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, (UINT)0U, atom, lpCreateParam); if(bMaximized) { // Maximize and redraw everything if(hWnd != NULL) MDIMaximize(hWnd); wndParent.SetRedraw(TRUE); wndParent.RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); ::SetFocus(GetMDIFrame()); // focus will be set back to this window } else if(hWnd != NULL && ::IsWindowVisible(m_hWnd) && !::IsChild(hWnd, ::GetFocus())) { ::SetFocus(hWnd); } return hWnd; } HWND CreateEx(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR lpcstrWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, LPVOID lpCreateParam = NULL) { const int cchName = 256; TCHAR szWindowName[cchName] = { 0 }; if(lpcstrWindowName == NULL) { ::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName); lpcstrWindowName = szWindowName; } T* pT = static_cast(this); HWND hWnd = pT->Create(hWndParent, rect, lpcstrWindowName, dwStyle, dwExStyle, T::GetWndClassInfo().m_uCommonResourceID, lpCreateParam); if(hWnd != NULL) m_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID)); return hWnd; } BOOL CreateSimpleToolBar(UINT nResourceID = 0, DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR) { ATLASSERT(!::IsWindow(m_hWndToolBar)); if(nResourceID == 0) nResourceID = T::GetWndClassInfo().m_uCommonResourceID; m_hWndToolBar = T::CreateSimpleToolBarCtrl(m_hWnd, nResourceID, TRUE, dwStyle, nID); return (m_hWndToolBar != NULL); } BOOL UpdateClientEdge(LPRECT lpRect = NULL) { // only adjust for active MDI child window HWND hWndChild = MDIGetActive(); if(hWndChild != NULL && hWndChild != m_hWnd) return FALSE; // need to adjust the client edge style as max/restore happens DWORD dwStyle = ::GetWindowLong(m_hWndMDIClient, GWL_EXSTYLE); DWORD dwNewStyle = dwStyle; if(hWndChild != NULL && ((GetExStyle() & WS_EX_CLIENTEDGE) == 0) && ((GetStyle() & WS_MAXIMIZE) != 0)) dwNewStyle &= ~(WS_EX_CLIENTEDGE); else dwNewStyle |= WS_EX_CLIENTEDGE; if(dwStyle != dwNewStyle) { // SetWindowPos will not move invalid bits ::RedrawWindow(m_hWndMDIClient, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); // remove/add WS_EX_CLIENTEDGE to MDI client area ::SetWindowLong(m_hWndMDIClient, GWL_EXSTYLE, dwNewStyle); ::SetWindowPos(m_hWndMDIClient, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOCOPYBITS); // return new client area if (lpRect != NULL) ::GetClientRect(m_hWndMDIClient, lpRect); return TRUE; } return FALSE; } typedef CFrameWindowImplBase _baseClass; BEGIN_MSG_MAP(CMDIChildWindowImpl) MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_WINDOWPOSCHANGED, OnWindowPosChanged) MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate) MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect) MESSAGE_HANDLER(WM_MDIACTIVATE, OnMDIActivate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) #ifndef _ATL_NO_REBAR_SUPPORT #if (_WIN32_IE >= 0x0400) NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnReBarAutoSize) #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) NOTIFY_CODE_HANDLER(RBN_CHEVRONPUSHED, OnChevronPushed) #endif // (_WIN32_IE >= 0x0500) #endif // !_ATL_NO_REBAR_SUPPORT CHAIN_MSG_MAP(_baseClass) END_MSG_MAP() LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { DefWindowProc(uMsg, wParam, lParam); // needed for MDI children if(wParam != SIZE_MINIMIZED) { T* pT = static_cast(this); pT->UpdateLayout(); } return 0; } LRESULT OnWindowPosChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { // update MDI client edge and adjust MDI child rect LPWINDOWPOS lpWndPos = (LPWINDOWPOS)lParam; if(!(lpWndPos->flags & SWP_NOSIZE)) { RECT rectClient = { 0 }; if(UpdateClientEdge(&rectClient) && ((GetStyle() & WS_MAXIMIZE) != 0)) { ::AdjustWindowRectEx(&rectClient, GetStyle(), FALSE, GetExStyle()); lpWndPos->x = rectClient.left; lpWndPos->y = rectClient.top; lpWndPos->cx = rectClient.right - rectClient.left; lpWndPos->cy = rectClient.bottom - rectClient.top; } } bHandled = FALSE; return 1; } LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { LRESULT lRes = DefWindowProc(uMsg, wParam, lParam); // Activate this MDI window if needed if(lRes == MA_ACTIVATE || lRes == MA_ACTIVATEANDEAT) { if(MDIGetActive() != m_hWnd) MDIActivate(m_hWnd); } return lRes; } LRESULT OnMenuSelect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { return ::SendMessage(GetMDIFrame(), uMsg, wParam, lParam); } LRESULT OnMDIActivate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { if((HWND)lParam == m_hWnd && m_hMenu != NULL) SetMDIFrameMenu(); else if((HWND)lParam == NULL) ::SendMessage(GetMDIFrame(), WM_MDISETMENU, 0, 0); bHandled = FALSE; return 1; } LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(m_hMenu != NULL) { ::DestroyMenu(m_hMenu); m_hMenu = NULL; } UpdateClientEdge(); bHandled = FALSE; return 1; } #ifndef _ATL_NO_REBAR_SUPPORT #if (_WIN32_IE >= 0x0400) LRESULT OnReBarAutoSize(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->UpdateLayout(FALSE); return 0; } #endif // (_WIN32_IE >= 0x0400) #if (_WIN32_IE >= 0x0500) LRESULT OnChevronPushed(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled) { T* pT = static_cast(this); _ChevronMenuInfo cmi = { NULL, (LPNMREBARCHEVRON)pnmh, false }; if(!pT->PrepareChevronMenu(cmi)) { bHandled = FALSE; return 1; } // display a popup menu with hidden items pT->DisplayChevronMenu(cmi); // cleanup pT->CleanupChevronMenu(cmi); return 0; } #endif // (_WIN32_IE >= 0x0500) #endif // !_ATL_NO_REBAR_SUPPORT }; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // COwnerDraw - MI class for owner-draw support template class COwnerDraw { public: #if (_ATL_VER < 0x0700) BOOL m_bHandledOD; BOOL IsMsgHandled() const { return m_bHandledOD; } void SetMsgHandled(BOOL bHandled) { m_bHandledOD = bHandled; } #endif // (_ATL_VER < 0x0700) // Message map and handlers BEGIN_MSG_MAP(COwnerDraw< T >) MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem) MESSAGE_HANDLER(WM_MEASUREITEM, OnMeasureItem) MESSAGE_HANDLER(WM_COMPAREITEM, OnCompareItem) MESSAGE_HANDLER(WM_DELETEITEM, OnDeleteItem) ALT_MSG_MAP(1) MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem) MESSAGE_HANDLER(OCM_MEASUREITEM, OnMeasureItem) MESSAGE_HANDLER(OCM_COMPAREITEM, OnCompareItem) MESSAGE_HANDLER(OCM_DELETEITEM, OnDeleteItem) END_MSG_MAP() LRESULT OnDrawItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { T* pT = static_cast(this); pT->SetMsgHandled(TRUE); pT->DrawItem((LPDRAWITEMSTRUCT)lParam); bHandled = pT->IsMsgHandled(); return (LRESULT)TRUE; } LRESULT OnMeasureItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { T* pT = static_cast(this); pT->SetMsgHandled(TRUE); pT->MeasureItem((LPMEASUREITEMSTRUCT)lParam); bHandled = pT->IsMsgHandled(); return (LRESULT)TRUE; } LRESULT OnCompareItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { T* pT = static_cast(this); pT->SetMsgHandled(TRUE); bHandled = pT->IsMsgHandled(); return (LRESULT)pT->CompareItem((LPCOMPAREITEMSTRUCT)lParam); } LRESULT OnDeleteItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { T* pT = static_cast(this); pT->SetMsgHandled(TRUE); pT->DeleteItem((LPDELETEITEMSTRUCT)lParam); bHandled = pT->IsMsgHandled(); return (LRESULT)TRUE; } // Overrideables void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/) { // must be implemented ATLASSERT(FALSE); } void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) { if(lpMeasureItemStruct->CtlType != ODT_MENU) { // return default height for a system font T* pT = static_cast(this); HWND hWnd = pT->GetDlgItem(lpMeasureItemStruct->CtlID); CClientDC dc(hWnd); TEXTMETRIC tm = { 0 }; dc.GetTextMetrics(&tm); lpMeasureItemStruct->itemHeight = tm.tmHeight; } else lpMeasureItemStruct->itemHeight = ::GetSystemMetrics(SM_CYMENU); } int CompareItem(LPCOMPAREITEMSTRUCT /*lpCompareItemStruct*/) { // all items are equal return 0; } void DeleteItem(LPDELETEITEMSTRUCT /*lpDeleteItemStruct*/) { // default - nothing } }; /////////////////////////////////////////////////////////////////////////////// // Update UI macros // these build the Update UI map inside a class definition #define BEGIN_UPDATE_UI_MAP(thisClass) \ static const CUpdateUIBase::_AtlUpdateUIMap* GetUpdateUIMap() \ { \ static const _AtlUpdateUIMap theMap[] = \ { #define UPDATE_ELEMENT(nID, wType) \ { nID, wType }, #define END_UPDATE_UI_MAP() \ { (WORD)-1, 0 } \ }; \ return theMap; \ } /////////////////////////////////////////////////////////////////////////////// // CUpdateUI - manages UI elements updating class CUpdateUIBase { public: // constants enum { // UI element type UPDUI_MENUPOPUP = 0x0001, UPDUI_MENUBAR = 0x0002, UPDUI_CHILDWINDOW = 0x0004, UPDUI_TOOLBAR = 0x0008, UPDUI_STATUSBAR = 0x0010, // state UPDUI_ENABLED = 0x0000, UPDUI_DISABLED = 0x0100, UPDUI_CHECKED = 0x0200, UPDUI_CHECKED2 = 0x0400, UPDUI_RADIO = 0x0800, UPDUI_DEFAULT = 0x1000, UPDUI_TEXT = 0x2000, // internal state UPDUI_CLEARDEFAULT = 0x4000, }; // element data struct _AtlUpdateUIElement { HWND m_hWnd; WORD m_wType; bool operator ==(const _AtlUpdateUIElement& e) const { return (m_hWnd == e.m_hWnd && m_wType == e.m_wType); } }; // map data struct _AtlUpdateUIMap { WORD m_nID; WORD m_wType; bool operator ==(const _AtlUpdateUIMap& e) const { return (m_nID == e.m_nID && m_wType == e.m_wType); } }; // instance data #pragma warning(push) #pragma warning(disable: 4201) // nameless unions are part of C++ struct _AtlUpdateUIData { WORD m_wState; union { void* m_lpData; LPTSTR m_lpstrText; struct { WORD m_nIDFirst; WORD m_nIDLast; }; }; bool operator ==(const _AtlUpdateUIData& e) const { return (m_wState == e.m_wState && m_lpData == e.m_lpData); } }; #pragma warning(pop) ATL::CSimpleArray<_AtlUpdateUIElement> m_UIElements; // elements data const _AtlUpdateUIMap* m_pUIMap; // static UI data _AtlUpdateUIData* m_pUIData; // instance UI data WORD m_wDirtyType; // global dirty flag bool m_bBlockAccelerators; // Constructor, destructor CUpdateUIBase() : m_pUIMap(NULL), m_pUIData(NULL), m_wDirtyType(0), m_bBlockAccelerators(false) { } ~CUpdateUIBase() { if(m_pUIMap != NULL && m_pUIData != NULL) { const _AtlUpdateUIMap* pUIMap = m_pUIMap; _AtlUpdateUIData* pUIData = m_pUIData; while(pUIMap->m_nID != (WORD)-1) { if(pUIData->m_wState & UPDUI_TEXT) delete [] pUIData->m_lpstrText; pUIMap++; pUIData++; } delete [] m_pUIData; } } // Check for disabled commands bool UIGetBlockAccelerators() const { return m_bBlockAccelerators; } bool UISetBlockAccelerators(bool bBlock) { bool bOld = m_bBlockAccelerators; m_bBlockAccelerators = bBlock; return bOld; } // Add elements BOOL UIAddMenuBar(HWND hWnd) // menu bar (main menu) { if(hWnd == NULL) return FALSE; _AtlUpdateUIElement e; e.m_hWnd = hWnd; e.m_wType = UPDUI_MENUBAR; return m_UIElements.Add(e); } BOOL UIAddToolBar(HWND hWnd) // toolbar { if(hWnd == NULL) return FALSE; _AtlUpdateUIElement e; e.m_hWnd = hWnd; e.m_wType = UPDUI_TOOLBAR; return m_UIElements.Add(e); } BOOL UIAddStatusBar(HWND hWnd) // status bar { if(hWnd == NULL) return FALSE; _AtlUpdateUIElement e; e.m_hWnd = hWnd; e.m_wType = UPDUI_STATUSBAR; return m_UIElements.Add(e); } BOOL UIAddChildWindowContainer(HWND hWnd) // child window { if(hWnd == NULL) return FALSE; _AtlUpdateUIElement e; e.m_hWnd = hWnd; e.m_wType = UPDUI_CHILDWINDOW; return m_UIElements.Add(e); } // Message map for popup menu updates and accelerator blocking BEGIN_MSG_MAP(CUpdateUIBase) MESSAGE_HANDLER(WM_INITMENUPOPUP, OnInitMenuPopup) MESSAGE_HANDLER(WM_COMMAND, OnCommand) END_MSG_MAP() LRESULT OnInitMenuPopup(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { bHandled = FALSE; HMENU hMenu = (HMENU)wParam; if(hMenu == NULL) return 1; _AtlUpdateUIData* pUIData = m_pUIData; if(pUIData == NULL) return 1; const _AtlUpdateUIMap* pMap = m_pUIMap; while(pMap->m_nID != (WORD)-1) { if(pMap->m_wType & UPDUI_MENUPOPUP) { UIUpdateMenuBarElement(pMap->m_nID, pUIData, hMenu); if((pUIData->m_wState & UPDUI_RADIO) != 0) ::CheckMenuRadioItem(hMenu, pUIData->m_nIDFirst, pUIData->m_nIDLast, pMap->m_nID, MF_BYCOMMAND); } pMap++; pUIData++; } return 0; } LRESULT OnCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { bHandled = FALSE; if(m_bBlockAccelerators && HIWORD(wParam) == 1) // accelerators only { int nID = LOWORD(wParam); if((UIGetState(nID) & UPDUI_DISABLED) == UPDUI_DISABLED) { ATLTRACE2(atlTraceUI, 0, _T("CUpdateUIBase::OnCommand - blocked disabled command 0x%4.4X\n"), nID); bHandled = TRUE; // eat the command, UI item is disabled } } return 0; } // methods for setting UI element state BOOL UIEnable(int nID, BOOL bEnable, BOOL bForceUpdate = FALSE) { const _AtlUpdateUIMap* pMap = m_pUIMap; _AtlUpdateUIData* pUIData = m_pUIData; if(pUIData == NULL) return FALSE; for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++) { if(nID == (int)pMap->m_nID) { if(bEnable) { if(pUIData->m_wState & UPDUI_DISABLED) { pUIData->m_wState |= pMap->m_wType; pUIData->m_wState &= ~UPDUI_DISABLED; } } else { if(!(pUIData->m_wState & UPDUI_DISABLED)) { pUIData->m_wState |= pMap->m_wType; pUIData->m_wState |= UPDUI_DISABLED; } } if(bForceUpdate) pUIData->m_wState |= pMap->m_wType; if(pUIData->m_wState & pMap->m_wType) m_wDirtyType |= pMap->m_wType; break; // found } } return TRUE; } BOOL UISetCheck(int nID, int nCheck, BOOL bForceUpdate = FALSE) { const _AtlUpdateUIMap* pMap = m_pUIMap; _AtlUpdateUIData* pUIData = m_pUIData; if(pUIData == NULL) return FALSE; for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++) { if(nID == (int)pMap->m_nID) { switch(nCheck) { case 0: if((pUIData->m_wState & UPDUI_CHECKED) || (pUIData->m_wState & UPDUI_CHECKED2)) { pUIData->m_wState |= pMap->m_wType; pUIData->m_wState &= ~(UPDUI_CHECKED | UPDUI_CHECKED2); } break; case 1: if(!(pUIData->m_wState & UPDUI_CHECKED)) { pUIData->m_wState |= pMap->m_wType; pUIData->m_wState &= ~UPDUI_CHECKED2; pUIData->m_wState |= UPDUI_CHECKED; } break; case 2: if(!(pUIData->m_wState & UPDUI_CHECKED2)) { pUIData->m_wState |= pMap->m_wType; pUIData->m_wState &= ~UPDUI_CHECKED; pUIData->m_wState |= UPDUI_CHECKED2; } break; } if(bForceUpdate) pUIData->m_wState |= pMap->m_wType; if(pUIData->m_wState & pMap->m_wType) m_wDirtyType |= pMap->m_wType; break; // found } } return TRUE; } // variant that supports bool (checked/not-checked, no intermediate state) BOOL UISetCheck(int nID, bool bCheck, BOOL bForceUpdate = FALSE) { return UISetCheck(nID, bCheck ? 1 : 0, bForceUpdate); } BOOL UISetRadio(int nID, BOOL bRadio, BOOL bForceUpdate = FALSE) { const _AtlUpdateUIMap* pMap = m_pUIMap; _AtlUpdateUIData* pUIData = m_pUIData; if(pUIData == NULL) return FALSE; for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++) { if(nID == (int)pMap->m_nID) { if(bRadio) { if(!(pUIData->m_wState & UPDUI_RADIO)) { pUIData->m_wState |= pMap->m_wType; pUIData->m_wState |= UPDUI_RADIO; } } else { if(pUIData->m_wState & UPDUI_RADIO) { pUIData->m_wState |= pMap->m_wType; pUIData->m_wState &= ~UPDUI_RADIO; } } if(bForceUpdate) pUIData->m_wState |= pMap->m_wType; if(pUIData->m_wState & pMap->m_wType) m_wDirtyType |= pMap->m_wType; break; // found } } return TRUE; } // for menu items BOOL UISetRadioMenuItem(int nID, int nIDFirst, int nIDLast, BOOL bForceUpdate = FALSE) { const _AtlUpdateUIMap* pMap = m_pUIMap; _AtlUpdateUIData* pUIData = m_pUIData; if(pUIData == NULL) return FALSE; for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++) { if(nID == (int)pMap->m_nID) { pUIData->m_wState |= pMap->m_wType; pUIData->m_wState |= UPDUI_RADIO; pUIData->m_nIDFirst = (WORD)nIDFirst; pUIData->m_nIDLast = (WORD)nIDLast; if(bForceUpdate) pUIData->m_wState |= pMap->m_wType; if(pUIData->m_wState & pMap->m_wType) m_wDirtyType |= pMap->m_wType; } else if(pMap->m_nID >= nIDFirst && pMap->m_nID <= nIDLast) { if(pUIData->m_wState & UPDUI_RADIO) { pUIData->m_wState &= ~pMap->m_wType; pUIData->m_wState &= ~UPDUI_RADIO; pUIData->m_nIDFirst = 0; pUIData->m_nIDLast = 0; } } if(pMap->m_nID == nIDLast) break; } return TRUE; } BOOL UISetText(int nID, LPCTSTR lpstrText, BOOL bForceUpdate = FALSE) { const _AtlUpdateUIMap* pMap = m_pUIMap; _AtlUpdateUIData* pUIData = m_pUIData; if(pUIData == NULL) return FALSE; if(lpstrText == NULL) lpstrText = _T(""); for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++) { if(nID == (int)pMap->m_nID) { if(pUIData->m_lpstrText == NULL || lstrcmp(pUIData->m_lpstrText, lpstrText)) { delete [] pUIData->m_lpstrText; pUIData->m_lpstrText = NULL; int nStrLen = lstrlen(lpstrText); ATLTRY(pUIData->m_lpstrText = new TCHAR[nStrLen + 1]); if(pUIData->m_lpstrText == NULL) { ATLTRACE2(atlTraceUI, 0, _T("UISetText - memory allocation failed\n")); break; } SecureHelper::strcpy_x(pUIData->m_lpstrText, nStrLen + 1, lpstrText); pUIData->m_wState |= (UPDUI_TEXT | pMap->m_wType); } if(bForceUpdate) pUIData->m_wState |= (UPDUI_TEXT | pMap->m_wType); if(pUIData->m_wState & pMap->m_wType) m_wDirtyType |= pMap->m_wType; break; // found } } return TRUE; } BOOL UISetDefault(int nID, BOOL bDefault, BOOL bForceUpdate = FALSE) { const _AtlUpdateUIMap* pMap = m_pUIMap; _AtlUpdateUIData* pUIData = m_pUIData; if(pUIData == NULL) return FALSE; for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++) { if(nID == (int)pMap->m_nID) { if(bDefault) { if((pUIData->m_wState & UPDUI_DEFAULT) == 0) { pUIData->m_wState |= pMap->m_wType; pUIData->m_wState |= UPDUI_DEFAULT; } } else { if((pUIData->m_wState & UPDUI_DEFAULT) != 0) { pUIData->m_wState |= pMap->m_wType; pUIData->m_wState &= ~UPDUI_DEFAULT; pUIData->m_wState |= UPDUI_CLEARDEFAULT; } } if(bForceUpdate) pUIData->m_wState |= pMap->m_wType; if(pUIData->m_wState & pMap->m_wType) m_wDirtyType |= pMap->m_wType; break; // found } } return TRUE; } // methods for complete state set/get BOOL UISetState(int nID, DWORD dwState) { const _AtlUpdateUIMap* pMap = m_pUIMap; _AtlUpdateUIData* pUIData = m_pUIData; if(pUIData == NULL) return FALSE; for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++) { if(nID == (int)pMap->m_nID) { pUIData->m_wState = (WORD)(dwState | pMap->m_wType); m_wDirtyType |= pMap->m_wType; break; // found } } return TRUE; } DWORD UIGetState(int nID) { const _AtlUpdateUIMap* pMap = m_pUIMap; _AtlUpdateUIData* pUIData = m_pUIData; if(pUIData == NULL) return 0; for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++) { if(nID == (int)pMap->m_nID) return pUIData->m_wState; } return 0; } // methods for updating UI #ifndef _WIN32_WCE BOOL UIUpdateMenuBar(BOOL bForceUpdate = FALSE, BOOL bMainMenu = FALSE) { if(!(m_wDirtyType & UPDUI_MENUBAR) && !bForceUpdate) return TRUE; const _AtlUpdateUIMap* pMap = m_pUIMap; _AtlUpdateUIData* pUIData = m_pUIData; if(pUIData == NULL) return FALSE; while(pMap->m_nID != (WORD)-1) { for(int i = 0; i < m_UIElements.GetSize(); i++) { if(m_UIElements[i].m_wType == UPDUI_MENUBAR) { HMENU hMenu = ::GetMenu(m_UIElements[i].m_hWnd); if(hMenu != NULL && (pUIData->m_wState & UPDUI_MENUBAR) && (pMap->m_wType & UPDUI_MENUBAR)) UIUpdateMenuBarElement(pMap->m_nID, pUIData, hMenu); } if(bMainMenu) ::DrawMenuBar(m_UIElements[i].m_hWnd); } pMap++; pUIData->m_wState &= ~UPDUI_MENUBAR; if(pUIData->m_wState & UPDUI_TEXT) { delete [] pUIData->m_lpstrText; pUIData->m_lpstrText = NULL; pUIData->m_wState &= ~UPDUI_TEXT; } pUIData++; } m_wDirtyType &= ~UPDUI_MENUBAR; return TRUE; } #endif // !_WIN32_WCE BOOL UIUpdateToolBar(BOOL bForceUpdate = FALSE) { if(!(m_wDirtyType & UPDUI_TOOLBAR) && !bForceUpdate) return TRUE; const _AtlUpdateUIMap* pMap = m_pUIMap; _AtlUpdateUIData* pUIData = m_pUIData; if(pUIData == NULL) return FALSE; while(pMap->m_nID != (WORD)-1) { for(int i = 0; i < m_UIElements.GetSize(); i++) { if(m_UIElements[i].m_wType == UPDUI_TOOLBAR) { if((pUIData->m_wState & UPDUI_TOOLBAR) && (pMap->m_wType & UPDUI_TOOLBAR)) UIUpdateToolBarElement(pMap->m_nID, pUIData, m_UIElements[i].m_hWnd); } } pMap++; pUIData->m_wState &= ~UPDUI_TOOLBAR; pUIData++; } m_wDirtyType &= ~UPDUI_TOOLBAR; return TRUE; } BOOL UIUpdateStatusBar(BOOL bForceUpdate = FALSE) { if(!(m_wDirtyType & UPDUI_STATUSBAR) && !bForceUpdate) return TRUE; const _AtlUpdateUIMap* pMap = m_pUIMap; _AtlUpdateUIData* pUIData = m_pUIData; if(pUIData == NULL) return FALSE; while(pMap->m_nID != (WORD)-1) { for(int i = 0; i < m_UIElements.GetSize(); i++) { if(m_UIElements[i].m_wType == UPDUI_STATUSBAR) { if((pUIData->m_wState & UPDUI_STATUSBAR) && (pMap->m_wType & UPDUI_STATUSBAR)) UIUpdateStatusBarElement(pMap->m_nID, pUIData, m_UIElements[i].m_hWnd); } } pMap++; pUIData->m_wState &= ~UPDUI_STATUSBAR; if(pUIData->m_wState & UPDUI_TEXT) { delete [] pUIData->m_lpstrText; pUIData->m_lpstrText = NULL; pUIData->m_wState &= ~UPDUI_TEXT; } pUIData++; } m_wDirtyType &= ~UPDUI_STATUSBAR; return TRUE; } BOOL UIUpdateChildWindows(BOOL bForceUpdate = FALSE) { if(!(m_wDirtyType & UPDUI_CHILDWINDOW) && !bForceUpdate) return TRUE; const _AtlUpdateUIMap* pMap = m_pUIMap; _AtlUpdateUIData* pUIData = m_pUIData; if(pUIData == NULL) return FALSE; while(pMap->m_nID != (WORD)-1) { for(int i = 0; i < m_UIElements.GetSize(); i++) { if(m_UIElements[i].m_wType == UPDUI_CHILDWINDOW) { if((pUIData->m_wState & UPDUI_CHILDWINDOW) && (pMap->m_wType & UPDUI_CHILDWINDOW)) UIUpdateChildWindow(pMap->m_nID, pUIData, m_UIElements[i].m_hWnd); } } pMap++; pUIData->m_wState &= ~UPDUI_CHILDWINDOW; if(pUIData->m_wState & UPDUI_TEXT) { delete [] pUIData->m_lpstrText; pUIData->m_lpstrText = NULL; pUIData->m_wState &= ~UPDUI_TEXT; } pUIData++; } m_wDirtyType &= ~UPDUI_CHILDWINDOW; return TRUE; } // internal element specific methods static void UIUpdateMenuBarElement(int nID, _AtlUpdateUIData* pUIData, HMENU hMenu) { #ifndef _WIN32_WCE if((pUIData->m_wState & UPDUI_CLEARDEFAULT) != 0) { ::SetMenuDefaultItem(hMenu, (UINT)-1, 0); pUIData->m_wState &= ~UPDUI_CLEARDEFAULT; } #endif // !_WIN32_WCE CMenuItemInfo mii; mii.fMask = MIIM_STATE; mii.wID = nID; #ifndef _WIN32_WCE if((pUIData->m_wState & UPDUI_DISABLED) != 0) mii.fState |= MFS_DISABLED | MFS_GRAYED; else mii.fState |= MFS_ENABLED; if((pUIData->m_wState & UPDUI_CHECKED) != 0) mii.fState |= MFS_CHECKED; else mii.fState |= MFS_UNCHECKED; if((pUIData->m_wState & UPDUI_DEFAULT) != 0) mii.fState |= MFS_DEFAULT; #else // CE specific // ::SetMenuItemInfo() can't disable or check menu items // on Windows CE, so we have to do that directly UINT uEnable = MF_BYCOMMAND; if((pUIData->m_wState & UPDUI_DISABLED) != 0) uEnable |= MF_GRAYED; else uEnable |= MF_ENABLED; ::EnableMenuItem(hMenu, nID, uEnable); UINT uCheck = MF_BYCOMMAND; if((pUIData->m_wState & UPDUI_CHECKED) != 0) uCheck |= MF_CHECKED; else uCheck |= MF_UNCHECKED; ::CheckMenuItem(hMenu, nID, uCheck); #endif // _WIN32_WCE if((pUIData->m_wState & UPDUI_TEXT) != 0) { CMenuItemInfo miiNow; miiNow.fMask = MIIM_TYPE; miiNow.wID = nID; if(::GetMenuItemInfo(hMenu, nID, FALSE, &miiNow)) { mii.fMask |= MIIM_TYPE; // MFT_BITMAP and MFT_SEPARATOR don't go together with MFT_STRING #ifndef _WIN32_WCE mii.fType |= (miiNow.fType & ~(MFT_BITMAP | MFT_SEPARATOR)) | MFT_STRING; #else // CE specific mii.fType |= (miiNow.fType & ~(MFT_SEPARATOR)) | MFT_STRING; #endif // _WIN32_WCE mii.dwTypeData = pUIData->m_lpstrText; } } ::SetMenuItemInfo(hMenu, nID, FALSE, &mii); } static void UIUpdateToolBarElement(int nID, _AtlUpdateUIData* pUIData, HWND hWndToolBar) { // Note: only handles enabled/disabled, checked state, and radio (press) ::SendMessage(hWndToolBar, TB_ENABLEBUTTON, nID, (LPARAM)(pUIData->m_wState & UPDUI_DISABLED) ? FALSE : TRUE); ::SendMessage(hWndToolBar, TB_CHECKBUTTON, nID, (LPARAM)(pUIData->m_wState & UPDUI_CHECKED) ? TRUE : FALSE); ::SendMessage(hWndToolBar, TB_INDETERMINATE, nID, (LPARAM)(pUIData->m_wState & UPDUI_CHECKED2) ? TRUE : FALSE); ::SendMessage(hWndToolBar, TB_PRESSBUTTON, nID, (LPARAM)(pUIData->m_wState & UPDUI_RADIO) ? TRUE : FALSE); } static void UIUpdateStatusBarElement(int nID, _AtlUpdateUIData* pUIData, HWND hWndStatusBar) { // Note: only handles text if(pUIData->m_wState & UPDUI_TEXT) ::SendMessage(hWndStatusBar, SB_SETTEXT, nID, (LPARAM)pUIData->m_lpstrText); } static void UIUpdateChildWindow(int nID, _AtlUpdateUIData* pUIData, HWND hWnd) { HWND hChild = ::GetDlgItem(hWnd, nID); ::EnableWindow(hChild, (pUIData->m_wState & UPDUI_DISABLED) ? FALSE : TRUE); // for check and radio, assume that window is a button int nCheck = BST_UNCHECKED; if(pUIData->m_wState & UPDUI_CHECKED || pUIData->m_wState & UPDUI_RADIO) nCheck = BST_CHECKED; else if(pUIData->m_wState & UPDUI_CHECKED2) nCheck = BST_INDETERMINATE; ::SendMessage(hChild, BM_SETCHECK, nCheck, 0L); if(pUIData->m_wState & UPDUI_DEFAULT) { DWORD dwRet = (DWORD)::SendMessage(hWnd, DM_GETDEFID, 0, 0L); if(HIWORD(dwRet) == DC_HASDEFID) { HWND hOldDef = ::GetDlgItem(hWnd, (int)(short)LOWORD(dwRet)); // remove BS_DEFPUSHBUTTON ::SendMessage(hOldDef, BM_SETSTYLE, BS_PUSHBUTTON, MAKELPARAM(TRUE, 0)); } ::SendMessage(hWnd, DM_SETDEFID, nID, 0L); } if(pUIData->m_wState & UPDUI_TEXT) ::SetWindowText(hChild, pUIData->m_lpstrText); } }; template class CUpdateUI : public CUpdateUIBase { public: CUpdateUI() { T* pT = static_cast(this); pT; const _AtlUpdateUIMap* pMap = pT->GetUpdateUIMap(); m_pUIMap = pMap; ATLASSERT(m_pUIMap != NULL); int nCount = 1; for( ; pMap->m_nID != (WORD)-1; nCount++) pMap++; // check for duplicates (debug only) #ifdef _DEBUG for(int i = 0; i < nCount; i++) { for(int j = 0; j < nCount; j++) { // shouldn't have duplicates in the update UI map if(i != j) ATLASSERT(m_pUIMap[j].m_nID != m_pUIMap[i].m_nID); } } #endif // _DEBUG ATLTRY(m_pUIData = new _AtlUpdateUIData[nCount]); ATLASSERT(m_pUIData != NULL); if(m_pUIData != NULL) memset(m_pUIData, 0, sizeof(_AtlUpdateUIData) * nCount); } }; /////////////////////////////////////////////////////////////////////////////// // CDynamicUpdateUI - allows update elements to dynamically added and removed // in addition to a static update UI map template class CDynamicUpdateUI : public CUpdateUIBase { public: // Data members ATL::CSimpleArray<_AtlUpdateUIMap> m_arrUIMap; // copy of the static UI data ATL::CSimpleArray<_AtlUpdateUIData> m_arrUIData; // instance UI data // Constructor/destructor CDynamicUpdateUI() { T* pT = static_cast(this); pT; const _AtlUpdateUIMap* pMap = pT->GetUpdateUIMap(); ATLASSERT(pMap != NULL); for(;;) { BOOL bRet = m_arrUIMap.Add(*(_AtlUpdateUIMap*)pMap); ATLASSERT(bRet); if(bRet != FALSE) { _AtlUpdateUIData data = { 0, NULL }; bRet = m_arrUIData.Add(data); ATLASSERT(bRet); } if(pMap->m_nID == (WORD)-1) break; pMap++; } ATLASSERT(m_arrUIMap.GetSize() == m_arrUIData.GetSize()); #ifdef _DEBUG // check for duplicates (debug only) for(int i = 0; i < m_arrUIMap.GetSize(); i++) { for(int j = 0; j < m_arrUIMap.GetSize(); j++) { // shouldn't have duplicates in the update UI map if(i != j) ATLASSERT(m_arrUIMap[j].m_nID != m_arrUIMap[i].m_nID); } } #endif // _DEBUG // Set internal data pointers to point to the new data arrays m_pUIMap = m_arrUIMap.m_aT; m_pUIData = m_arrUIData.m_aT; } ~CDynamicUpdateUI() { for(int i = 0; i < m_arrUIData.GetSize(); i++) { if((m_arrUIData[i].m_wState & UPDUI_TEXT) != 0) delete [] m_arrUIData[i].m_lpstrText; } // Reset internal data pointers (memory will be released by CSimpleArray d-tor) m_pUIMap = NULL; m_pUIData = NULL; } // Methods for dynamically adding and removing update elements bool UIAddUpdateElement(WORD nID, WORD wType) { // check for duplicates for(int i = 0; i < m_arrUIMap.GetSize(); i++) { // shouldn't have duplicates in the update UI map ATLASSERT(m_arrUIMap[i].m_nID != nID); if(m_arrUIMap[i].m_nID == nID) return false; } bool bRetVal = false; // Add new end element _AtlUpdateUIMap uumEnd = { (WORD)-1, 0 }; BOOL bRet = m_arrUIMap.Add(uumEnd); ATLASSERT(bRet); if(bRet != FALSE) { _AtlUpdateUIData uud = { 0, NULL }; bRet = m_arrUIData.Add(uud); ATLASSERT(bRet); // Set new data to the previous end element if(bRet != FALSE) { int nSize = m_arrUIMap.GetSize(); _AtlUpdateUIMap uum = { nID, wType }; m_arrUIMap.SetAtIndex(nSize - 2, uum); m_arrUIData.SetAtIndex(nSize - 2, uud); // Set internal data pointers again, just in case that memory moved m_pUIMap = m_arrUIMap.m_aT; m_pUIData = m_arrUIData.m_aT; bRetVal = true; } } return bRetVal; } bool UIRemoveUpdateElement(WORD nID) { bool bRetVal = false; for(int i = 0; i < m_arrUIMap.GetSize(); i++) { if(m_arrUIMap[i].m_nID == nID) { BOOL bRet = m_arrUIMap.RemoveAt(i); ATLASSERT(bRet); bRet = m_arrUIData.RemoveAt(i); ATLASSERT(bRet); bRetVal = true; break; } } return bRetVal; } }; /////////////////////////////////////////////////////////////////////////////// // CAutoUpdateUI : Automatic mapping of UI elements template class CAutoUpdateUI : public CDynamicUpdateUI { public: LPCTSTR UIGetText(int nID) { for(int i = 0; i < m_arrUIMap.GetSize(); i++) { if(m_arrUIMap[i].m_nID == nID) return m_arrUIData[i].m_lpstrText; } return NULL; } // Element template bool UIAddElement(UINT nID) { // check for existing UI map element for(int i = 0; i < m_arrUIMap.GetSize(); i++) { if(m_arrUIMap[i].m_nID == nID) { // set requested type m_arrUIMap[i].m_wType |= t_wType; return true; } } // Add element to UI map with requested type return UIAddUpdateElement((WORD)nID, t_wType); } template bool UIRemoveElement(UINT nID) { for(int i = 0; i < m_arrUIMap.GetSize(); i++) { if(m_arrUIMap[i].m_nID == nID) // matching UI map element { WORD wType = m_arrUIMap[i].m_wType & ~t_wType; if (wType != 0) // has other types { m_arrUIMap[i].m_wType = wType; // keep other types return true; } else { return UIRemoveUpdateElement((WORD)nID); } } } return false; } // Menu bool UIAddMenu(HMENU hMenu, bool bSetText = false) { #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800) using ATL::GetMenuString; #endif ATLASSERT(::IsMenu(hMenu)); MENUITEMINFO mii = {sizeof(MENUITEMINFO), MIIM_TYPE | MIIM_ID | MIIM_SUBMENU}; // Complete the UI map for (INT uItem = 0; CMenuHandle(hMenu).GetMenuItemInfo(uItem, TRUE, &mii); uItem++) { if(mii.hSubMenu) { // Add submenu to UI map UIAddMenu(mii.hSubMenu, bSetText); } else if (mii.wID != 0) { // Add element to UI map UIAddElement(mii.wID); #if !defined(_WIN32_WCE) || (_ATL_VER >= 0x0800) if (bSetText) { TCHAR sText[64] = { 0 }; if (GetMenuString(hMenu, uItem, sText, 64, MF_BYPOSITION)) UISetText(mii.wID, sText); } #else bSetText; #endif // !defined(_WIN32_WCE) || (_ATL_VER >= 0x0800) } } return true; } bool UIAddMenu(UINT uID, bool bSetText = false) { CMenu menu; ATLVERIFY(menu.LoadMenu(uID)); return UIAddMenu(menu, bSetText); } // ToolBar #if !defined(_WIN32_WCE) || (defined(_AUTOUI_CE_TOOLBAR) && defined(TBIF_BYINDEX)) bool UIAddToolBar(HWND hWndToolBar) { ATLASSERT(::IsWindow(hWndToolBar)); TBBUTTONINFO tbbi = {sizeof TBBUTTONINFO, TBIF_COMMAND | TBIF_STYLE | TBIF_BYINDEX}; // Add toolbar buttons for (int uItem = 0; ::SendMessage(hWndToolBar, TB_GETBUTTONINFO, uItem, (LPARAM)&tbbi) != -1; uItem++) { if (tbbi.fsStyle ^ BTNS_SEP) UIAddElement(tbbi.idCommand); } // Add embedded controls if any if (::GetWindow(hWndToolBar, GW_CHILD)) UIAddChildWindowContainer(hWndToolBar); return (CUpdateUIBase::UIAddToolBar(hWndToolBar) != FALSE); } #endif // !defined(_WIN32_WCE) || (defined(_AUTOUI_CE_TOOLBAR) && defined(TBIF_BYINDEX)) // Container bool UIAddChildWindowContainer(HWND hWnd) { ATLASSERT(::IsWindow(hWnd)); // Add children controls if any for (ATL::CWindow wCtl = ::GetWindow(hWnd, GW_CHILD); wCtl.IsWindow(); wCtl = wCtl.GetWindow(GW_HWNDNEXT)) { int id = wCtl.GetDlgCtrlID(); if(id != 0) UIAddElement(id); } return (CUpdateUIBase::UIAddChildWindowContainer(hWnd) != FALSE); } // StatusBar BOOL UIUpdateStatusBar(BOOL bForceUpdate = FALSE) { if(!(m_wDirtyType & UPDUI_STATUSBAR) && !bForceUpdate) return TRUE; for(int i = 0; i < m_arrUIMap.GetSize(); i++) { for(int e = 0; e < m_UIElements.GetSize(); e++) { if((m_UIElements[e].m_wType == UPDUI_STATUSBAR) && (m_arrUIMap[i].m_wType & UPDUI_STATUSBAR) && (m_arrUIData[i].m_wState & UPDUI_STATUSBAR)) { UIUpdateStatusBarElement(m_arrUIMap[i].m_nID, &m_arrUIData[i], m_UIElements[e].m_hWnd); m_arrUIData[i].m_wState &= ~UPDUI_STATUSBAR; if(m_arrUIData[i].m_wState & UPDUI_TEXT) m_arrUIData[i].m_wState &= ~UPDUI_TEXT; } } } m_wDirtyType &= ~UPDUI_STATUSBAR; return TRUE; } bool UIAddStatusBar(HWND hWndStatusBar, INT nPanes = 1) { ATLASSERT(::IsWindow(hWndStatusBar)); // Add StatusBar panes for (int iPane = 0; iPane < nPanes; iPane++) UIAddElement(ID_DEFAULT_PANE + iPane); return (CUpdateUIBase::UIAddStatusBar(hWndStatusBar) != FALSE); } // UI Map used if derived class has none BEGIN_UPDATE_UI_MAP(CAutoUpdateUI) END_UPDATE_UI_MAP() }; /////////////////////////////////////////////////////////////////////////////// // CDialogResize - provides support for resizing dialog controls // (works for any window that has child controls) // Put CDialogResize in the list of base classes for a dialog (or even plain window), // then implement DLGRESIZE map by specifying controls and groups of control // and using DLSZ_* values to specify how are they supposed to be resized. // // Notes: // - Resizeable border (WS_THICKFRAME style) should be set in the dialog template // for top level dialogs (popup or overlapped), so that users can resize the dialog. // - Some flags cannot be combined; for instance DLSZ_CENTER_X overrides DLSZ_SIZE_X, // DLSZ_SIZE_X overrides DLSZ_MOVE_X. X and Y flags can be combined. // - Order of controls is important - group controls are resized and moved based // on the position of the previous control in a group. // dialog resize map macros #define BEGIN_DLGRESIZE_MAP(thisClass) \ static const _AtlDlgResizeMap* GetDlgResizeMap() \ { \ static const _AtlDlgResizeMap theMap[] = \ { #define END_DLGRESIZE_MAP() \ { -1, 0 }, \ }; \ return theMap; \ } #define DLGRESIZE_CONTROL(id, flags) \ { id, flags }, #define BEGIN_DLGRESIZE_GROUP() \ { -1, _DLSZ_BEGIN_GROUP }, #define END_DLGRESIZE_GROUP() \ { -1, _DLSZ_END_GROUP }, template class CDialogResize { public: // Data declarations and members enum { DLSZ_SIZE_X = 0x00000001, DLSZ_SIZE_Y = 0x00000002, DLSZ_MOVE_X = 0x00000004, DLSZ_MOVE_Y = 0x00000008, DLSZ_REPAINT = 0x00000010, DLSZ_CENTER_X = 0x00000020, DLSZ_CENTER_Y = 0x00000040, // internal use only _DLSZ_BEGIN_GROUP = 0x00001000, _DLSZ_END_GROUP = 0x00002000, _DLSZ_GRIPPER = 0x00004000 }; struct _AtlDlgResizeMap { int m_nCtlID; DWORD m_dwResizeFlags; }; struct _AtlDlgResizeData { int m_nCtlID; DWORD m_dwResizeFlags; RECT m_rect; int GetGroupCount() const { return (int)LOBYTE(HIWORD(m_dwResizeFlags)); } void SetGroupCount(int nCount) { ATLASSERT(nCount > 0 && nCount < 256); DWORD dwCount = (DWORD)MAKELONG(0, MAKEWORD(nCount, 0)); m_dwResizeFlags &= 0xFF00FFFF; m_dwResizeFlags |= dwCount; } bool operator ==(const _AtlDlgResizeData& r) const { return (m_nCtlID == r.m_nCtlID && m_dwResizeFlags == r.m_dwResizeFlags); } }; ATL::CSimpleArray<_AtlDlgResizeData> m_arrData; SIZE m_sizeDialog; POINT m_ptMinTrackSize; bool m_bGripper; // Constructor CDialogResize() : m_bGripper(false) { m_sizeDialog.cx = 0; m_sizeDialog.cy = 0; m_ptMinTrackSize.x = -1; m_ptMinTrackSize.y = -1; } // Operations void DlgResize_Init(bool bAddGripper = true, bool bUseMinTrackSize = true, DWORD dwForceStyle = WS_CLIPCHILDREN) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); DWORD dwStyle = pT->GetStyle(); #ifdef _DEBUG // Debug only: Check if top level dialogs have a resizeable border. if(((dwStyle & WS_CHILD) == 0) && ((dwStyle & WS_THICKFRAME) == 0)) ATLTRACE2(atlTraceUI, 0, _T("DlgResize_Init - warning: top level dialog without the WS_THICKFRAME style - user cannot resize it\n")); #endif // _DEBUG // Force specified styles (default WS_CLIPCHILDREN reduces flicker) if((dwStyle & dwForceStyle) != dwForceStyle) pT->ModifyStyle(0, dwForceStyle); #ifndef _WIN32_WCE // Adding this style removes an empty icon that dialogs with WS_THICKFRAME have. // Setting icon to NULL is required when XP themes are active. // Note: This will not prevent adding an icon for the dialog using SetIcon() if((dwStyle & WS_CHILD) == 0) { pT->ModifyStyleEx(0, WS_EX_DLGMODALFRAME); if(pT->GetIcon(FALSE) == NULL) pT->SetIcon(NULL, FALSE); } #endif // Cleanup in case of multiple initialization // block: first check for the gripper control, destroy it if needed { ATL::CWindow wndGripper = pT->GetDlgItem(ATL_IDW_STATUS_BAR); if(wndGripper.IsWindow() && m_arrData.GetSize() > 0 && (m_arrData[0].m_dwResizeFlags & _DLSZ_GRIPPER) != 0) wndGripper.DestroyWindow(); } // clear out everything else m_arrData.RemoveAll(); m_sizeDialog.cx = 0; m_sizeDialog.cy = 0; m_ptMinTrackSize.x = -1; m_ptMinTrackSize.y = -1; // Get initial dialog client size RECT rectDlg = { 0 }; pT->GetClientRect(&rectDlg); m_sizeDialog.cx = rectDlg.right; m_sizeDialog.cy = rectDlg.bottom; #ifndef _WIN32_WCE // Create gripper if requested m_bGripper = false; if(bAddGripper) { // shouldn't exist already ATLASSERT(!::IsWindow(pT->GetDlgItem(ATL_IDW_STATUS_BAR))); if(!::IsWindow(pT->GetDlgItem(ATL_IDW_STATUS_BAR))) { ATL::CWindow wndGripper; wndGripper.Create(_T("SCROLLBAR"), pT->m_hWnd, rectDlg, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SBS_SIZEBOX | SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN, 0, ATL_IDW_STATUS_BAR); ATLASSERT(wndGripper.IsWindow()); if(wndGripper.IsWindow()) { m_bGripper = true; RECT rectCtl = { 0 }; wndGripper.GetWindowRect(&rectCtl); ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rectCtl, 2); _AtlDlgResizeData data = { ATL_IDW_STATUS_BAR, DLSZ_MOVE_X | DLSZ_MOVE_Y | DLSZ_REPAINT | _DLSZ_GRIPPER, { rectCtl.left, rectCtl.top, rectCtl.right, rectCtl.bottom } }; m_arrData.Add(data); } } } #else // CE specific bAddGripper; // avoid level 4 warning #endif // _WIN32_WCE // Get min track position if requested if(bUseMinTrackSize) { if((dwStyle & WS_CHILD) != 0) { RECT rect = { 0 }; pT->GetClientRect(&rect); m_ptMinTrackSize.x = rect.right - rect.left; m_ptMinTrackSize.y = rect.bottom - rect.top; } else { RECT rect = { 0 }; pT->GetWindowRect(&rect); m_ptMinTrackSize.x = rect.right - rect.left; m_ptMinTrackSize.y = rect.bottom - rect.top; } } // Walk the map and initialize data const _AtlDlgResizeMap* pMap = pT->GetDlgResizeMap(); ATLASSERT(pMap != NULL); int nGroupStart = -1; for(int nCount = 1; !(pMap->m_nCtlID == -1 && pMap->m_dwResizeFlags == 0); nCount++, pMap++) { if(pMap->m_nCtlID == -1) { switch(pMap->m_dwResizeFlags) { case _DLSZ_BEGIN_GROUP: ATLASSERT(nGroupStart == -1); nGroupStart = m_arrData.GetSize(); break; case _DLSZ_END_GROUP: { ATLASSERT(nGroupStart != -1); int nGroupCount = m_arrData.GetSize() - nGroupStart; m_arrData[nGroupStart].SetGroupCount(nGroupCount); nGroupStart = -1; } break; default: ATLASSERT(FALSE && _T("Invalid DLGRESIZE Map Entry")); break; } } else { // this ID conflicts with the default gripper one ATLASSERT(m_bGripper ? (pMap->m_nCtlID != ATL_IDW_STATUS_BAR) : TRUE); ATL::CWindow ctl = pT->GetDlgItem(pMap->m_nCtlID); ATLASSERT(ctl.IsWindow()); RECT rectCtl = { 0 }; ctl.GetWindowRect(&rectCtl); ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rectCtl, 2); DWORD dwGroupFlag = (nGroupStart != -1 && m_arrData.GetSize() == nGroupStart) ? _DLSZ_BEGIN_GROUP : 0; _AtlDlgResizeData data = { pMap->m_nCtlID, pMap->m_dwResizeFlags | dwGroupFlag, { rectCtl.left, rectCtl.top, rectCtl.right, rectCtl.bottom } }; m_arrData.Add(data); } } ATLASSERT((nGroupStart == -1) && _T("No End Group Entry in the DLGRESIZE Map")); } void DlgResize_UpdateLayout(int cxWidth, int cyHeight) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); // Restrict minimum size if requested if(((pT->GetStyle() & WS_CHILD) != 0) && m_ptMinTrackSize.x != -1 && m_ptMinTrackSize.y != -1) { if(cxWidth < m_ptMinTrackSize.x) cxWidth = m_ptMinTrackSize.x; if(cyHeight < m_ptMinTrackSize.y) cyHeight = m_ptMinTrackSize.y; } BOOL bVisible = pT->IsWindowVisible(); if(bVisible) pT->SetRedraw(FALSE); for(int i = 0; i < m_arrData.GetSize(); i++) { if((m_arrData[i].m_dwResizeFlags & _DLSZ_BEGIN_GROUP) != 0) // start of a group { int nGroupCount = m_arrData[i].GetGroupCount(); ATLASSERT(nGroupCount > 0 && i + nGroupCount - 1 < m_arrData.GetSize()); RECT rectGroup = m_arrData[i].m_rect; int j = 1; for(j = 1; j < nGroupCount; j++) { rectGroup.left = __min(rectGroup.left, m_arrData[i + j].m_rect.left); rectGroup.top = __min(rectGroup.top, m_arrData[i + j].m_rect.top); rectGroup.right = __max(rectGroup.right, m_arrData[i + j].m_rect.right); rectGroup.bottom = __max(rectGroup.bottom, m_arrData[i + j].m_rect.bottom); } for(j = 0; j < nGroupCount; j++) { _AtlDlgResizeData* pDataPrev = NULL; if(j > 0) pDataPrev = &(m_arrData[i + j - 1]); pT->DlgResize_PositionControl(cxWidth, cyHeight, rectGroup, m_arrData[i + j], true, pDataPrev); } i += nGroupCount - 1; // increment to skip all group controls } else // one control entry { RECT rectGroup = { 0 }; pT->DlgResize_PositionControl(cxWidth, cyHeight, rectGroup, m_arrData[i], false); } } if(bVisible) pT->SetRedraw(TRUE); pT->RedrawWindow(NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); } // Message map and handlers BEGIN_MSG_MAP(CDialogResize) MESSAGE_HANDLER(WM_SIZE, OnSize) #ifndef _WIN32_WCE MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo) #endif // _WIN32_WCE END_MSG_MAP() LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { T* pT = static_cast(this); #ifndef _WIN32_WCE if(m_bGripper) { ATL::CWindow wndGripper = pT->GetDlgItem(ATL_IDW_STATUS_BAR); if(wParam == SIZE_MAXIMIZED) wndGripper.ShowWindow(SW_HIDE); else if(wParam == SIZE_RESTORED) wndGripper.ShowWindow(SW_SHOW); } #endif // _WIN32_WCE if(wParam != SIZE_MINIMIZED) { ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DlgResize_UpdateLayout(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); } return 0; } #ifndef _WIN32_WCE LRESULT OnGetMinMaxInfo(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/) { if(m_ptMinTrackSize.x != -1 && m_ptMinTrackSize.y != -1) { LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam; lpMMI->ptMinTrackSize = m_ptMinTrackSize; } return 0; } #endif // _WIN32_WCE // Implementation bool DlgResize_PositionControl(int cxWidth, int cyHeight, RECT& rectGroup, _AtlDlgResizeData& data, bool bGroup, _AtlDlgResizeData* pDataPrev = NULL) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); ATL::CWindow ctl; RECT rectCtl = { 0 }; ctl = pT->GetDlgItem(data.m_nCtlID); if(!ctl.GetWindowRect(&rectCtl)) return false; ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rectCtl, 2); if(bGroup) { if((data.m_dwResizeFlags & DLSZ_CENTER_X) != 0) { int cxRight = rectGroup.right + cxWidth - m_sizeDialog.cx; int cxCtl = data.m_rect.right - data.m_rect.left; rectCtl.left = rectGroup.left + (cxRight - rectGroup.left - cxCtl) / 2; rectCtl.right = rectCtl.left + cxCtl; } else if((data.m_dwResizeFlags & (DLSZ_SIZE_X | DLSZ_MOVE_X)) != 0) { rectCtl.left = rectGroup.left + ::MulDiv(data.m_rect.left - rectGroup.left, rectGroup.right - rectGroup.left + (cxWidth - m_sizeDialog.cx), rectGroup.right - rectGroup.left); if((data.m_dwResizeFlags & DLSZ_SIZE_X) != 0) { rectCtl.right = rectGroup.left + ::MulDiv(data.m_rect.right - rectGroup.left, rectGroup.right - rectGroup.left + (cxWidth - m_sizeDialog.cx), rectGroup.right - rectGroup.left); if(pDataPrev != NULL) { ATL::CWindow ctlPrev = pT->GetDlgItem(pDataPrev->m_nCtlID); RECT rcPrev = { 0 }; ctlPrev.GetWindowRect(&rcPrev); ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rcPrev, 2); int dxAdjust = (rectCtl.left - rcPrev.right) - (data.m_rect.left - pDataPrev->m_rect.right); rcPrev.right += dxAdjust; ctlPrev.SetWindowPos(NULL, &rcPrev, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); } } else { rectCtl.right = rectCtl.left + (data.m_rect.right - data.m_rect.left); } } if((data.m_dwResizeFlags & DLSZ_CENTER_Y) != 0) { int cyBottom = rectGroup.bottom + cyHeight - m_sizeDialog.cy; int cyCtl = data.m_rect.bottom - data.m_rect.top; rectCtl.top = rectGroup.top + (cyBottom - rectGroup.top - cyCtl) / 2; rectCtl.bottom = rectCtl.top + cyCtl; } else if((data.m_dwResizeFlags & (DLSZ_SIZE_Y | DLSZ_MOVE_Y)) != 0) { rectCtl.top = rectGroup.top + ::MulDiv(data.m_rect.top - rectGroup.top, rectGroup.bottom - rectGroup.top + (cyHeight - m_sizeDialog.cy), rectGroup.bottom - rectGroup.top); if((data.m_dwResizeFlags & DLSZ_SIZE_Y) != 0) { rectCtl.bottom = rectGroup.top + ::MulDiv(data.m_rect.bottom - rectGroup.top, rectGroup.bottom - rectGroup.top + (cyHeight - m_sizeDialog.cy), rectGroup.bottom - rectGroup.top); if(pDataPrev != NULL) { ATL::CWindow ctlPrev = pT->GetDlgItem(pDataPrev->m_nCtlID); RECT rcPrev = { 0 }; ctlPrev.GetWindowRect(&rcPrev); ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rcPrev, 2); int dxAdjust = (rectCtl.top - rcPrev.bottom) - (data.m_rect.top - pDataPrev->m_rect.bottom); rcPrev.bottom += dxAdjust; ctlPrev.SetWindowPos(NULL, &rcPrev, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); } } else { rectCtl.bottom = rectCtl.top + (data.m_rect.bottom - data.m_rect.top); } } } else // no group { if((data.m_dwResizeFlags & DLSZ_CENTER_X) != 0) { int cxCtl = data.m_rect.right - data.m_rect.left; rectCtl.left = (cxWidth - cxCtl) / 2; rectCtl.right = rectCtl.left + cxCtl; } else if((data.m_dwResizeFlags & (DLSZ_SIZE_X | DLSZ_MOVE_X)) != 0) { rectCtl.right = data.m_rect.right + (cxWidth - m_sizeDialog.cx); if((data.m_dwResizeFlags & DLSZ_MOVE_X) != 0) rectCtl.left = rectCtl.right - (data.m_rect.right - data.m_rect.left); } if((data.m_dwResizeFlags & DLSZ_CENTER_Y) != 0) { int cyCtl = data.m_rect.bottom - data.m_rect.top; rectCtl.top = (cyHeight - cyCtl) / 2; rectCtl.bottom = rectCtl.top + cyCtl; } else if((data.m_dwResizeFlags & (DLSZ_SIZE_Y | DLSZ_MOVE_Y)) != 0) { rectCtl.bottom = data.m_rect.bottom + (cyHeight - m_sizeDialog.cy); if((data.m_dwResizeFlags & DLSZ_MOVE_Y) != 0) rectCtl.top = rectCtl.bottom - (data.m_rect.bottom - data.m_rect.top); } } if((data.m_dwResizeFlags & DLSZ_REPAINT) != 0) ctl.Invalidate(); if((data.m_dwResizeFlags & (DLSZ_SIZE_X | DLSZ_SIZE_Y | DLSZ_MOVE_X | DLSZ_MOVE_Y | DLSZ_REPAINT | DLSZ_CENTER_X | DLSZ_CENTER_Y)) != 0) ctl.SetWindowPos(NULL, &rectCtl, SWP_NOZORDER | SWP_NOACTIVATE); return true; } }; /////////////////////////////////////////////////////////////////////////////// // CDoubleBufferImpl - Provides double-buffer painting support to any window template class CDoubleBufferImpl { public: // Overrideables void DoPaint(CDCHandle /*dc*/) { // must be implemented in a derived class ATLASSERT(FALSE); } // Message map and handlers BEGIN_MSG_MAP(CDoubleBufferImpl) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) MESSAGE_HANDLER(WM_PAINT, OnPaint) #ifndef _WIN32_WCE MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint) #endif // !_WIN32_WCE END_MSG_MAP() LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return 1; // no background painting needed } LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); if(wParam != NULL) { RECT rect = { 0 }; pT->GetClientRect(&rect); CMemoryDC dcMem((HDC)wParam, rect); pT->DoPaint(dcMem.m_hDC); } else { CPaintDC dc(pT->m_hWnd); CMemoryDC dcMem(dc.m_hDC, dc.m_ps.rcPaint); pT->DoPaint(dcMem.m_hDC); } return 0; } }; /////////////////////////////////////////////////////////////////////////////// // CDoubleBufferWindowImpl - Implements a double-buffer painting window template class ATL_NO_VTABLE CDoubleBufferWindowImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CDoubleBufferImpl< T > { public: BEGIN_MSG_MAP(CDoubleBufferWindowImpl) CHAIN_MSG_MAP(CDoubleBufferImpl< T >) END_MSG_MAP() }; // command bar support #if !defined(__ATLCTRLW_H__) && !defined(_WIN32_WCE) #undef CBRM_GETMENU #undef CBRM_TRACKPOPUPMENU #undef CBRM_GETCMDBAR #undef CBRPOPUPMENU #endif // !defined(__ATLCTRLW_H__) && !defined(_WIN32_WCE) }; // namespace WTL #endif // __ATLFRAME_H__ ================================================ FILE: src/Setup/wtl90/atlgdi.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLGDI_H__ #define __ATLGDI_H__ #pragma once #ifndef __ATLAPP_H__ #error atlgdi.h requires atlapp.h to be included first #endif // protect template members from windowsx.h macros #ifdef _INC_WINDOWSX #undef CopyRgn #undef CreateBrush #undef CreatePen #undef SelectBrush #undef SelectPen #undef SelectFont #undef SelectBitmap #endif // _INC_WINDOWSX // required libraries #if !defined(_ATL_NO_MSIMG) && !defined(_WIN32_WCE) #pragma comment(lib, "msimg32.lib") #endif #if !defined(_ATL_NO_OPENGL) && !defined(_WIN32_WCE) #pragma comment(lib, "opengl32.lib") #endif /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CPenT // CBrushT // CLogFont // CFontT // CBitmapT // CPaletteT // CRgnT // CDCT // CPaintDC // CClientDC // CWindowDC // CMemoryDC // CEnhMetaFileInfo // CEnhMetaFileT // CEnhMetaFileDC // // Global functions: // AtlGetBitmapResourceInfo() // AtlGetBitmapResourceBitsPerPixel() // AtlIsAlphaBitmapResource() // AtlIsDib16() // AtlGetDibColorTableSize() // AtlGetDibNumColors(), // AtlGetDibBitmap() // AtlCopyBitmap() // AtlCreatePackedDib16() // AtlSetClipboardDib16() // AtlGetClipboardDib() namespace WTL { /////////////////////////////////////////////////////////////////////////////// // Bitmap resource helpers to extract bitmap information for a bitmap resource inline LPBITMAPINFOHEADER AtlGetBitmapResourceInfo(HMODULE hModule, ATL::_U_STRINGorID image) { HRSRC hResource = ::FindResource(hModule, image.m_lpstr, RT_BITMAP); ATLASSERT(hResource != NULL); HGLOBAL hGlobal = ::LoadResource(hModule, hResource); ATLASSERT(hGlobal != NULL); LPBITMAPINFOHEADER pBitmapInfoHeader = (LPBITMAPINFOHEADER)::LockResource(hGlobal); ATLASSERT(pBitmapInfoHeader != NULL); return pBitmapInfoHeader; } inline WORD AtlGetBitmapResourceBitsPerPixel(HMODULE hModule, ATL::_U_STRINGorID image) { LPBITMAPINFOHEADER pBitmapInfoHeader = AtlGetBitmapResourceInfo(hModule, image); ATLASSERT(pBitmapInfoHeader != NULL); return pBitmapInfoHeader->biBitCount; } inline WORD AtlGetBitmapResourceBitsPerPixel(ATL::_U_STRINGorID image) { return AtlGetBitmapResourceBitsPerPixel(ModuleHelper::GetResourceInstance(), image); } /////////////////////////////////////////////////////////////////////////////// // 32-bit (alpha channel) bitmap resource helper // Note: 32-bit (alpha channel) images work only on Windows XP with Common Controls version 6. // If you want your app to work on older version of Windows, load non-alpha images if Common // Controls version is less than 6. inline bool AtlIsAlphaBitmapResource(ATL::_U_STRINGorID image) { return (AtlGetBitmapResourceBitsPerPixel(image) == 32); } /////////////////////////////////////////////////////////////////////////////// // CPen template class CPenT { public: // Data members HPEN m_hPen; // Constructor/destructor/operators CPenT(HPEN hPen = NULL) : m_hPen(hPen) { } ~CPenT() { if(t_bManaged && m_hPen != NULL) DeleteObject(); } CPenT& operator =(HPEN hPen) { Attach(hPen); return *this; } void Attach(HPEN hPen) { if(t_bManaged && m_hPen != NULL && m_hPen != hPen) ::DeleteObject(m_hPen); m_hPen = hPen; } HPEN Detach() { HPEN hPen = m_hPen; m_hPen = NULL; return hPen; } operator HPEN() const { return m_hPen; } bool IsNull() const { return (m_hPen == NULL); } // Create methods HPEN CreatePen(int nPenStyle, int nWidth, COLORREF crColor) { ATLASSERT(m_hPen == NULL); m_hPen = ::CreatePen(nPenStyle, nWidth, crColor); return m_hPen; } #ifndef _WIN32_WCE HPEN CreatePen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount = 0, const DWORD* lpStyle = NULL) { ATLASSERT(m_hPen == NULL); m_hPen = ::ExtCreatePen(nPenStyle, nWidth, pLogBrush, nStyleCount, lpStyle); return m_hPen; } #endif // !_WIN32_WCE HPEN CreatePenIndirect(LPLOGPEN lpLogPen) { ATLASSERT(m_hPen == NULL); m_hPen = ::CreatePenIndirect(lpLogPen); return m_hPen; } BOOL DeleteObject() { ATLASSERT(m_hPen != NULL); BOOL bRet = ::DeleteObject(m_hPen); if(bRet) m_hPen = NULL; return bRet; } // Attributes int GetLogPen(LOGPEN* pLogPen) const { ATLASSERT(m_hPen != NULL); return ::GetObject(m_hPen, sizeof(LOGPEN), pLogPen); } bool GetLogPen(LOGPEN& LogPen) const { ATLASSERT(m_hPen != NULL); return (::GetObject(m_hPen, sizeof(LOGPEN), &LogPen) == sizeof(LOGPEN)); } #ifndef _WIN32_WCE int GetExtLogPen(EXTLOGPEN* pLogPen, int nSize = sizeof(EXTLOGPEN)) const { ATLASSERT(m_hPen != NULL); return ::GetObject(m_hPen, nSize, pLogPen); } bool GetExtLogPen(EXTLOGPEN& ExtLogPen, int nSize = sizeof(EXTLOGPEN)) const { ATLASSERT(m_hPen != NULL); int nRet = ::GetObject(m_hPen, nSize, &ExtLogPen); return ((nRet > 0) && (nRet <= nSize)); } #endif // !_WIN32_WCE }; typedef CPenT CPenHandle; typedef CPenT CPen; /////////////////////////////////////////////////////////////////////////////// // CBrush template class CBrushT { public: // Data members HBRUSH m_hBrush; // Constructor/destructor/operators CBrushT(HBRUSH hBrush = NULL) : m_hBrush(hBrush) { } ~CBrushT() { if(t_bManaged && m_hBrush != NULL) DeleteObject(); } CBrushT& operator =(HBRUSH hBrush) { Attach(hBrush); return *this; } void Attach(HBRUSH hBrush) { if(t_bManaged && m_hBrush != NULL && m_hBrush != hBrush) ::DeleteObject(m_hBrush); m_hBrush = hBrush; } HBRUSH Detach() { HBRUSH hBrush = m_hBrush; m_hBrush = NULL; return hBrush; } operator HBRUSH() const { return m_hBrush; } bool IsNull() const { return (m_hBrush == NULL); } // Create methods HBRUSH CreateSolidBrush(COLORREF crColor) { ATLASSERT(m_hBrush == NULL); m_hBrush = ::CreateSolidBrush(crColor); return m_hBrush; } #ifndef _WIN32_WCE HBRUSH CreateHatchBrush(int nIndex, COLORREF crColor) { ATLASSERT(m_hBrush == NULL); m_hBrush = ::CreateHatchBrush(nIndex, crColor); return m_hBrush; } #endif // !_WIN32_WCE #if !defined(_WIN32_WCE) || (_ATL_VER >= 0x0800) HBRUSH CreateBrushIndirect(const LOGBRUSH* lpLogBrush) { ATLASSERT(m_hBrush == NULL); #ifndef _WIN32_WCE m_hBrush = ::CreateBrushIndirect(lpLogBrush); #else // CE specific m_hBrush = ATL::CreateBrushIndirect(lpLogBrush); #endif // _WIN32_WCE return m_hBrush; } #endif // !defined(_WIN32_WCE) || (_ATL_VER >= 0x0800) HBRUSH CreatePatternBrush(HBITMAP hBitmap) { ATLASSERT(m_hBrush == NULL); m_hBrush = ::CreatePatternBrush(hBitmap); return m_hBrush; } HBRUSH CreateDIBPatternBrush(HGLOBAL hPackedDIB, UINT nUsage) { ATLASSERT(hPackedDIB != NULL); const void* lpPackedDIB = GlobalLock(hPackedDIB); ATLASSERT(lpPackedDIB != NULL); m_hBrush = ::CreateDIBPatternBrushPt(lpPackedDIB, nUsage); GlobalUnlock(hPackedDIB); return m_hBrush; } HBRUSH CreateDIBPatternBrush(const void* lpPackedDIB, UINT nUsage) { ATLASSERT(m_hBrush == NULL); m_hBrush = ::CreateDIBPatternBrushPt(lpPackedDIB, nUsage); return m_hBrush; } HBRUSH CreateSysColorBrush(int nIndex) { ATLASSERT(m_hBrush == NULL); m_hBrush = ::GetSysColorBrush(nIndex); return m_hBrush; } BOOL DeleteObject() { ATLASSERT(m_hBrush != NULL); BOOL bRet = ::DeleteObject(m_hBrush); if(bRet) m_hBrush = NULL; return bRet; } // Attributes int GetLogBrush(LOGBRUSH* pLogBrush) const { ATLASSERT(m_hBrush != NULL); return ::GetObject(m_hBrush, sizeof(LOGBRUSH), pLogBrush); } bool GetLogBrush(LOGBRUSH& LogBrush) const { ATLASSERT(m_hBrush != NULL); return (::GetObject(m_hBrush, sizeof(LOGBRUSH), &LogBrush) == sizeof(LOGBRUSH)); } }; typedef CBrushT CBrushHandle; typedef CBrushT CBrush; /////////////////////////////////////////////////////////////////////////////// // CFont class CLogFont : public LOGFONT { public: CLogFont() { memset(this, 0, sizeof(LOGFONT)); } CLogFont(const LOGFONT& lf) { Copy(&lf); } CLogFont(HFONT hFont) { ATLASSERT(::GetObjectType(hFont) == OBJ_FONT); ::GetObject(hFont, sizeof(LOGFONT), (LOGFONT*)this); } HFONT CreateFontIndirect() { return ::CreateFontIndirect(this); } void SetBold() { lfWeight = FW_BOLD; } bool IsBold() const { return (lfWeight >= FW_BOLD); } void MakeBolder(int iScale = 1) { lfWeight += FW_BOLD * iScale; } void MakeLarger(int iScale) { if(lfHeight > 0) lfHeight += iScale; else lfHeight -= iScale; } void SetHeight(LONG nPointSize, HDC hDC = NULL) { HDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL); // For MM_TEXT mapping mode lfHeight = -::MulDiv(nPointSize, ::GetDeviceCaps(hDC1, LOGPIXELSY), 72); if(hDC == NULL) ::ReleaseDC(NULL, hDC1); } LONG GetHeight(HDC hDC = NULL) const { HDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL); // For MM_TEXT mapping mode LONG nPointSize = ::MulDiv(-lfHeight, 72, ::GetDeviceCaps(hDC1, LOGPIXELSY)); if(hDC == NULL) ::ReleaseDC(NULL, hDC1); return nPointSize; } LONG GetDeciPointHeight(HDC hDC = NULL) const { HDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL); #ifndef _WIN32_WCE POINT ptOrg = { 0, 0 }; ::DPtoLP(hDC1, &ptOrg, 1); POINT pt = { 0, 0 }; pt.y = abs(lfHeight) + ptOrg.y; ::LPtoDP(hDC1, &pt,1); LONG nDeciPoint = ::MulDiv(pt.y, 720, ::GetDeviceCaps(hDC1, LOGPIXELSY)); // 72 points/inch, 10 decipoints/point #else // CE specific // DP and LP are always the same on CE LONG nDeciPoint = ::MulDiv(abs(lfHeight), 720, ::GetDeviceCaps(hDC1, LOGPIXELSY)); // 72 points/inch, 10 decipoints/point #endif // _WIN32_WCE if(hDC == NULL) ::ReleaseDC(NULL, hDC1); return nDeciPoint; } void SetHeightFromDeciPoint(LONG nDeciPtHeight, HDC hDC = NULL) { HDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL); #ifndef _WIN32_WCE POINT pt = { 0, 0 }; pt.y = ::MulDiv(::GetDeviceCaps(hDC1, LOGPIXELSY), nDeciPtHeight, 720); // 72 points/inch, 10 decipoints/point ::DPtoLP(hDC1, &pt, 1); POINT ptOrg = { 0, 0 }; ::DPtoLP(hDC1, &ptOrg, 1); lfHeight = -abs(pt.y - ptOrg.y); #else // CE specific // DP and LP are always the same on CE lfHeight = -abs(::MulDiv(::GetDeviceCaps(hDC1, LOGPIXELSY), nDeciPtHeight, 720)); // 72 points/inch, 10 decipoints/point #endif // _WIN32_WCE if(hDC == NULL) ::ReleaseDC(NULL, hDC1); } #ifndef _WIN32_WCE void SetCaptionFont() { NONCLIENTMETRICS ncm = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() }; ATLVERIFY(::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)); Copy(&ncm.lfCaptionFont); } void SetMenuFont() { NONCLIENTMETRICS ncm = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() }; ATLVERIFY(::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)); Copy(&ncm.lfMenuFont); } void SetStatusFont() { NONCLIENTMETRICS ncm = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() }; ATLVERIFY(::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)); Copy(&ncm.lfStatusFont); } void SetMessageBoxFont() { NONCLIENTMETRICS ncm = { RunTimeHelper::SizeOf_NONCLIENTMETRICS() }; ATLVERIFY(::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)); Copy(&ncm.lfMessageFont); } #endif // !_WIN32_WCE void Copy(const LOGFONT* pLogFont) { ATLASSERT(pLogFont != NULL); *(LOGFONT*)this = *pLogFont; } CLogFont& operator =(const CLogFont& src) { Copy(&src); return *this; } CLogFont& operator =(const LOGFONT& src) { Copy(&src); return *this; } CLogFont& operator =(HFONT hFont) { ATLASSERT(::GetObjectType(hFont) == OBJ_FONT); ::GetObject(hFont, sizeof(LOGFONT), (LOGFONT*)this); return *this; } bool operator ==(const LOGFONT& logfont) const { return(logfont.lfHeight == lfHeight && logfont.lfWidth == lfWidth && logfont.lfEscapement == lfEscapement && logfont.lfOrientation == lfOrientation && logfont.lfWeight == lfWeight && logfont.lfItalic == lfItalic && logfont.lfUnderline == lfUnderline && logfont.lfStrikeOut == lfStrikeOut && logfont.lfCharSet == lfCharSet && logfont.lfOutPrecision == lfOutPrecision && logfont.lfClipPrecision == lfClipPrecision && logfont.lfQuality == lfQuality && logfont.lfPitchAndFamily == lfPitchAndFamily && lstrcmp(logfont.lfFaceName, lfFaceName) == 0); } }; template class CFontT { public: // Data members HFONT m_hFont; // Constructor/destructor/operators CFontT(HFONT hFont = NULL) : m_hFont(hFont) { } ~CFontT() { if(t_bManaged && m_hFont != NULL) DeleteObject(); } CFontT& operator =(HFONT hFont) { Attach(hFont); return *this; } void Attach(HFONT hFont) { if(t_bManaged && m_hFont != NULL && m_hFont != hFont) ::DeleteObject(m_hFont); m_hFont = hFont; } HFONT Detach() { HFONT hFont = m_hFont; m_hFont = NULL; return hFont; } operator HFONT() const { return m_hFont; } bool IsNull() const { return (m_hFont == NULL); } // Create methods HFONT CreateFontIndirect(const LOGFONT* lpLogFont) { ATLASSERT(m_hFont == NULL); m_hFont = ::CreateFontIndirect(lpLogFont); return m_hFont; } #if !defined(_WIN32_WCE) && (_WIN32_WINNT >= 0x0500) HFONT CreateFontIndirectEx(CONST ENUMLOGFONTEXDV* penumlfex) { ATLASSERT(m_hFont == NULL); m_hFont = ::CreateFontIndirectEx(penumlfex); return m_hFont; } #endif // !defined(_WIN32_WCE) && (_WIN32_WINNT >= 0x0500) #if !defined(_WIN32_WCE) || (_ATL_VER >= 0x0800) HFONT CreateFont(int nHeight, int nWidth, int nEscapement, int nOrientation, int nWeight, BYTE bItalic, BYTE bUnderline, BYTE cStrikeOut, BYTE nCharSet, BYTE nOutPrecision, BYTE nClipPrecision, BYTE nQuality, BYTE nPitchAndFamily, LPCTSTR lpszFacename) { ATLASSERT(m_hFont == NULL); #ifndef _WIN32_WCE m_hFont = ::CreateFont(nHeight, nWidth, nEscapement, nOrientation, nWeight, bItalic, bUnderline, cStrikeOut, nCharSet, nOutPrecision, nClipPrecision, nQuality, nPitchAndFamily, lpszFacename); #else // CE specific m_hFont = ATL::CreateFont(nHeight, nWidth, nEscapement, nOrientation, nWeight, bItalic, bUnderline, cStrikeOut, nCharSet, nOutPrecision, nClipPrecision, nQuality, nPitchAndFamily, lpszFacename); #endif // _WIN32_WCE return m_hFont; } #endif // !defined(_WIN32_WCE) || (_ATL_VER >= 0x0800) HFONT CreatePointFont(int nPointSize, LPCTSTR lpszFaceName, HDC hDC = NULL, bool bBold = false, bool bItalic = false) { LOGFONT logFont = { 0 }; logFont.lfCharSet = DEFAULT_CHARSET; logFont.lfHeight = nPointSize; SecureHelper::strncpy_x(logFont.lfFaceName, _countof(logFont.lfFaceName), lpszFaceName, _TRUNCATE); if(bBold) logFont.lfWeight = FW_BOLD; if(bItalic) logFont.lfItalic = (BYTE)TRUE; return CreatePointFontIndirect(&logFont, hDC); } HFONT CreatePointFontIndirect(const LOGFONT* lpLogFont, HDC hDC = NULL) { HDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(NULL); // convert nPointSize to logical units based on hDC LOGFONT logFont = *lpLogFont; #ifndef _WIN32_WCE POINT pt = { 0, 0 }; pt.y = ::MulDiv(::GetDeviceCaps(hDC1, LOGPIXELSY), logFont.lfHeight, 720); // 72 points/inch, 10 decipoints/point ::DPtoLP(hDC1, &pt, 1); POINT ptOrg = { 0, 0 }; ::DPtoLP(hDC1, &ptOrg, 1); logFont.lfHeight = -abs(pt.y - ptOrg.y); #else // CE specific // DP and LP are always the same on CE logFont.lfHeight = -abs(::MulDiv(::GetDeviceCaps(hDC1, LOGPIXELSY), logFont.lfHeight, 720)); // 72 points/inch, 10 decipoints/point #endif // _WIN32_WCE if(hDC == NULL) ::ReleaseDC(NULL, hDC1); return CreateFontIndirect(&logFont); } BOOL DeleteObject() { ATLASSERT(m_hFont != NULL); BOOL bRet = ::DeleteObject(m_hFont); if(bRet) m_hFont = NULL; return bRet; } // Attributes int GetLogFont(LOGFONT* pLogFont) const { ATLASSERT(m_hFont != NULL); return ::GetObject(m_hFont, sizeof(LOGFONT), pLogFont); } bool GetLogFont(LOGFONT& LogFont) const { ATLASSERT(m_hFont != NULL); return (::GetObject(m_hFont, sizeof(LOGFONT), &LogFont) == sizeof(LOGFONT)); } }; typedef CFontT CFontHandle; typedef CFontT CFont; /////////////////////////////////////////////////////////////////////////////// // CBitmap template class CBitmapT { public: // Data members HBITMAP m_hBitmap; // Constructor/destructor/operators CBitmapT(HBITMAP hBitmap = NULL) : m_hBitmap(hBitmap) { } ~CBitmapT() { if(t_bManaged && m_hBitmap != NULL) DeleteObject(); } CBitmapT& operator =(HBITMAP hBitmap) { Attach(hBitmap); return *this; } void Attach(HBITMAP hBitmap) { if(t_bManaged && m_hBitmap != NULL&& m_hBitmap != hBitmap) ::DeleteObject(m_hBitmap); m_hBitmap = hBitmap; } HBITMAP Detach() { HBITMAP hBitmap = m_hBitmap; m_hBitmap = NULL; return hBitmap; } operator HBITMAP() const { return m_hBitmap; } bool IsNull() const { return (m_hBitmap == NULL); } // Create and load methods HBITMAP LoadBitmap(ATL::_U_STRINGorID bitmap) { ATLASSERT(m_hBitmap == NULL); m_hBitmap = ::LoadBitmap(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr); return m_hBitmap; } HBITMAP LoadOEMBitmap(UINT nIDBitmap) // for OBM_/OCR_/OIC_ { ATLASSERT(m_hBitmap == NULL); m_hBitmap = ::LoadBitmap(NULL, MAKEINTRESOURCE(nIDBitmap)); return m_hBitmap; } #ifndef _WIN32_WCE HBITMAP LoadMappedBitmap(UINT nIDBitmap, UINT nFlags = 0, LPCOLORMAP lpColorMap = NULL, int nMapSize = 0) { ATLASSERT(m_hBitmap == NULL); m_hBitmap = ::CreateMappedBitmap(ModuleHelper::GetResourceInstance(), nIDBitmap, (WORD)nFlags, lpColorMap, nMapSize); return m_hBitmap; } #endif // !_WIN32_WCE HBITMAP CreateBitmap(int nWidth, int nHeight, UINT nPlanes, UINT nBitsPerPixel, const void* lpBits) { ATLASSERT(m_hBitmap == NULL); m_hBitmap = ::CreateBitmap(nWidth, nHeight, nPlanes, nBitsPerPixel, lpBits); return m_hBitmap; } #ifndef _WIN32_WCE HBITMAP CreateBitmapIndirect(LPBITMAP lpBitmap) { ATLASSERT(m_hBitmap == NULL); m_hBitmap = ::CreateBitmapIndirect(lpBitmap); return m_hBitmap; } #endif // !_WIN32_WCE HBITMAP CreateCompatibleBitmap(HDC hDC, int nWidth, int nHeight) { ATLASSERT(m_hBitmap == NULL); m_hBitmap = ::CreateCompatibleBitmap(hDC, nWidth, nHeight); return m_hBitmap; } #ifndef _WIN32_WCE HBITMAP CreateDiscardableBitmap(HDC hDC, int nWidth, int nHeight) { ATLASSERT(m_hBitmap == NULL); m_hBitmap = ::CreateDiscardableBitmap(hDC, nWidth, nHeight); return m_hBitmap; } #endif // !_WIN32_WCE BOOL DeleteObject() { ATLASSERT(m_hBitmap != NULL); BOOL bRet = ::DeleteObject(m_hBitmap); if(bRet) m_hBitmap = NULL; return bRet; } // Attributes int GetBitmap(BITMAP* pBitMap) const { ATLASSERT(m_hBitmap != NULL); return ::GetObject(m_hBitmap, sizeof(BITMAP), pBitMap); } bool GetBitmap(BITMAP& bm) const { ATLASSERT(m_hBitmap != NULL); return (::GetObject(m_hBitmap, sizeof(BITMAP), &bm) == sizeof(BITMAP)); } bool GetSize(SIZE& size) const { ATLASSERT(m_hBitmap != NULL); BITMAP bm = { 0 }; if(!GetBitmap(&bm)) return false; size.cx = bm.bmWidth; size.cy = bm.bmHeight; return true; } #ifndef _WIN32_WCE DWORD GetBitmapBits(DWORD dwCount, LPVOID lpBits) const { ATLASSERT(m_hBitmap != NULL); return ::GetBitmapBits(m_hBitmap, dwCount, lpBits); } #endif // !_WIN32_WCE #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 410) DWORD SetBitmapBits(DWORD dwCount, const void* lpBits) { ATLASSERT(m_hBitmap != NULL); return ::SetBitmapBits(m_hBitmap, dwCount, lpBits); } #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 410) #ifndef _WIN32_WCE BOOL GetBitmapDimension(LPSIZE lpSize) const { ATLASSERT(m_hBitmap != NULL); return ::GetBitmapDimensionEx(m_hBitmap, lpSize); } BOOL SetBitmapDimension(int nWidth, int nHeight, LPSIZE lpSize = NULL) { ATLASSERT(m_hBitmap != NULL); return ::SetBitmapDimensionEx(m_hBitmap, nWidth, nHeight, lpSize); } // DIB support HBITMAP CreateDIBitmap(HDC hDC, CONST BITMAPINFOHEADER* lpbmih, DWORD dwInit, CONST VOID* lpbInit, CONST BITMAPINFO* lpbmi, UINT uColorUse) { ATLASSERT(m_hBitmap == NULL); m_hBitmap = ::CreateDIBitmap(hDC, lpbmih, dwInit, lpbInit, lpbmi, uColorUse); return m_hBitmap; } #endif // !_WIN32_WCE HBITMAP CreateDIBSection(HDC hDC, CONST BITMAPINFO* lpbmi, UINT uColorUse, VOID** ppvBits, HANDLE hSection, DWORD dwOffset) { ATLASSERT(m_hBitmap == NULL); m_hBitmap = ::CreateDIBSection(hDC, lpbmi, uColorUse, ppvBits, hSection, dwOffset); return m_hBitmap; } #ifndef _WIN32_WCE int GetDIBits(HDC hDC, UINT uStartScan, UINT cScanLines, LPVOID lpvBits, LPBITMAPINFO lpbmi, UINT uColorUse) const { ATLASSERT(m_hBitmap != NULL); return ::GetDIBits(hDC, m_hBitmap, uStartScan, cScanLines, lpvBits, lpbmi, uColorUse); } int SetDIBits(HDC hDC, UINT uStartScan, UINT cScanLines, CONST VOID* lpvBits, CONST BITMAPINFO* lpbmi, UINT uColorUse) { ATLASSERT(m_hBitmap != NULL); return ::SetDIBits(hDC, m_hBitmap, uStartScan, cScanLines, lpvBits, lpbmi, uColorUse); } #endif // !_WIN32_WCE }; typedef CBitmapT CBitmapHandle; typedef CBitmapT CBitmap; /////////////////////////////////////////////////////////////////////////////// // CPalette template class CPaletteT { public: // Data members HPALETTE m_hPalette; // Constructor/destructor/operators CPaletteT(HPALETTE hPalette = NULL) : m_hPalette(hPalette) { } ~CPaletteT() { if(t_bManaged && m_hPalette != NULL) DeleteObject(); } CPaletteT& operator =(HPALETTE hPalette) { Attach(hPalette); return *this; } void Attach(HPALETTE hPalette) { if(t_bManaged && m_hPalette != NULL && m_hPalette != hPalette) ::DeleteObject(m_hPalette); m_hPalette = hPalette; } HPALETTE Detach() { HPALETTE hPalette = m_hPalette; m_hPalette = NULL; return hPalette; } operator HPALETTE() const { return m_hPalette; } bool IsNull() const { return (m_hPalette == NULL); } // Create methods HPALETTE CreatePalette(LPLOGPALETTE lpLogPalette) { ATLASSERT(m_hPalette == NULL); m_hPalette = ::CreatePalette(lpLogPalette); return m_hPalette; } #ifndef _WIN32_WCE HPALETTE CreateHalftonePalette(HDC hDC) { ATLASSERT(m_hPalette == NULL); ATLASSERT(hDC != NULL); m_hPalette = ::CreateHalftonePalette(hDC); return m_hPalette; } #endif // !_WIN32_WCE BOOL DeleteObject() { ATLASSERT(m_hPalette != NULL); BOOL bRet = ::DeleteObject(m_hPalette); if(bRet) m_hPalette = NULL; return bRet; } // Attributes int GetEntryCount() const { ATLASSERT(m_hPalette != NULL); WORD nEntries = 0; ::GetObject(m_hPalette, sizeof(WORD), &nEntries); return (int)nEntries; } UINT GetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors) const { ATLASSERT(m_hPalette != NULL); return ::GetPaletteEntries(m_hPalette, nStartIndex, nNumEntries, lpPaletteColors); } UINT SetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors) { ATLASSERT(m_hPalette != NULL); return ::SetPaletteEntries(m_hPalette, nStartIndex, nNumEntries, lpPaletteColors); } // Operations #ifndef _WIN32_WCE void AnimatePalette(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors) { ATLASSERT(m_hPalette != NULL); ::AnimatePalette(m_hPalette, nStartIndex, nNumEntries, lpPaletteColors); } BOOL ResizePalette(UINT nNumEntries) { ATLASSERT(m_hPalette != NULL); return ::ResizePalette(m_hPalette, nNumEntries); } #endif // !_WIN32_WCE UINT GetNearestPaletteIndex(COLORREF crColor) const { ATLASSERT(m_hPalette != NULL); return ::GetNearestPaletteIndex(m_hPalette, crColor); } }; typedef CPaletteT CPaletteHandle; typedef CPaletteT CPalette; /////////////////////////////////////////////////////////////////////////////// // CRgn template class CRgnT { public: // Data members HRGN m_hRgn; // Constructor/destructor/operators CRgnT(HRGN hRgn = NULL) : m_hRgn(hRgn) { } ~CRgnT() { if(t_bManaged && m_hRgn != NULL) DeleteObject(); } CRgnT& operator =(HRGN hRgn) { Attach(hRgn); return *this; } void Attach(HRGN hRgn) { if(t_bManaged && m_hRgn != NULL && m_hRgn != hRgn) ::DeleteObject(m_hRgn); m_hRgn = hRgn; } HRGN Detach() { HRGN hRgn = m_hRgn; m_hRgn = NULL; return hRgn; } operator HRGN() const { return m_hRgn; } bool IsNull() const { return (m_hRgn == NULL); } // Create methods HRGN CreateRectRgn(int x1, int y1, int x2, int y2) { ATLASSERT(m_hRgn == NULL); m_hRgn = ::CreateRectRgn(x1, y1, x2, y2); return m_hRgn; } HRGN CreateRectRgnIndirect(LPCRECT lpRect) { ATLASSERT(m_hRgn == NULL); m_hRgn = ::CreateRectRgnIndirect(lpRect); return m_hRgn; } #ifndef _WIN32_WCE HRGN CreateEllipticRgn(int x1, int y1, int x2, int y2) { ATLASSERT(m_hRgn == NULL); m_hRgn = ::CreateEllipticRgn(x1, y1, x2, y2); return m_hRgn; } HRGN CreateEllipticRgnIndirect(LPCRECT lpRect) { ATLASSERT(m_hRgn == NULL); m_hRgn = ::CreateEllipticRgnIndirect(lpRect); return m_hRgn; } HRGN CreatePolygonRgn(LPPOINT lpPoints, int nCount, int nMode) { ATLASSERT(m_hRgn == NULL); m_hRgn = ::CreatePolygonRgn(lpPoints, nCount, nMode); return m_hRgn; } HRGN CreatePolyPolygonRgn(LPPOINT lpPoints, LPINT lpPolyCounts, int nCount, int nPolyFillMode) { ATLASSERT(m_hRgn == NULL); m_hRgn = ::CreatePolyPolygonRgn(lpPoints, lpPolyCounts, nCount, nPolyFillMode); return m_hRgn; } HRGN CreateRoundRectRgn(int x1, int y1, int x2, int y2, int x3, int y3) { ATLASSERT(m_hRgn == NULL); m_hRgn = ::CreateRoundRectRgn(x1, y1, x2, y2, x3, y3); return m_hRgn; } HRGN CreateFromPath(HDC hDC) { ATLASSERT(m_hRgn == NULL); ATLASSERT(hDC != NULL); m_hRgn = ::PathToRegion(hDC); return m_hRgn; } HRGN CreateFromData(const XFORM* lpXForm, int nCount, const RGNDATA* pRgnData) { ATLASSERT(m_hRgn == NULL); m_hRgn = ::ExtCreateRegion(lpXForm, nCount, pRgnData); return m_hRgn; } #endif // !_WIN32_WCE BOOL DeleteObject() { ATLASSERT(m_hRgn != NULL); BOOL bRet = ::DeleteObject(m_hRgn); if(bRet) m_hRgn = NULL; return bRet; } // Operations void SetRectRgn(int x1, int y1, int x2, int y2) { ATLASSERT(m_hRgn != NULL); ::SetRectRgn(m_hRgn, x1, y1, x2, y2); } void SetRectRgn(LPCRECT lpRect) { ATLASSERT(m_hRgn != NULL); ::SetRectRgn(m_hRgn, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); } int CombineRgn(HRGN hRgnSrc1, HRGN hRgnSrc2, int nCombineMode) { ATLASSERT(m_hRgn != NULL); return ::CombineRgn(m_hRgn, hRgnSrc1, hRgnSrc2, nCombineMode); } int CombineRgn(HRGN hRgnSrc, int nCombineMode) { ATLASSERT(m_hRgn != NULL); return ::CombineRgn(m_hRgn, m_hRgn, hRgnSrc, nCombineMode); } int CopyRgn(HRGN hRgnSrc) { ATLASSERT(m_hRgn != NULL); return ::CombineRgn(m_hRgn, hRgnSrc, NULL, RGN_COPY); } BOOL EqualRgn(HRGN hRgn) const { ATLASSERT(m_hRgn != NULL); return ::EqualRgn(m_hRgn, hRgn); } int OffsetRgn(int x, int y) { ATLASSERT(m_hRgn != NULL); return ::OffsetRgn(m_hRgn, x, y); } int OffsetRgn(POINT point) { ATLASSERT(m_hRgn != NULL); return ::OffsetRgn(m_hRgn, point.x, point.y); } int GetRgnBox(LPRECT lpRect) const { ATLASSERT(m_hRgn != NULL); return ::GetRgnBox(m_hRgn, lpRect); } BOOL PtInRegion(int x, int y) const { ATLASSERT(m_hRgn != NULL); return ::PtInRegion(m_hRgn, x, y); } BOOL PtInRegion(POINT point) const { ATLASSERT(m_hRgn != NULL); return ::PtInRegion(m_hRgn, point.x, point.y); } BOOL RectInRegion(LPCRECT lpRect) const { ATLASSERT(m_hRgn != NULL); return ::RectInRegion(m_hRgn, lpRect); } int GetRegionData(LPRGNDATA lpRgnData, int nDataSize) const { ATLASSERT(m_hRgn != NULL); return (int)::GetRegionData(m_hRgn, nDataSize, lpRgnData); } }; typedef CRgnT CRgnHandle; typedef CRgnT CRgn; /////////////////////////////////////////////////////////////////////////////// // CDC - The device context class template class CDCT { public: // Data members HDC m_hDC; // Constructor/destructor/operators CDCT(HDC hDC = NULL) : m_hDC(hDC) { } ~CDCT() { if(t_bManaged && m_hDC != NULL) ::DeleteDC(Detach()); } CDCT& operator =(HDC hDC) { Attach(hDC); return *this; } void Attach(HDC hDC) { if(t_bManaged && m_hDC != NULL && m_hDC != hDC) ::DeleteDC(m_hDC); m_hDC = hDC; } HDC Detach() { HDC hDC = m_hDC; m_hDC = NULL; return hDC; } operator HDC() const { return m_hDC; } bool IsNull() const { return (m_hDC == NULL); } // Operations #ifndef _WIN32_WCE HWND WindowFromDC() const { ATLASSERT(m_hDC != NULL); return ::WindowFromDC(m_hDC); } #endif // !_WIN32_WCE CPenHandle GetCurrentPen() const { ATLASSERT(m_hDC != NULL); return CPenHandle((HPEN)::GetCurrentObject(m_hDC, OBJ_PEN)); } CBrushHandle GetCurrentBrush() const { ATLASSERT(m_hDC != NULL); return CBrushHandle((HBRUSH)::GetCurrentObject(m_hDC, OBJ_BRUSH)); } CPaletteHandle GetCurrentPalette() const { ATLASSERT(m_hDC != NULL); return CPaletteHandle((HPALETTE)::GetCurrentObject(m_hDC, OBJ_PAL)); } CFontHandle GetCurrentFont() const { ATLASSERT(m_hDC != NULL); return CFontHandle((HFONT)::GetCurrentObject(m_hDC, OBJ_FONT)); } CBitmapHandle GetCurrentBitmap() const { ATLASSERT(m_hDC != NULL); return CBitmapHandle((HBITMAP)::GetCurrentObject(m_hDC, OBJ_BITMAP)); } HDC CreateDC(LPCTSTR lpszDriverName, LPCTSTR lpszDeviceName, LPCTSTR lpszOutput, const DEVMODE* lpInitData) { ATLASSERT(m_hDC == NULL); m_hDC = ::CreateDC(lpszDriverName, lpszDeviceName, lpszOutput, lpInitData); return m_hDC; } HDC CreateCompatibleDC(HDC hDC = NULL) { ATLASSERT(m_hDC == NULL); m_hDC = ::CreateCompatibleDC(hDC); return m_hDC; } BOOL DeleteDC() { if(m_hDC == NULL) return FALSE; BOOL bRet = ::DeleteDC(m_hDC); if(bRet) m_hDC = NULL; return bRet; } // Device-Context Functions int SaveDC() { ATLASSERT(m_hDC != NULL); return ::SaveDC(m_hDC); } BOOL RestoreDC(int nSavedDC) { ATLASSERT(m_hDC != NULL); return ::RestoreDC(m_hDC, nSavedDC); } int GetDeviceCaps(int nIndex) const { ATLASSERT(m_hDC != NULL); return ::GetDeviceCaps(m_hDC, nIndex); } #ifndef _WIN32_WCE UINT SetBoundsRect(LPCRECT lpRectBounds, UINT flags) { ATLASSERT(m_hDC != NULL); return ::SetBoundsRect(m_hDC, lpRectBounds, flags); } UINT GetBoundsRect(LPRECT lpRectBounds, UINT flags) const { ATLASSERT(m_hDC != NULL); return ::GetBoundsRect(m_hDC, lpRectBounds, flags); } BOOL ResetDC(const DEVMODE* lpDevMode) { ATLASSERT(m_hDC != NULL); return ::ResetDC(m_hDC, lpDevMode) != NULL; } // Drawing-Tool Functions BOOL GetBrushOrg(LPPOINT lpPoint) const { ATLASSERT(m_hDC != NULL); return ::GetBrushOrgEx(m_hDC, lpPoint); } #endif // !_WIN32_WCE BOOL SetBrushOrg(int x, int y, LPPOINT lpPoint = NULL) { ATLASSERT(m_hDC != NULL); return ::SetBrushOrgEx(m_hDC, x, y, lpPoint); } BOOL SetBrushOrg(POINT point, LPPOINT lpPointRet = NULL) { ATLASSERT(m_hDC != NULL); return ::SetBrushOrgEx(m_hDC, point.x, point.y, lpPointRet); } #ifndef _WIN32_WCE int EnumObjects(int nObjectType, int (CALLBACK* lpfn)(LPVOID, LPARAM), LPARAM lpData) { ATLASSERT(m_hDC != NULL); #ifdef STRICT return ::EnumObjects(m_hDC, nObjectType, (GOBJENUMPROC)lpfn, lpData); #else return ::EnumObjects(m_hDC, nObjectType, (GOBJENUMPROC)lpfn, (LPVOID)lpData); #endif } #endif // !_WIN32_WCE // Type-safe selection helpers HPEN SelectPen(HPEN hPen) { ATLASSERT(m_hDC != NULL); #ifndef _WIN32_WCE ATLASSERT(hPen == NULL || ::GetObjectType(hPen) == OBJ_PEN || ::GetObjectType(hPen) == OBJ_EXTPEN); #else // CE specific ATLASSERT(hPen == NULL || ::GetObjectType(hPen) == OBJ_PEN); #endif // _WIN32_WCE return (HPEN)::SelectObject(m_hDC, hPen); } HBRUSH SelectBrush(HBRUSH hBrush) { ATLASSERT(m_hDC != NULL); ATLASSERT(hBrush == NULL || ::GetObjectType(hBrush) == OBJ_BRUSH); return (HBRUSH)::SelectObject(m_hDC, hBrush); } HFONT SelectFont(HFONT hFont) { ATLASSERT(m_hDC != NULL); ATLASSERT(hFont == NULL || ::GetObjectType(hFont) == OBJ_FONT); return (HFONT)::SelectObject(m_hDC, hFont); } HBITMAP SelectBitmap(HBITMAP hBitmap) { ATLASSERT(m_hDC != NULL); ATLASSERT(hBitmap == NULL || ::GetObjectType(hBitmap) == OBJ_BITMAP); return (HBITMAP)::SelectObject(m_hDC, hBitmap); } int SelectRgn(HRGN hRgn) // special return for regions { ATLASSERT(m_hDC != NULL); ATLASSERT(hRgn == NULL || ::GetObjectType(hRgn) == OBJ_REGION); return PtrToInt(::SelectObject(m_hDC, hRgn)); } // Type-safe selection helpers for stock objects HPEN SelectStockPen(int nPen) { ATLASSERT(m_hDC != NULL); #if (_WIN32_WINNT >= 0x0500) ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN || nPen == DC_PEN); #else ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN); #endif // !(_WIN32_WINNT >= 0x0500) return SelectPen((HPEN)::GetStockObject(nPen)); } HBRUSH SelectStockBrush(int nBrush) { #if (_WIN32_WINNT >= 0x0500) ATLASSERT((nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH) || nBrush == DC_BRUSH); #else ATLASSERT(nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH); #endif // !(_WIN32_WINNT >= 0x0500) return SelectBrush((HBRUSH)::GetStockObject(nBrush)); } HFONT SelectStockFont(int nFont) { #ifndef _WIN32_WCE ATLASSERT((nFont >= OEM_FIXED_FONT && nFont <= SYSTEM_FIXED_FONT) || nFont == DEFAULT_GUI_FONT); #else // CE specific ATLASSERT(nFont == SYSTEM_FONT); #endif // _WIN32_WCE return SelectFont((HFONT)::GetStockObject(nFont)); } HPALETTE SelectStockPalette(int nPalette, BOOL bForceBackground) { ATLASSERT(nPalette == DEFAULT_PALETTE); // the only one supported return SelectPalette((HPALETTE)::GetStockObject(nPalette), bForceBackground); } // Color and Color Palette Functions COLORREF GetNearestColor(COLORREF crColor) const { ATLASSERT(m_hDC != NULL); return ::GetNearestColor(m_hDC, crColor); } HPALETTE SelectPalette(HPALETTE hPalette, BOOL bForceBackground) { ATLASSERT(m_hDC != NULL); return ::SelectPalette(m_hDC, hPalette, bForceBackground); } UINT RealizePalette() { ATLASSERT(m_hDC != NULL); return ::RealizePalette(m_hDC); } #ifndef _WIN32_WCE void UpdateColors() { ATLASSERT(m_hDC != NULL); ::UpdateColors(m_hDC); } #endif // !_WIN32_WCE // Drawing-Attribute Functions COLORREF GetBkColor() const { ATLASSERT(m_hDC != NULL); return ::GetBkColor(m_hDC); } int GetBkMode() const { ATLASSERT(m_hDC != NULL); return ::GetBkMode(m_hDC); } #ifndef _WIN32_WCE int GetPolyFillMode() const { ATLASSERT(m_hDC != NULL); return ::GetPolyFillMode(m_hDC); } int GetROP2() const { ATLASSERT(m_hDC != NULL); return ::GetROP2(m_hDC); } int GetStretchBltMode() const { ATLASSERT(m_hDC != NULL); return ::GetStretchBltMode(m_hDC); } #endif // !_WIN32_WCE COLORREF GetTextColor() const { ATLASSERT(m_hDC != NULL); return ::GetTextColor(m_hDC); } COLORREF SetBkColor(COLORREF crColor) { ATLASSERT(m_hDC != NULL); return ::SetBkColor(m_hDC, crColor); } int SetBkMode(int nBkMode) { ATLASSERT(m_hDC != NULL); return ::SetBkMode(m_hDC, nBkMode); } #ifndef _WIN32_WCE int SetPolyFillMode(int nPolyFillMode) { ATLASSERT(m_hDC != NULL); return ::SetPolyFillMode(m_hDC, nPolyFillMode); } #endif // !_WIN32_WCE int SetROP2(int nDrawMode) { ATLASSERT(m_hDC != NULL); return ::SetROP2(m_hDC, nDrawMode); } #ifndef _WIN32_WCE int SetStretchBltMode(int nStretchMode) { ATLASSERT(m_hDC != NULL); return ::SetStretchBltMode(m_hDC, nStretchMode); } #endif // !_WIN32_WCE COLORREF SetTextColor(COLORREF crColor) { ATLASSERT(m_hDC != NULL); return ::SetTextColor(m_hDC, crColor); } #ifndef _WIN32_WCE BOOL GetColorAdjustment(LPCOLORADJUSTMENT lpColorAdjust) const { ATLASSERT(m_hDC != NULL); return ::GetColorAdjustment(m_hDC, lpColorAdjust); } BOOL SetColorAdjustment(const COLORADJUSTMENT* lpColorAdjust) { ATLASSERT(m_hDC != NULL); return ::SetColorAdjustment(m_hDC, lpColorAdjust); } // Mapping Functions int GetMapMode() const { ATLASSERT(m_hDC != NULL); return ::GetMapMode(m_hDC); } BOOL GetViewportOrg(LPPOINT lpPoint) const { ATLASSERT(m_hDC != NULL); return ::GetViewportOrgEx(m_hDC, lpPoint); } int SetMapMode(int nMapMode) { ATLASSERT(m_hDC != NULL); return ::SetMapMode(m_hDC, nMapMode); } #endif // !_WIN32_WCE // Viewport Origin BOOL SetViewportOrg(int x, int y, LPPOINT lpPoint = NULL) { ATLASSERT(m_hDC != NULL); return ::SetViewportOrgEx(m_hDC, x, y, lpPoint); } BOOL SetViewportOrg(POINT point, LPPOINT lpPointRet = NULL) { ATLASSERT(m_hDC != NULL); return SetViewportOrg(point.x, point.y, lpPointRet); } #ifndef _WIN32_WCE BOOL OffsetViewportOrg(int nWidth, int nHeight, LPPOINT lpPoint = NULL) { ATLASSERT(m_hDC != NULL); return ::OffsetViewportOrgEx(m_hDC, nWidth, nHeight, lpPoint); } // Viewport Extent BOOL GetViewportExt(LPSIZE lpSize) const { ATLASSERT(m_hDC != NULL); return ::GetViewportExtEx(m_hDC, lpSize); } BOOL SetViewportExt(int x, int y, LPSIZE lpSize = NULL) { ATLASSERT(m_hDC != NULL); return ::SetViewportExtEx(m_hDC, x, y, lpSize); } BOOL SetViewportExt(SIZE size, LPSIZE lpSizeRet = NULL) { ATLASSERT(m_hDC != NULL); return SetViewportExt(size.cx, size.cy, lpSizeRet); } BOOL ScaleViewportExt(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize = NULL) { ATLASSERT(m_hDC != NULL); return ::ScaleViewportExtEx(m_hDC, xNum, xDenom, yNum, yDenom, lpSize); } #endif // !_WIN32_WCE // Window Origin #ifndef _WIN32_WCE BOOL GetWindowOrg(LPPOINT lpPoint) const { ATLASSERT(m_hDC != NULL); return ::GetWindowOrgEx(m_hDC, lpPoint); } BOOL SetWindowOrg(int x, int y, LPPOINT lpPoint = NULL) { ATLASSERT(m_hDC != NULL); return ::SetWindowOrgEx(m_hDC, x, y, lpPoint); } BOOL SetWindowOrg(POINT point, LPPOINT lpPointRet = NULL) { ATLASSERT(m_hDC != NULL); return SetWindowOrg(point.x, point.y, lpPointRet); } BOOL OffsetWindowOrg(int nWidth, int nHeight, LPPOINT lpPoint = NULL) { ATLASSERT(m_hDC != NULL); return ::OffsetWindowOrgEx(m_hDC, nWidth, nHeight, lpPoint); } // Window extent BOOL GetWindowExt(LPSIZE lpSize) const { ATLASSERT(m_hDC != NULL); return ::GetWindowExtEx(m_hDC, lpSize); } BOOL SetWindowExt(int x, int y, LPSIZE lpSize = NULL) { ATLASSERT(m_hDC != NULL); return ::SetWindowExtEx(m_hDC, x, y, lpSize); } BOOL SetWindowExt(SIZE size, LPSIZE lpSizeRet = NULL) { ATLASSERT(m_hDC != NULL); return SetWindowExt(size.cx, size.cy, lpSizeRet); } BOOL ScaleWindowExt(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize = NULL) { ATLASSERT(m_hDC != NULL); return ::ScaleWindowExtEx(m_hDC, xNum, xDenom, yNum, yDenom, lpSize); } // Coordinate Functions BOOL DPtoLP(LPPOINT lpPoints, int nCount = 1) const { ATLASSERT(m_hDC != NULL); return ::DPtoLP(m_hDC, lpPoints, nCount); } BOOL DPtoLP(LPRECT lpRect) const { ATLASSERT(m_hDC != NULL); return ::DPtoLP(m_hDC, (LPPOINT)lpRect, 2); } BOOL DPtoLP(LPSIZE lpSize) const { SIZE sizeWinExt = { 0, 0 }; if(!GetWindowExt(&sizeWinExt)) return FALSE; SIZE sizeVpExt = { 0, 0 }; if(!GetViewportExt(&sizeVpExt)) return FALSE; lpSize->cx = ::MulDiv(lpSize->cx, abs(sizeWinExt.cx), abs(sizeVpExt.cx)); lpSize->cy = ::MulDiv(lpSize->cy, abs(sizeWinExt.cy), abs(sizeVpExt.cy)); return TRUE; } BOOL LPtoDP(LPPOINT lpPoints, int nCount = 1) const { ATLASSERT(m_hDC != NULL); return ::LPtoDP(m_hDC, lpPoints, nCount); } BOOL LPtoDP(LPRECT lpRect) const { ATLASSERT(m_hDC != NULL); return ::LPtoDP(m_hDC, (LPPOINT)lpRect, 2); } BOOL LPtoDP(LPSIZE lpSize) const { SIZE sizeWinExt = { 0, 0 }; if(!GetWindowExt(&sizeWinExt)) return FALSE; SIZE sizeVpExt = { 0, 0 }; if(!GetViewportExt(&sizeVpExt)) return FALSE; lpSize->cx = ::MulDiv(lpSize->cx, abs(sizeVpExt.cx), abs(sizeWinExt.cx)); lpSize->cy = ::MulDiv(lpSize->cy, abs(sizeVpExt.cy), abs(sizeWinExt.cy)); return TRUE; } // Special Coordinate Functions (useful for dealing with metafiles and OLE) #define HIMETRIC_INCH 2540 // HIMETRIC units per inch void DPtoHIMETRIC(LPSIZE lpSize) const { ATLASSERT(m_hDC != NULL); int nMapMode; if((nMapMode = GetMapMode()) < MM_ISOTROPIC && nMapMode != MM_TEXT) { // when using a constrained map mode, map against physical inch ((CDCHandle*)this)->SetMapMode(MM_HIMETRIC); DPtoLP(lpSize); ((CDCHandle*)this)->SetMapMode(nMapMode); } else { // map against logical inch for non-constrained mapping modes int cxPerInch = GetDeviceCaps(LOGPIXELSX); int cyPerInch = GetDeviceCaps(LOGPIXELSY); ATLASSERT(cxPerInch != 0 && cyPerInch != 0); lpSize->cx = ::MulDiv(lpSize->cx, HIMETRIC_INCH, cxPerInch); lpSize->cy = ::MulDiv(lpSize->cy, HIMETRIC_INCH, cyPerInch); } } void HIMETRICtoDP(LPSIZE lpSize) const { ATLASSERT(m_hDC != NULL); int nMapMode; if((nMapMode = GetMapMode()) < MM_ISOTROPIC && nMapMode != MM_TEXT) { // when using a constrained map mode, map against physical inch ((CDCHandle*)this)->SetMapMode(MM_HIMETRIC); LPtoDP(lpSize); ((CDCHandle*)this)->SetMapMode(nMapMode); } else { // map against logical inch for non-constrained mapping modes int cxPerInch = GetDeviceCaps(LOGPIXELSX); int cyPerInch = GetDeviceCaps(LOGPIXELSY); ATLASSERT(cxPerInch != 0 && cyPerInch != 0); lpSize->cx = ::MulDiv(lpSize->cx, cxPerInch, HIMETRIC_INCH); lpSize->cy = ::MulDiv(lpSize->cy, cyPerInch, HIMETRIC_INCH); } } void LPtoHIMETRIC(LPSIZE lpSize) const { LPtoDP(lpSize); DPtoHIMETRIC(lpSize); } void HIMETRICtoLP(LPSIZE lpSize) const { HIMETRICtoDP(lpSize); DPtoLP(lpSize); } #endif // !_WIN32_WCE // Region Functions BOOL FillRgn(HRGN hRgn, HBRUSH hBrush) { ATLASSERT(m_hDC != NULL); return ::FillRgn(m_hDC, hRgn, hBrush); } #ifndef _WIN32_WCE BOOL FrameRgn(HRGN hRgn, HBRUSH hBrush, int nWidth, int nHeight) { ATLASSERT(m_hDC != NULL); return ::FrameRgn(m_hDC, hRgn, hBrush, nWidth, nHeight); } BOOL InvertRgn(HRGN hRgn) { ATLASSERT(m_hDC != NULL); return ::InvertRgn(m_hDC, hRgn); } BOOL PaintRgn(HRGN hRgn) { ATLASSERT(m_hDC != NULL); return ::PaintRgn(m_hDC, hRgn); } #endif // !_WIN32_WCE // Clipping Functions int GetClipBox(LPRECT lpRect) const { ATLASSERT(m_hDC != NULL); return ::GetClipBox(m_hDC, lpRect); } int GetClipRgn(CRgn& region) const { ATLASSERT(m_hDC != NULL); if(region.IsNull()) region.CreateRectRgn(0, 0, 0, 0); int nRet = ::GetClipRgn(m_hDC, region); if(nRet != 1) region.DeleteObject(); return nRet; } #ifndef _WIN32_WCE BOOL PtVisible(int x, int y) const { ATLASSERT(m_hDC != NULL); return ::PtVisible(m_hDC, x, y); } BOOL PtVisible(POINT point) const { ATLASSERT(m_hDC != NULL); return ::PtVisible(m_hDC, point.x, point.y); } #endif // !_WIN32_WCE BOOL RectVisible(LPCRECT lpRect) const { ATLASSERT(m_hDC != NULL); return ::RectVisible(m_hDC, lpRect); } int SelectClipRgn(HRGN hRgn) { ATLASSERT(m_hDC != NULL); return ::SelectClipRgn(m_hDC, (HRGN)hRgn); } int ExcludeClipRect(int x1, int y1, int x2, int y2) { ATLASSERT(m_hDC != NULL); return ::ExcludeClipRect(m_hDC, x1, y1, x2, y2); } int ExcludeClipRect(LPCRECT lpRect) { ATLASSERT(m_hDC != NULL); return ::ExcludeClipRect(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); } #ifndef _WIN32_WCE int ExcludeUpdateRgn(HWND hWnd) { ATLASSERT(m_hDC != NULL); return ::ExcludeUpdateRgn(m_hDC, hWnd); } #endif // !_WIN32_WCE int IntersectClipRect(int x1, int y1, int x2, int y2) { ATLASSERT(m_hDC != NULL); return ::IntersectClipRect(m_hDC, x1, y1, x2, y2); } int IntersectClipRect(LPCRECT lpRect) { ATLASSERT(m_hDC != NULL); return ::IntersectClipRect(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); } #ifndef _WIN32_WCE int OffsetClipRgn(int x, int y) { ATLASSERT(m_hDC != NULL); return ::OffsetClipRgn(m_hDC, x, y); } int OffsetClipRgn(SIZE size) { ATLASSERT(m_hDC != NULL); return ::OffsetClipRgn(m_hDC, size.cx, size.cy); } int SelectClipRgn(HRGN hRgn, int nMode) { ATLASSERT(m_hDC != NULL); return ::ExtSelectClipRgn(m_hDC, hRgn, nMode); } #endif // !_WIN32_WCE // Line-Output Functions #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400) BOOL GetCurrentPosition(LPPOINT lpPoint) const { ATLASSERT(m_hDC != NULL); return ::GetCurrentPositionEx(m_hDC, lpPoint); } BOOL MoveTo(int x, int y, LPPOINT lpPoint = NULL) { ATLASSERT(m_hDC != NULL); return ::MoveToEx(m_hDC, x, y, lpPoint); } BOOL MoveTo(POINT point, LPPOINT lpPointRet = NULL) { ATLASSERT(m_hDC != NULL); return MoveTo(point.x, point.y, lpPointRet); } BOOL LineTo(int x, int y) { ATLASSERT(m_hDC != NULL); return ::LineTo(m_hDC, x, y); } BOOL LineTo(POINT point) { ATLASSERT(m_hDC != NULL); return LineTo(point.x, point.y); } #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400) #ifndef _WIN32_WCE BOOL Arc(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { ATLASSERT(m_hDC != NULL); return ::Arc(m_hDC, x1, y1, x2, y2, x3, y3, x4, y4); } BOOL Arc(LPCRECT lpRect, POINT ptStart, POINT ptEnd) { ATLASSERT(m_hDC != NULL); return ::Arc(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); } #endif // !_WIN32_WCE BOOL Polyline(const POINT* lpPoints, int nCount) { ATLASSERT(m_hDC != NULL); return ::Polyline(m_hDC, lpPoints, nCount); } #ifndef _WIN32_WCE BOOL AngleArc(int x, int y, int nRadius, float fStartAngle, float fSweepAngle) { ATLASSERT(m_hDC != NULL); return ::AngleArc(m_hDC, x, y, nRadius, fStartAngle, fSweepAngle); } BOOL ArcTo(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { ATLASSERT(m_hDC != NULL); return ::ArcTo(m_hDC, x1, y1, x2, y2, x3, y3, x4, y4); } BOOL ArcTo(LPCRECT lpRect, POINT ptStart, POINT ptEnd) { ATLASSERT(m_hDC != NULL); return ArcTo(lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); } int GetArcDirection() const { ATLASSERT(m_hDC != NULL); return ::GetArcDirection(m_hDC); } int SetArcDirection(int nArcDirection) { ATLASSERT(m_hDC != NULL); return ::SetArcDirection(m_hDC, nArcDirection); } BOOL PolyDraw(const POINT* lpPoints, const BYTE* lpTypes, int nCount) { ATLASSERT(m_hDC != NULL); return ::PolyDraw(m_hDC, lpPoints, lpTypes, nCount); } BOOL PolylineTo(const POINT* lpPoints, int nCount) { ATLASSERT(m_hDC != NULL); return ::PolylineTo(m_hDC, lpPoints, nCount); } BOOL PolyPolyline(const POINT* lpPoints, const DWORD* lpPolyPoints, int nCount) { ATLASSERT(m_hDC != NULL); return ::PolyPolyline(m_hDC, lpPoints, lpPolyPoints, nCount); } BOOL PolyBezier(const POINT* lpPoints, int nCount) { ATLASSERT(m_hDC != NULL); return ::PolyBezier(m_hDC, lpPoints, nCount); } BOOL PolyBezierTo(const POINT* lpPoints, int nCount) { ATLASSERT(m_hDC != NULL); return ::PolyBezierTo(m_hDC, lpPoints, nCount); } #endif // !_WIN32_WCE // Simple Drawing Functions BOOL FillRect(LPCRECT lpRect, HBRUSH hBrush) { ATLASSERT(m_hDC != NULL); return ::FillRect(m_hDC, lpRect, hBrush); } BOOL FillRect(LPCRECT lpRect, int nColorIndex) { ATLASSERT(m_hDC != NULL); #ifndef _WIN32_WCE return ::FillRect(m_hDC, lpRect, (HBRUSH)LongToPtr(nColorIndex + 1)); #else // CE specific return ::FillRect(m_hDC, lpRect, ::GetSysColorBrush(nColorIndex)); #endif // _WIN32_WCE } #ifndef _WIN32_WCE BOOL FrameRect(LPCRECT lpRect, HBRUSH hBrush) { ATLASSERT(m_hDC != NULL); return ::FrameRect(m_hDC, lpRect, hBrush); } #endif // !_WIN32_WCE #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 420) BOOL InvertRect(LPCRECT lpRect) { ATLASSERT(m_hDC != NULL); return ::InvertRect(m_hDC, lpRect); } #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 420) BOOL DrawIcon(int x, int y, HICON hIcon) { ATLASSERT(m_hDC != NULL); #ifndef _WIN32_WCE return ::DrawIcon(m_hDC, x, y, hIcon); #else // CE specific return ::DrawIconEx(m_hDC, x, y, hIcon, 0, 0, 0, NULL, DI_NORMAL); #endif // _WIN32_WCE } BOOL DrawIcon(POINT point, HICON hIcon) { ATLASSERT(m_hDC != NULL); #ifndef _WIN32_WCE return ::DrawIcon(m_hDC, point.x, point.y, hIcon); #else // CE specific return ::DrawIconEx(m_hDC, point.x, point.y, hIcon, 0, 0, 0, NULL, DI_NORMAL); #endif // _WIN32_WCE } BOOL DrawIconEx(int x, int y, HICON hIcon, int cxWidth, int cyWidth, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL) { ATLASSERT(m_hDC != NULL); return ::DrawIconEx(m_hDC, x, y, hIcon, cxWidth, cyWidth, uStepIfAniCur, hbrFlickerFreeDraw, uFlags); } BOOL DrawIconEx(POINT point, HICON hIcon, SIZE size, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL) { ATLASSERT(m_hDC != NULL); return ::DrawIconEx(m_hDC, point.x, point.y, hIcon, size.cx, size.cy, uStepIfAniCur, hbrFlickerFreeDraw, uFlags); } #ifndef _WIN32_WCE BOOL DrawState(POINT pt, SIZE size, HBITMAP hBitmap, UINT nFlags, HBRUSH hBrush = NULL) { ATLASSERT(m_hDC != NULL); return ::DrawState(m_hDC, hBrush, NULL, (LPARAM)hBitmap, 0, pt.x, pt.y, size.cx, size.cy, nFlags | DST_BITMAP); } BOOL DrawState(POINT pt, SIZE size, HICON hIcon, UINT nFlags, HBRUSH hBrush = NULL) { ATLASSERT(m_hDC != NULL); return ::DrawState(m_hDC, hBrush, NULL, (LPARAM)hIcon, 0, pt.x, pt.y, size.cx, size.cy, nFlags | DST_ICON); } BOOL DrawState(POINT pt, SIZE size, LPCTSTR lpszText, UINT nFlags, BOOL bPrefixText = TRUE, int nTextLen = 0, HBRUSH hBrush = NULL) { ATLASSERT(m_hDC != NULL); return ::DrawState(m_hDC, hBrush, NULL, (LPARAM)lpszText, (WPARAM)nTextLen, pt.x, pt.y, size.cx, size.cy, nFlags | (bPrefixText ? DST_PREFIXTEXT : DST_TEXT)); } BOOL DrawState(POINT pt, SIZE size, DRAWSTATEPROC lpDrawProc, LPARAM lData, UINT nFlags, HBRUSH hBrush = NULL) { ATLASSERT(m_hDC != NULL); return ::DrawState(m_hDC, hBrush, lpDrawProc, lData, 0, pt.x, pt.y, size.cx, size.cy, nFlags | DST_COMPLEX); } #endif // !_WIN32_WCE // Ellipse and Polygon Functions #ifndef _WIN32_WCE BOOL Chord(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { ATLASSERT(m_hDC != NULL); return ::Chord(m_hDC, x1, y1, x2, y2, x3, y3, x4, y4); } BOOL Chord(LPCRECT lpRect, POINT ptStart, POINT ptEnd) { ATLASSERT(m_hDC != NULL); return ::Chord(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); } #endif // !_WIN32_WCE void DrawFocusRect(LPCRECT lpRect) { ATLASSERT(m_hDC != NULL); ::DrawFocusRect(m_hDC, lpRect); } BOOL Ellipse(int x1, int y1, int x2, int y2) { ATLASSERT(m_hDC != NULL); return ::Ellipse(m_hDC, x1, y1, x2, y2); } BOOL Ellipse(LPCRECT lpRect) { ATLASSERT(m_hDC != NULL); return ::Ellipse(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); } #ifndef _WIN32_WCE BOOL Pie(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { ATLASSERT(m_hDC != NULL); return ::Pie(m_hDC, x1, y1, x2, y2, x3, y3, x4, y4); } BOOL Pie(LPCRECT lpRect, POINT ptStart, POINT ptEnd) { ATLASSERT(m_hDC != NULL); return ::Pie(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); } #endif // !_WIN32_WCE BOOL Polygon(const POINT* lpPoints, int nCount) { ATLASSERT(m_hDC != NULL); return ::Polygon(m_hDC, lpPoints, nCount); } #ifndef _WIN32_WCE BOOL PolyPolygon(const POINT* lpPoints, const INT* lpPolyCounts, int nCount) { ATLASSERT(m_hDC != NULL); return ::PolyPolygon(m_hDC, lpPoints, lpPolyCounts, nCount); } #endif // !_WIN32_WCE BOOL Rectangle(int x1, int y1, int x2, int y2) { ATLASSERT(m_hDC != NULL); return ::Rectangle(m_hDC, x1, y1, x2, y2); } BOOL Rectangle(LPCRECT lpRect) { ATLASSERT(m_hDC != NULL); return ::Rectangle(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); } BOOL RoundRect(int x1, int y1, int x2, int y2, int x3, int y3) { ATLASSERT(m_hDC != NULL); return ::RoundRect(m_hDC, x1, y1, x2, y2, x3, y3); } BOOL RoundRect(LPCRECT lpRect, POINT point) { ATLASSERT(m_hDC != NULL); return ::RoundRect(m_hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, point.x, point.y); } // Bitmap Functions BOOL PatBlt(int x, int y, int nWidth, int nHeight, DWORD dwRop) { ATLASSERT(m_hDC != NULL); return ::PatBlt(m_hDC, x, y, nWidth, nHeight, dwRop); } BOOL BitBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, DWORD dwRop) { ATLASSERT(m_hDC != NULL); return ::BitBlt(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, dwRop); } BOOL StretchBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop) { ATLASSERT(m_hDC != NULL); return ::StretchBlt(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, nSrcWidth, nSrcHeight, dwRop); } COLORREF GetPixel(int x, int y) const { ATLASSERT(m_hDC != NULL); return ::GetPixel(m_hDC, x, y); } COLORREF GetPixel(POINT point) const { ATLASSERT(m_hDC != NULL); return ::GetPixel(m_hDC, point.x, point.y); } COLORREF SetPixel(int x, int y, COLORREF crColor) { ATLASSERT(m_hDC != NULL); return ::SetPixel(m_hDC, x, y, crColor); } COLORREF SetPixel(POINT point, COLORREF crColor) { ATLASSERT(m_hDC != NULL); return ::SetPixel(m_hDC, point.x, point.y, crColor); } #ifndef _WIN32_WCE BOOL FloodFill(int x, int y, COLORREF crColor) { ATLASSERT(m_hDC != NULL); return ::FloodFill(m_hDC, x, y, crColor); } BOOL ExtFloodFill(int x, int y, COLORREF crColor, UINT nFillType) { ATLASSERT(m_hDC != NULL); return ::ExtFloodFill(m_hDC, x, y, crColor, nFillType); } #endif // !_WIN32_WCE BOOL MaskBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, HBITMAP hMaskBitmap, int xMask, int yMask, DWORD dwRop) { ATLASSERT(m_hDC != NULL); return ::MaskBlt(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, hMaskBitmap, xMask, yMask, dwRop); } #ifndef _WIN32_WCE BOOL PlgBlt(LPPOINT lpPoint, HDC hSrcDC, int xSrc, int ySrc, int nWidth, int nHeight, HBITMAP hMaskBitmap, int xMask, int yMask) { ATLASSERT(m_hDC != NULL); return ::PlgBlt(m_hDC, lpPoint, hSrcDC, xSrc, ySrc, nWidth, nHeight, hMaskBitmap, xMask, yMask); } BOOL SetPixelV(int x, int y, COLORREF crColor) { ATLASSERT(m_hDC != NULL); return ::SetPixelV(m_hDC, x, y, crColor); } BOOL SetPixelV(POINT point, COLORREF crColor) { ATLASSERT(m_hDC != NULL); return ::SetPixelV(m_hDC, point.x, point.y, crColor); } #endif // !_WIN32_WCE #if !defined(_ATL_NO_MSIMG) || defined(_WIN32_WCE) #ifndef _WIN32_WCE BOOL TransparentBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, UINT crTransparent) { ATLASSERT(m_hDC != NULL); return ::TransparentBlt(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, nSrcWidth, nSrcHeight, crTransparent); } #else // CE specific BOOL TransparentImage(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, UINT crTransparent) { ATLASSERT(m_hDC != NULL); return ::TransparentImage(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, nSrcWidth, nSrcHeight, crTransparent); } #endif // _WIN32_WCE #if (!defined(_WIN32_WCE) || (_WIN32_WCE >= 420)) BOOL GradientFill(const PTRIVERTEX pVertices, DWORD nVertices, void* pMeshElements, DWORD nMeshElements, DWORD dwMode) { ATLASSERT(m_hDC != NULL); return ::GradientFill(m_hDC, pVertices, nVertices, pMeshElements, nMeshElements, dwMode); } BOOL GradientFillRect(RECT& rect, COLORREF clr1, COLORREF clr2, bool bHorizontal) { ATLASSERT(m_hDC != NULL); TRIVERTEX arrTvx[2] = { { 0 }, { 0 } }; arrTvx[0].x = rect.left; arrTvx[0].y = rect.top; arrTvx[0].Red = MAKEWORD(0, GetRValue(clr1)); arrTvx[0].Green = MAKEWORD(0, GetGValue(clr1)); arrTvx[0].Blue = MAKEWORD(0, GetBValue(clr1)); arrTvx[0].Alpha = 0; arrTvx[1].x = rect.right; arrTvx[1].y = rect.bottom; arrTvx[1].Red = MAKEWORD(0, GetRValue(clr2)); arrTvx[1].Green = MAKEWORD(0, GetGValue(clr2)); arrTvx[1].Blue = MAKEWORD(0, GetBValue(clr2)); arrTvx[1].Alpha = 0; GRADIENT_RECT gr = { 0, 1 }; return ::GradientFill(m_hDC, arrTvx, 2, &gr, 1, bHorizontal ? GRADIENT_FILL_RECT_H : GRADIENT_FILL_RECT_V); } #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 420) #if !defined(_WIN32_WCE) || (_WIN32_WCE > 0x500) BOOL AlphaBlend(int x, int y, int nWidth, int nHeight, HDC hSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, BLENDFUNCTION bf) { ATLASSERT(m_hDC != NULL); return ::AlphaBlend(m_hDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, nSrcWidth, nSrcHeight, bf); } #endif // !defined(_WIN32_WCE) || (_WIN32_WCE > 0x500) #endif // !defined(_ATL_NO_MSIMG) || defined(_WIN32_WCE) // Extra bitmap functions // Helper function for painting a disabled toolbar or menu bitmap // This function can take either an HBITMAP (for SS) or a DC with // the bitmap already painted (for cmdbar) BOOL DitherBlt(int x, int y, int nWidth, int nHeight, HDC hSrcDC, HBITMAP hBitmap, int xSrc, int ySrc, HBRUSH hBrushBackground = ::GetSysColorBrush(COLOR_3DFACE), HBRUSH hBrush3DEffect = ::GetSysColorBrush(COLOR_3DHILIGHT), HBRUSH hBrushDisabledImage = ::GetSysColorBrush(COLOR_3DSHADOW)) { ATLASSERT(m_hDC != NULL || hBitmap != NULL); ATLASSERT(nWidth > 0 && nHeight > 0); // Create a generic DC for all BitBlts CDCHandle dc = (hSrcDC != NULL) ? hSrcDC : ::CreateCompatibleDC(m_hDC); ATLASSERT(dc.m_hDC != NULL); if(dc.m_hDC == NULL) return FALSE; // Create a DC for the monochrome DIB section CDC dcBW = ::CreateCompatibleDC(m_hDC); ATLASSERT(dcBW.m_hDC != NULL); if(dcBW.m_hDC == NULL) { if(hSrcDC == NULL) dc.DeleteDC(); return FALSE; } // Create the monochrome DIB section with a black and white palette struct RGBBWBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[2]; }; RGBBWBITMAPINFO rgbBWBitmapInfo = { { sizeof(BITMAPINFOHEADER), nWidth, nHeight, 1, 1, BI_RGB, 0, 0, 0, 0, 0 }, { { 0x00, 0x00, 0x00, 0x00 }, { 0xFF, 0xFF, 0xFF, 0x00 } } }; VOID* pbitsBW; CBitmap bmpBW = ::CreateDIBSection(dcBW, (LPBITMAPINFO)&rgbBWBitmapInfo, DIB_RGB_COLORS, &pbitsBW, NULL, 0); ATLASSERT(bmpBW.m_hBitmap != NULL); if(bmpBW.m_hBitmap == NULL) { if(hSrcDC == NULL) dc.DeleteDC(); return FALSE; } // Attach the monochrome DIB section and the bitmap to the DCs HBITMAP hbmOldBW = dcBW.SelectBitmap(bmpBW); HBITMAP hbmOldDC = NULL; if(hBitmap != NULL) hbmOldDC = dc.SelectBitmap(hBitmap); // Block: Dark gray removal: we want (128, 128, 128) pixels to become black and not white { CDC dcTemp1 = ::CreateCompatibleDC(m_hDC); CDC dcTemp2 = ::CreateCompatibleDC(m_hDC); CBitmap bmpTemp1; bmpTemp1.CreateCompatibleBitmap(dc, nWidth, nHeight); CBitmap bmpTemp2; bmpTemp2.CreateBitmap(nWidth, nHeight, 1, 1, NULL); HBITMAP hOldBmp1 = dcTemp1.SelectBitmap(bmpTemp1); HBITMAP hOldBmp2 = dcTemp2.SelectBitmap(bmpTemp2); // Let's copy our image, it will be altered dcTemp1.BitBlt(0, 0, nWidth, nHeight, dc, xSrc, ySrc, SRCCOPY); // All dark gray pixels will become white, the others black dcTemp1.SetBkColor(RGB(128, 128, 128)); dcTemp2.BitBlt(0, 0, nWidth, nHeight, dcTemp1, 0, 0, SRCCOPY); // Do an XOR to set to black these white pixels dcTemp1.BitBlt(0, 0, nWidth, nHeight, dcTemp2, 0, 0, SRCINVERT); // BitBlt the bitmap into the monochrome DIB section // The DIB section will do a true monochrome conversion // The magenta background being closer to white will become white dcBW.BitBlt(0, 0, nWidth, nHeight, dcTemp1, 0, 0, SRCCOPY); // Cleanup dcTemp1.SelectBitmap(hOldBmp1); dcTemp2.SelectBitmap(hOldBmp2); } // Paint the destination rectangle using hBrushBackground if(hBrushBackground != NULL) { RECT rc = { x, y, x + nWidth, y + nHeight }; FillRect(&rc, hBrushBackground); } // BitBlt the black bits in the monochrome bitmap into hBrush3DEffect color in the destination DC // The magic ROP comes from the Charles Petzold's book HBRUSH hOldBrush = SelectBrush(hBrush3DEffect); BitBlt(x + 1, y + 1, nWidth, nHeight, dcBW, 0, 0, 0xB8074A); // BitBlt the black bits in the monochrome bitmap into hBrushDisabledImage color in the destination DC SelectBrush(hBrushDisabledImage); BitBlt(x, y, nWidth, nHeight, dcBW, 0, 0, 0xB8074A); SelectBrush(hOldBrush); dcBW.SelectBitmap(hbmOldBW); dc.SelectBitmap(hbmOldDC); if(hSrcDC == NULL) dc.DeleteDC(); return TRUE; } // Text Functions #ifndef _WIN32_WCE BOOL TextOut(int x, int y, LPCTSTR lpszString, int nCount = -1) { ATLASSERT(m_hDC != NULL); if(nCount == -1) nCount = lstrlen(lpszString); return ::TextOut(m_hDC, x, y, lpszString, nCount); } #endif // !_WIN32_WCE BOOL ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect, LPCTSTR lpszString, UINT nCount = -1, LPINT lpDxWidths = NULL) { ATLASSERT(m_hDC != NULL); if(nCount == -1) nCount = lstrlen(lpszString); return ::ExtTextOut(m_hDC, x, y, nOptions, lpRect, lpszString, nCount, lpDxWidths); } #ifndef _WIN32_WCE SIZE TabbedTextOut(int x, int y, LPCTSTR lpszString, int nCount = -1, int nTabPositions = 0, LPINT lpnTabStopPositions = NULL, int nTabOrigin = 0) { ATLASSERT(m_hDC != NULL); if(nCount == -1) nCount = lstrlen(lpszString); LONG lRes = ::TabbedTextOut(m_hDC, x, y, lpszString, nCount, nTabPositions, lpnTabStopPositions, nTabOrigin); SIZE size = { GET_X_LPARAM(lRes), GET_Y_LPARAM(lRes) }; return size; } #endif // !_WIN32_WCE int DrawText(LPCTSTR lpstrText, int cchText, LPRECT lpRect, UINT uFormat) { ATLASSERT(m_hDC != NULL); #ifndef _WIN32_WCE ATLASSERT((uFormat & DT_MODIFYSTRING) == 0); #endif // !_WIN32_WCE return ::DrawText(m_hDC, lpstrText, cchText, lpRect, uFormat); } int DrawText(LPTSTR lpstrText, int cchText, LPRECT lpRect, UINT uFormat) { ATLASSERT(m_hDC != NULL); return ::DrawText(m_hDC, lpstrText, cchText, lpRect, uFormat); } #ifndef _WIN32_WCE int DrawTextEx(LPTSTR lpstrText, int cchText, LPRECT lpRect, UINT uFormat, LPDRAWTEXTPARAMS lpDTParams = NULL) { ATLASSERT(m_hDC != NULL); return ::DrawTextEx(m_hDC, lpstrText, cchText, lpRect, uFormat, lpDTParams); } #endif // !_WIN32_WCE #if (_WIN32_WINNT >= 0x0501) int DrawShadowText(LPCWSTR lpstrText, int cchText, LPRECT lpRect, DWORD dwFlags, COLORREF clrText, COLORREF clrShadow, int xOffset, int yOffset) { ATLASSERT(m_hDC != NULL); // This function is present only if comctl32.dll version 6 is loaded; // we use LoadLibrary/GetProcAddress to allow apps compiled with // _WIN32_WINNT >= 0x0501 to run on older Windows/CommCtrl int nRet = 0; HMODULE hCommCtrlDLL = ::LoadLibrary(_T("comctl32.dll")); ATLASSERT(hCommCtrlDLL != NULL); if(hCommCtrlDLL != NULL) { typedef int (WINAPI *PFN_DrawShadowText)(HDC hDC, LPCWSTR lpstrText, UINT cchText, LPRECT lpRect, DWORD dwFlags, COLORREF clrText, COLORREF clrShadow, int xOffset, int yOffset); PFN_DrawShadowText pfnDrawShadowText = (PFN_DrawShadowText)::GetProcAddress(hCommCtrlDLL, "DrawShadowText"); ATLASSERT(pfnDrawShadowText != NULL); // this function requires CommCtrl6 if(pfnDrawShadowText != NULL) nRet = pfnDrawShadowText(m_hDC, lpstrText, cchText, lpRect, dwFlags, clrText, clrShadow, xOffset, yOffset); ::FreeLibrary(hCommCtrlDLL); } return nRet; } #endif // (_WIN32_WINNT >= 0x0501) BOOL GetTextExtent(LPCTSTR lpszString, int nCount, LPSIZE lpSize) const { ATLASSERT(m_hDC != NULL); if(nCount == -1) nCount = lstrlen(lpszString); return ::GetTextExtentPoint32(m_hDC, lpszString, nCount, lpSize); } BOOL GetTextExtentExPoint(LPCTSTR lpszString, int cchString, LPSIZE lpSize, int nMaxExtent, LPINT lpnFit = NULL, LPINT alpDx = NULL) { ATLASSERT(m_hDC != NULL); return ::GetTextExtentExPoint(m_hDC, lpszString, cchString, nMaxExtent, lpnFit, alpDx, lpSize); } #ifndef _WIN32_WCE DWORD GetTabbedTextExtent(LPCTSTR lpszString, int nCount = -1, int nTabPositions = 0, LPINT lpnTabStopPositions = NULL) const { ATLASSERT(m_hDC != NULL); if(nCount == -1) nCount = lstrlen(lpszString); return ::GetTabbedTextExtent(m_hDC, lpszString, nCount, nTabPositions, lpnTabStopPositions); } BOOL GrayString(HBRUSH hBrush, BOOL (CALLBACK* lpfnOutput)(HDC, LPARAM, int), LPARAM lpData, int nCount, int x, int y, int nWidth, int nHeight) { ATLASSERT(m_hDC != NULL); return ::GrayString(m_hDC, hBrush, (GRAYSTRINGPROC)lpfnOutput, lpData, nCount, x, y, nWidth, nHeight); } #endif // !_WIN32_WCE #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400) UINT GetTextAlign() const { ATLASSERT(m_hDC != NULL); return ::GetTextAlign(m_hDC); } UINT SetTextAlign(UINT nFlags) { ATLASSERT(m_hDC != NULL); return ::SetTextAlign(m_hDC, nFlags); } #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400) int GetTextFace(LPTSTR lpszFacename, int nCount) const { ATLASSERT(m_hDC != NULL); return ::GetTextFace(m_hDC, nCount, lpszFacename); } int GetTextFaceLen() const { ATLASSERT(m_hDC != NULL); return ::GetTextFace(m_hDC, 0, NULL); } #ifndef _ATL_NO_COM #ifdef _OLEAUTO_H_ BOOL GetTextFace(BSTR& bstrFace) const { USES_CONVERSION; ATLASSERT(m_hDC != NULL); ATLASSERT(bstrFace == NULL); int nLen = GetTextFaceLen(); if(nLen == 0) return FALSE; CTempBuffer buff; LPTSTR lpszText = buff.Allocate(nLen); if(lpszText == NULL) return FALSE; if(!GetTextFace(lpszText, nLen)) return FALSE; bstrFace = ::SysAllocString(T2OLE(lpszText)); return (bstrFace != NULL) ? TRUE : FALSE; } #endif #endif // !_ATL_NO_COM #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) int GetTextFace(_CSTRING_NS::CString& strFace) const { ATLASSERT(m_hDC != NULL); int nLen = GetTextFaceLen(); if(nLen == 0) return 0; LPTSTR lpstr = strFace.GetBufferSetLength(nLen); if(lpstr == NULL) return 0; int nRet = GetTextFace(lpstr, nLen); strFace.ReleaseBuffer(); return nRet; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) BOOL GetTextMetrics(LPTEXTMETRIC lpMetrics) const { ATLASSERT(m_hDC != NULL); return ::GetTextMetrics(m_hDC, lpMetrics); } #ifndef _WIN32_WCE int SetTextJustification(int nBreakExtra, int nBreakCount) { ATLASSERT(m_hDC != NULL); return ::SetTextJustification(m_hDC, nBreakExtra, nBreakCount); } int GetTextCharacterExtra() const { ATLASSERT(m_hDC != NULL); return ::GetTextCharacterExtra(m_hDC); } int SetTextCharacterExtra(int nCharExtra) { ATLASSERT(m_hDC != NULL); return ::SetTextCharacterExtra(m_hDC, nCharExtra); } #endif // !_WIN32_WCE // Advanced Drawing BOOL DrawEdge(LPRECT lpRect, UINT nEdge, UINT nFlags) { ATLASSERT(m_hDC != NULL); return ::DrawEdge(m_hDC, lpRect, nEdge, nFlags); } BOOL DrawFrameControl(LPRECT lpRect, UINT nType, UINT nState) { ATLASSERT(m_hDC != NULL); return ::DrawFrameControl(m_hDC, lpRect, nType, nState); } // Scrolling Functions BOOL ScrollDC(int dx, int dy, LPCRECT lpRectScroll, LPCRECT lpRectClip, HRGN hRgnUpdate, LPRECT lpRectUpdate) { ATLASSERT(m_hDC != NULL); return ::ScrollDC(m_hDC, dx, dy, lpRectScroll, lpRectClip, hRgnUpdate, lpRectUpdate); } // Font Functions #ifndef _WIN32_WCE BOOL GetCharWidth(UINT nFirstChar, UINT nLastChar, LPINT lpBuffer) const { ATLASSERT(m_hDC != NULL); return ::GetCharWidth(m_hDC, nFirstChar, nLastChar, lpBuffer); } // GetCharWidth32 is not supported under Win9x BOOL GetCharWidth32(UINT nFirstChar, UINT nLastChar, LPINT lpBuffer) const { ATLASSERT(m_hDC != NULL); return ::GetCharWidth32(m_hDC, nFirstChar, nLastChar, lpBuffer); } DWORD SetMapperFlags(DWORD dwFlag) { ATLASSERT(m_hDC != NULL); return ::SetMapperFlags(m_hDC, dwFlag); } BOOL GetAspectRatioFilter(LPSIZE lpSize) const { ATLASSERT(m_hDC != NULL); return ::GetAspectRatioFilterEx(m_hDC, lpSize); } BOOL GetCharABCWidths(UINT nFirstChar, UINT nLastChar, LPABC lpabc) const { ATLASSERT(m_hDC != NULL); return ::GetCharABCWidths(m_hDC, nFirstChar, nLastChar, lpabc); } DWORD GetFontData(DWORD dwTable, DWORD dwOffset, LPVOID lpData, DWORD cbData) const { ATLASSERT(m_hDC != NULL); return ::GetFontData(m_hDC, dwTable, dwOffset, lpData, cbData); } int GetKerningPairs(int nPairs, LPKERNINGPAIR lpkrnpair) const { ATLASSERT(m_hDC != NULL); return ::GetKerningPairs(m_hDC, nPairs, lpkrnpair); } UINT GetOutlineTextMetrics(UINT cbData, LPOUTLINETEXTMETRIC lpotm) const { ATLASSERT(m_hDC != NULL); return ::GetOutlineTextMetrics(m_hDC, cbData, lpotm); } DWORD GetGlyphOutline(UINT nChar, UINT nFormat, LPGLYPHMETRICS lpgm, DWORD cbBuffer, LPVOID lpBuffer, const MAT2* lpmat2) const { ATLASSERT(m_hDC != NULL); return ::GetGlyphOutline(m_hDC, nChar, nFormat, lpgm, cbBuffer, lpBuffer, lpmat2); } BOOL GetCharABCWidths(UINT nFirstChar, UINT nLastChar, LPABCFLOAT lpABCF) const { ATLASSERT(m_hDC != NULL); return ::GetCharABCWidthsFloat(m_hDC, nFirstChar, nLastChar, lpABCF); } BOOL GetCharWidth(UINT nFirstChar, UINT nLastChar, float* lpFloatBuffer) const { ATLASSERT(m_hDC != NULL); return ::GetCharWidthFloat(m_hDC, nFirstChar, nLastChar, lpFloatBuffer); } #endif // !_WIN32_WCE // Printer/Device Escape Functions #ifndef _WIN32_WCE int Escape(int nEscape, int nCount, LPCSTR lpszInData, LPVOID lpOutData) { ATLASSERT(m_hDC != NULL); return ::Escape(m_hDC, nEscape, nCount, lpszInData, lpOutData); } #endif // !_WIN32_WCE int Escape(int nEscape, int nInputSize, LPCSTR lpszInputData, int nOutputSize, LPSTR lpszOutputData) { ATLASSERT(m_hDC != NULL); return ::ExtEscape(m_hDC, nEscape, nInputSize, lpszInputData, nOutputSize, lpszOutputData); } #ifndef _WIN32_WCE int DrawEscape(int nEscape, int nInputSize, LPCSTR lpszInputData) { ATLASSERT(m_hDC != NULL); return ::DrawEscape(m_hDC, nEscape, nInputSize, lpszInputData); } #endif // !_WIN32_WCE // Escape helpers #if !defined(_WIN32_WCE) || ((_WIN32_WCE >= 200) && defined(StartDoc)) int StartDoc(LPCTSTR lpszDocName) // old Win3.0 version { DOCINFO di = { 0 }; di.cbSize = sizeof(DOCINFO); di.lpszDocName = lpszDocName; return StartDoc(&di); } int StartDoc(LPDOCINFO lpDocInfo) { ATLASSERT(m_hDC != NULL); return ::StartDoc(m_hDC, lpDocInfo); } int StartPage() { ATLASSERT(m_hDC != NULL); return ::StartPage(m_hDC); } int EndPage() { ATLASSERT(m_hDC != NULL); return ::EndPage(m_hDC); } int SetAbortProc(BOOL (CALLBACK* lpfn)(HDC, int)) { ATLASSERT(m_hDC != NULL); return ::SetAbortProc(m_hDC, (ABORTPROC)lpfn); } int AbortDoc() { ATLASSERT(m_hDC != NULL); return ::AbortDoc(m_hDC); } int EndDoc() { ATLASSERT(m_hDC != NULL); return ::EndDoc(m_hDC); } #endif // !defined(_WIN32_WCE) || ((_WIN32_WCE >= 200) && defined(StartDoc)) // MetaFile Functions #ifndef _WIN32_WCE BOOL PlayMetaFile(HMETAFILE hMF) { ATLASSERT(m_hDC != NULL); if(::GetDeviceCaps(m_hDC, TECHNOLOGY) == DT_METAFILE) { // playing metafile in metafile, just use core windows API return ::PlayMetaFile(m_hDC, hMF); } // for special playback, lParam == pDC return ::EnumMetaFile(m_hDC, hMF, EnumMetaFileProc, (LPARAM)this); } BOOL PlayMetaFile(HENHMETAFILE hEnhMetaFile, LPCRECT lpBounds) { ATLASSERT(m_hDC != NULL); return ::PlayEnhMetaFile(m_hDC, hEnhMetaFile, lpBounds); } BOOL AddMetaFileComment(UINT nDataSize, const BYTE* pCommentData) // can be used for enhanced metafiles only { ATLASSERT(m_hDC != NULL); return ::GdiComment(m_hDC, nDataSize, pCommentData); } // Special handling for metafile playback static int CALLBACK EnumMetaFileProc(HDC hDC, HANDLETABLE* pHandleTable, METARECORD* pMetaRec, int nHandles, LPARAM lParam) { CDCHandle* pDC = (CDCHandle*)lParam; switch (pMetaRec->rdFunction) { case META_SETMAPMODE: pDC->SetMapMode((int)(short)pMetaRec->rdParm[0]); break; case META_SETWINDOWEXT: pDC->SetWindowExt((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); break; case META_SETWINDOWORG: pDC->SetWindowOrg((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); break; case META_SETVIEWPORTEXT: pDC->SetViewportExt((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); break; case META_SETVIEWPORTORG: pDC->SetViewportOrg((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); break; case META_SCALEWINDOWEXT: pDC->ScaleWindowExt((int)(short)pMetaRec->rdParm[3], (int)(short)pMetaRec->rdParm[2], (int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); break; case META_SCALEVIEWPORTEXT: pDC->ScaleViewportExt((int)(short)pMetaRec->rdParm[3], (int)(short)pMetaRec->rdParm[2], (int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); break; case META_OFFSETVIEWPORTORG: pDC->OffsetViewportOrg((int)(short)pMetaRec->rdParm[1], (int)(short)pMetaRec->rdParm[0]); break; case META_SAVEDC: pDC->SaveDC(); break; case META_RESTOREDC: pDC->RestoreDC((int)(short)pMetaRec->rdParm[0]); break; case META_SETBKCOLOR: pDC->SetBkColor(*(UNALIGNED COLORREF*)&pMetaRec->rdParm[0]); break; case META_SETTEXTCOLOR: pDC->SetTextColor(*(UNALIGNED COLORREF*)&pMetaRec->rdParm[0]); break; // need to watch out for SelectObject(HFONT), for custom font mapping case META_SELECTOBJECT: { HGDIOBJ hObject = pHandleTable->objectHandle[pMetaRec->rdParm[0]]; UINT nObjType = ::GetObjectType(hObject); if(nObjType == 0) { // object type is unknown, determine if it is a font HFONT hStockFont = (HFONT)::GetStockObject(SYSTEM_FONT); HFONT hFontOld = (HFONT)::SelectObject(pDC->m_hDC, hStockFont); HGDIOBJ hObjOld = ::SelectObject(pDC->m_hDC, hObject); if(hObjOld == hStockFont) { // got the stock object back, so must be selecting a font pDC->SelectFont((HFONT)hObject); break; // don't play the default record } else { // didn't get the stock object back, so restore everything ::SelectObject(pDC->m_hDC, hFontOld); ::SelectObject(pDC->m_hDC, hObjOld); } // and fall through to PlayMetaFileRecord... } else if(nObjType == OBJ_FONT) { // play back as CDCHandle::SelectFont(HFONT) pDC->SelectFont((HFONT)hObject); break; // don't play the default record } } // fall through... default: ::PlayMetaFileRecord(hDC, pHandleTable, pMetaRec, nHandles); break; } return 1; } #endif // !_WIN32_WCE // Path Functions #ifndef _WIN32_WCE BOOL AbortPath() { ATLASSERT(m_hDC != NULL); return ::AbortPath(m_hDC); } BOOL BeginPath() { ATLASSERT(m_hDC != NULL); return ::BeginPath(m_hDC); } BOOL CloseFigure() { ATLASSERT(m_hDC != NULL); return ::CloseFigure(m_hDC); } BOOL EndPath() { ATLASSERT(m_hDC != NULL); return ::EndPath(m_hDC); } BOOL FillPath() { ATLASSERT(m_hDC != NULL); return ::FillPath(m_hDC); } BOOL FlattenPath() { ATLASSERT(m_hDC != NULL); return ::FlattenPath(m_hDC); } BOOL StrokeAndFillPath() { ATLASSERT(m_hDC != NULL); return ::StrokeAndFillPath(m_hDC); } BOOL StrokePath() { ATLASSERT(m_hDC != NULL); return ::StrokePath(m_hDC); } BOOL WidenPath() { ATLASSERT(m_hDC != NULL); return ::WidenPath(m_hDC); } BOOL GetMiterLimit(PFLOAT pfMiterLimit) const { ATLASSERT(m_hDC != NULL); return ::GetMiterLimit(m_hDC, pfMiterLimit); } BOOL SetMiterLimit(float fMiterLimit) { ATLASSERT(m_hDC != NULL); return ::SetMiterLimit(m_hDC, fMiterLimit, NULL); } int GetPath(LPPOINT lpPoints, LPBYTE lpTypes, int nCount) const { ATLASSERT(m_hDC != NULL); return ::GetPath(m_hDC, lpPoints, lpTypes, nCount); } BOOL SelectClipPath(int nMode) { ATLASSERT(m_hDC != NULL); return ::SelectClipPath(m_hDC, nMode); } #endif // !_WIN32_WCE // Misc Helper Functions static CBrushHandle PASCAL GetHalftoneBrush() { HBRUSH halftoneBrush = NULL; WORD grayPattern[8] = { 0 }; for(int i = 0; i < 8; i++) grayPattern[i] = (WORD)(0x5555 << (i & 1)); HBITMAP grayBitmap = CreateBitmap(8, 8, 1, 1, &grayPattern); if(grayBitmap != NULL) { halftoneBrush = ::CreatePatternBrush(grayBitmap); DeleteObject(grayBitmap); } return CBrushHandle(halftoneBrush); } void DrawDragRect(LPCRECT lpRect, SIZE size, LPCRECT lpRectLast, SIZE sizeLast, HBRUSH hBrush = NULL, HBRUSH hBrushLast = NULL) { // first, determine the update region and select it CRgn rgnOutside; rgnOutside.CreateRectRgnIndirect(lpRect); RECT rect = *lpRect; ::InflateRect(&rect, -size.cx, -size.cy); ::IntersectRect(&rect, &rect, lpRect); CRgn rgnInside; rgnInside.CreateRectRgnIndirect(&rect); CRgn rgnNew; rgnNew.CreateRectRgn(0, 0, 0, 0); rgnNew.CombineRgn(rgnOutside, rgnInside, RGN_XOR); HBRUSH hBrushOld = NULL; CBrush brushHalftone; if(hBrush == NULL) brushHalftone = hBrush = CDCHandle::GetHalftoneBrush(); if(hBrushLast == NULL) hBrushLast = hBrush; CRgn rgnLast; CRgn rgnUpdate; if(lpRectLast != NULL) { // find difference between new region and old region rgnLast.CreateRectRgn(0, 0, 0, 0); rgnOutside.SetRectRgn(lpRectLast->left, lpRectLast->top, lpRectLast->right, lpRectLast->bottom); rect = *lpRectLast; ::InflateRect(&rect, -sizeLast.cx, -sizeLast.cy); ::IntersectRect(&rect, &rect, lpRectLast); rgnInside.SetRectRgn(rect.left, rect.top, rect.right, rect.bottom); rgnLast.CombineRgn(rgnOutside, rgnInside, RGN_XOR); // only diff them if brushes are the same if(hBrush == hBrushLast) { rgnUpdate.CreateRectRgn(0, 0, 0, 0); rgnUpdate.CombineRgn(rgnLast, rgnNew, RGN_XOR); } } if(hBrush != hBrushLast && lpRectLast != NULL) { // brushes are different -- erase old region first SelectClipRgn(rgnLast); GetClipBox(&rect); hBrushOld = SelectBrush(hBrushLast); PatBlt(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, PATINVERT); SelectBrush(hBrushOld); hBrushOld = NULL; } // draw into the update/new region SelectClipRgn(rgnUpdate.IsNull() ? rgnNew : rgnUpdate); GetClipBox(&rect); hBrushOld = SelectBrush(hBrush); PatBlt(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, PATINVERT); // cleanup DC if(hBrushOld != NULL) SelectBrush(hBrushOld); SelectClipRgn(NULL); } void FillSolidRect(LPCRECT lpRect, COLORREF clr) { ATLASSERT(m_hDC != NULL); COLORREF clrOld = ::SetBkColor(m_hDC, clr); ATLASSERT(clrOld != CLR_INVALID); if(clrOld != CLR_INVALID) { ::ExtTextOut(m_hDC, 0, 0, ETO_OPAQUE, lpRect, NULL, 0, NULL); ::SetBkColor(m_hDC, clrOld); } } void FillSolidRect(int x, int y, int cx, int cy, COLORREF clr) { ATLASSERT(m_hDC != NULL); RECT rect = { x, y, x + cx, y + cy }; FillSolidRect(&rect, clr); } void Draw3dRect(LPCRECT lpRect, COLORREF clrTopLeft, COLORREF clrBottomRight) { Draw3dRect(lpRect->left, lpRect->top, lpRect->right - lpRect->left, lpRect->bottom - lpRect->top, clrTopLeft, clrBottomRight); } void Draw3dRect(int x, int y, int cx, int cy, COLORREF clrTopLeft, COLORREF clrBottomRight) { FillSolidRect(x, y, cx - 1, 1, clrTopLeft); FillSolidRect(x, y, 1, cy - 1, clrTopLeft); FillSolidRect(x + cx, y, -1, cy, clrBottomRight); FillSolidRect(x, y + cy, cx, -1, clrBottomRight); } // DIB support #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 410) int SetDIBitsToDevice(int x, int y, DWORD dwWidth, DWORD dwHeight, int xSrc, int ySrc, UINT uStartScan, UINT cScanLines, CONST VOID* lpvBits, CONST BITMAPINFO* lpbmi, UINT uColorUse) { ATLASSERT(m_hDC != NULL); return ::SetDIBitsToDevice(m_hDC, x, y, dwWidth, dwHeight, xSrc, ySrc, uStartScan, cScanLines, lpvBits, lpbmi, uColorUse); } #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 410) #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400) int StretchDIBits(int x, int y, int nWidth, int nHeight, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, CONST VOID* lpvBits, CONST BITMAPINFO* lpbmi, UINT uColorUse, DWORD dwRop) { ATLASSERT(m_hDC != NULL); return ::StretchDIBits(m_hDC, x, y, nWidth, nHeight, xSrc, ySrc, nSrcWidth, nSrcHeight, lpvBits, lpbmi, uColorUse, dwRop); } UINT GetDIBColorTable(UINT uStartIndex, UINT cEntries, RGBQUAD* pColors) const { ATLASSERT(m_hDC != NULL); return ::GetDIBColorTable(m_hDC, uStartIndex, cEntries, pColors); } UINT SetDIBColorTable(UINT uStartIndex, UINT cEntries, CONST RGBQUAD* pColors) { ATLASSERT(m_hDC != NULL); return ::SetDIBColorTable(m_hDC, uStartIndex, cEntries, pColors); } #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400) // OpenGL support #if !defined(_ATL_NO_OPENGL) && !defined(_WIN32_WCE) int ChoosePixelFormat(CONST PIXELFORMATDESCRIPTOR* ppfd) { ATLASSERT(m_hDC != NULL); return ::ChoosePixelFormat(m_hDC, ppfd); } int DescribePixelFormat(int iPixelFormat, UINT nBytes, LPPIXELFORMATDESCRIPTOR ppfd) { ATLASSERT(m_hDC != NULL); return ::DescribePixelFormat(m_hDC, iPixelFormat, nBytes, ppfd); } int GetPixelFormat() const { ATLASSERT(m_hDC != NULL); return ::GetPixelFormat(m_hDC); } BOOL SetPixelFormat(int iPixelFormat, CONST PIXELFORMATDESCRIPTOR* ppfd) { ATLASSERT(m_hDC != NULL); return ::SetPixelFormat(m_hDC, iPixelFormat, ppfd); } BOOL SwapBuffers() { ATLASSERT(m_hDC != NULL); return ::SwapBuffers(m_hDC); } HGLRC wglCreateContext() { ATLASSERT(m_hDC != NULL); return ::wglCreateContext(m_hDC); } HGLRC wglCreateLayerContext(int iLayerPlane) { ATLASSERT(m_hDC != NULL); return ::wglCreateLayerContext(m_hDC, iLayerPlane); } BOOL wglMakeCurrent(HGLRC hglrc) { ATLASSERT(m_hDC != NULL); return ::wglMakeCurrent(m_hDC, hglrc); } BOOL wglUseFontBitmaps(DWORD dwFirst, DWORD dwCount, DWORD listBase) { ATLASSERT(m_hDC != NULL); return ::wglUseFontBitmaps(m_hDC, dwFirst, dwCount, listBase); } BOOL wglUseFontOutlines(DWORD dwFirst, DWORD dwCount, DWORD listBase, FLOAT deviation, FLOAT extrusion, int format, LPGLYPHMETRICSFLOAT lpgmf) { ATLASSERT(m_hDC != NULL); return ::wglUseFontOutlines(m_hDC, dwFirst, dwCount, listBase, deviation, extrusion, format, lpgmf); } BOOL wglDescribeLayerPlane(int iPixelFormat, int iLayerPlane, UINT nBytes, LPLAYERPLANEDESCRIPTOR plpd) { ATLASSERT(m_hDC != NULL); return ::wglDescribeLayerPlane(m_hDC, iPixelFormat, iLayerPlane, nBytes, plpd); } int wglSetLayerPaletteEntries(int iLayerPlane, int iStart, int cEntries, CONST COLORREF* pclr) { ATLASSERT(m_hDC != NULL); return ::wglSetLayerPaletteEntries(m_hDC, iLayerPlane, iStart, cEntries, pclr); } int wglGetLayerPaletteEntries(int iLayerPlane, int iStart, int cEntries, COLORREF* pclr) { ATLASSERT(m_hDC != NULL); return ::wglGetLayerPaletteEntries(m_hDC, iLayerPlane, iStart, cEntries, pclr); } BOOL wglRealizeLayerPalette(int iLayerPlane, BOOL bRealize) { ATLASSERT(m_hDC != NULL); return ::wglRealizeLayerPalette(m_hDC, iLayerPlane, bRealize); } BOOL wglSwapLayerBuffers(UINT uPlanes) { ATLASSERT(m_hDC != NULL); return ::wglSwapLayerBuffers(m_hDC, uPlanes); } #endif // !defined(_ATL_NO_OPENGL) && !defined(_WIN32_WCE) // New for Windows 2000 only #if (_WIN32_WINNT >= 0x0500) COLORREF GetDCPenColor() const { ATLASSERT(m_hDC != NULL); return ::GetDCPenColor(m_hDC); } COLORREF SetDCPenColor(COLORREF clr) { ATLASSERT(m_hDC != NULL); return ::SetDCPenColor(m_hDC, clr); } COLORREF GetDCBrushColor() const { ATLASSERT(m_hDC != NULL); return ::GetDCBrushColor(m_hDC); } COLORREF SetDCBrushColor(COLORREF clr) { ATLASSERT(m_hDC != NULL); return ::SetDCBrushColor(m_hDC, clr); } #ifndef _WIN32_WCE DWORD GetFontUnicodeRanges(LPGLYPHSET lpgs) const { ATLASSERT(m_hDC != NULL); return ::GetFontUnicodeRanges(m_hDC, lpgs); } #endif // !_WIN32_WCE DWORD GetGlyphIndices(LPCTSTR lpstr, int cch, LPWORD pgi, DWORD dwFlags) const { ATLASSERT(m_hDC != NULL); return ::GetGlyphIndices(m_hDC, lpstr, cch, pgi, dwFlags); } BOOL GetTextExtentPointI(LPWORD pgiIn, int cgi, LPSIZE lpSize) const { ATLASSERT(m_hDC != NULL); return ::GetTextExtentPointI(m_hDC, pgiIn, cgi, lpSize); } BOOL GetTextExtentExPointI(LPWORD pgiIn, int cgi, int nMaxExtent, LPINT lpnFit, LPINT alpDx, LPSIZE lpSize) const { ATLASSERT(m_hDC != NULL); return ::GetTextExtentExPointI(m_hDC, pgiIn, cgi, nMaxExtent, lpnFit, alpDx, lpSize); } BOOL GetCharWidthI(UINT giFirst, UINT cgi, LPWORD pgi, LPINT lpBuffer) const { ATLASSERT(m_hDC != NULL); return ::GetCharWidthI(m_hDC, giFirst, cgi, pgi, lpBuffer); } BOOL GetCharABCWidthsI(UINT giFirst, UINT cgi, LPWORD pgi, LPABC lpabc) const { ATLASSERT(m_hDC != NULL); return ::GetCharABCWidthsI(m_hDC, giFirst, cgi, pgi, lpabc); } #endif // (_WIN32_WINNT >= 0x0500) // New for Windows 2000 and Windows 98 #if (WINVER >= 0x0500) && !defined(_WIN32_WCE) BOOL ColorCorrectPalette(HPALETTE hPalette, DWORD dwFirstEntry, DWORD dwNumOfEntries) { ATLASSERT(m_hDC != NULL); return ::ColorCorrectPalette(m_hDC, hPalette, dwFirstEntry, dwNumOfEntries); } #endif // (WINVER >= 0x0500) && !defined(_WIN32_WCE) }; typedef CDCT CDCHandle; typedef CDCT CDC; /////////////////////////////////////////////////////////////////////////////// // CDC Helpers class CPaintDC : public CDC { public: // Data members HWND m_hWnd; PAINTSTRUCT m_ps; // Constructor/destructor CPaintDC(HWND hWnd) { ATLASSERT(::IsWindow(hWnd)); m_hWnd = hWnd; m_hDC = ::BeginPaint(hWnd, &m_ps); } ~CPaintDC() { ATLASSERT(m_hDC != NULL); ATLASSERT(::IsWindow(m_hWnd)); ::EndPaint(m_hWnd, &m_ps); Detach(); } }; class CClientDC : public CDC { public: // Data members HWND m_hWnd; // Constructor/destructor CClientDC(HWND hWnd) { ATLASSERT(hWnd == NULL || ::IsWindow(hWnd)); m_hWnd = hWnd; m_hDC = ::GetDC(hWnd); } ~CClientDC() { ATLASSERT(m_hDC != NULL); ::ReleaseDC(m_hWnd, Detach()); } }; class CWindowDC : public CDC { public: // Data members HWND m_hWnd; // Constructor/destructor CWindowDC(HWND hWnd) { ATLASSERT(hWnd == NULL || ::IsWindow(hWnd)); m_hWnd = hWnd; m_hDC = ::GetWindowDC(hWnd); } ~CWindowDC() { ATLASSERT(m_hDC != NULL); ::ReleaseDC(m_hWnd, Detach()); } }; class CMemoryDC : public CDC { public: // Data members HDC m_hDCOriginal; RECT m_rcPaint; CBitmap m_bmp; HBITMAP m_hBmpOld; // Constructor/destructor CMemoryDC(HDC hDC, const RECT& rcPaint) : m_hDCOriginal(hDC), m_hBmpOld(NULL) { m_rcPaint = rcPaint; CreateCompatibleDC(m_hDCOriginal); ATLASSERT(m_hDC != NULL); m_bmp.CreateCompatibleBitmap(m_hDCOriginal, m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom - m_rcPaint.top); ATLASSERT(m_bmp.m_hBitmap != NULL); m_hBmpOld = SelectBitmap(m_bmp); SetViewportOrg(-m_rcPaint.left, -m_rcPaint.top); } ~CMemoryDC() { ::BitBlt(m_hDCOriginal, m_rcPaint.left, m_rcPaint.top, m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom - m_rcPaint.top, m_hDC, m_rcPaint.left, m_rcPaint.top, SRCCOPY); SelectBitmap(m_hBmpOld); } }; /////////////////////////////////////////////////////////////////////////////// // Enhanced metafile support #ifndef _WIN32_WCE class CEnhMetaFileInfo { public: // Data members HENHMETAFILE m_hEMF; BYTE* m_pBits; TCHAR* m_pDesc; ENHMETAHEADER m_header; PIXELFORMATDESCRIPTOR m_pfd; // Constructor/destructor CEnhMetaFileInfo(HENHMETAFILE hEMF) : m_pBits(NULL), m_pDesc(NULL), m_hEMF(hEMF) { } ~CEnhMetaFileInfo() { delete [] m_pBits; delete [] m_pDesc; } // Operations BYTE* GetEnhMetaFileBits() { ATLASSERT(m_hEMF != NULL); UINT nBytes = ::GetEnhMetaFileBits(m_hEMF, 0, NULL); delete [] m_pBits; m_pBits = NULL; ATLTRY(m_pBits = new BYTE[nBytes]); if (m_pBits != NULL) ::GetEnhMetaFileBits(m_hEMF, nBytes, m_pBits); return m_pBits; } LPTSTR GetEnhMetaFileDescription() { ATLASSERT(m_hEMF != NULL); UINT nLen = ::GetEnhMetaFileDescription(m_hEMF, 0, NULL); delete [] m_pDesc; m_pDesc = NULL; ATLTRY(m_pDesc = new TCHAR[nLen]); if (m_pDesc != NULL) nLen = ::GetEnhMetaFileDescription(m_hEMF, nLen, m_pDesc); return m_pDesc; } ENHMETAHEADER* GetEnhMetaFileHeader() { ATLASSERT(m_hEMF != NULL); memset(&m_header, 0, sizeof(m_header)); m_header.iType = EMR_HEADER; m_header.nSize = sizeof(ENHMETAHEADER); UINT n = ::GetEnhMetaFileHeader(m_hEMF, sizeof(ENHMETAHEADER), &m_header); return (n != 0) ? &m_header : NULL; } PIXELFORMATDESCRIPTOR* GetEnhMetaFilePixelFormat() { ATLASSERT(m_hEMF != NULL); memset(&m_pfd, 0, sizeof(m_pfd)); UINT n = ::GetEnhMetaFilePixelFormat(m_hEMF, sizeof(m_pfd), &m_pfd); return (n != 0) ? &m_pfd : NULL; } }; template class CEnhMetaFileT { public: // Data members HENHMETAFILE m_hEMF; // Constructor/destructor CEnhMetaFileT(HENHMETAFILE hEMF = NULL) : m_hEMF(hEMF) { } ~CEnhMetaFileT() { if(t_bManaged && m_hEMF != NULL) DeleteObject(); } // Operations CEnhMetaFileT& operator =(HENHMETAFILE hEMF) { Attach(hEMF); return *this; } void Attach(HENHMETAFILE hEMF) { if(t_bManaged && m_hEMF != NULL && m_hEMF != hEMF) DeleteObject(); m_hEMF = hEMF; } HENHMETAFILE Detach() { HENHMETAFILE hEMF = m_hEMF; m_hEMF = NULL; return hEMF; } operator HENHMETAFILE() const { return m_hEMF; } bool IsNull() const { return (m_hEMF == NULL); } BOOL DeleteObject() { ATLASSERT(m_hEMF != NULL); BOOL bRet = ::DeleteEnhMetaFile(m_hEMF); m_hEMF = NULL; return bRet; } UINT GetEnhMetaFileBits(UINT cbBuffer, LPBYTE lpbBuffer) const { ATLASSERT(m_hEMF != NULL); return ::GetEnhMetaFileBits(m_hEMF, cbBuffer, lpbBuffer); } UINT GetEnhMetaFileDescription(UINT cchBuffer, LPTSTR lpszDescription) const { ATLASSERT(m_hEMF != NULL); return ::GetEnhMetaFileDescription(m_hEMF, cchBuffer, lpszDescription); } UINT GetEnhMetaFileHeader(LPENHMETAHEADER lpemh) const { ATLASSERT(m_hEMF != NULL); lpemh->iType = EMR_HEADER; lpemh->nSize = sizeof(ENHMETAHEADER); return ::GetEnhMetaFileHeader(m_hEMF, sizeof(ENHMETAHEADER), lpemh); } UINT GetEnhMetaFilePaletteEntries(UINT cEntries, LPPALETTEENTRY lppe) const { ATLASSERT(m_hEMF != NULL); return ::GetEnhMetaFilePaletteEntries(m_hEMF, cEntries, lppe); } UINT GetEnhMetaFilePixelFormat(DWORD cbBuffer, PIXELFORMATDESCRIPTOR* ppfd) const { ATLASSERT(m_hEMF != NULL); return ::GetEnhMetaFilePixelFormat(m_hEMF, cbBuffer, ppfd); } }; typedef CEnhMetaFileT CEnhMetaFileHandle; typedef CEnhMetaFileT CEnhMetaFile; class CEnhMetaFileDC : public CDC { public: // Constructor/destructor CEnhMetaFileDC() { } CEnhMetaFileDC(HDC hdc, LPCRECT lpRect) { Create(hdc, NULL, lpRect, NULL); ATLASSERT(m_hDC != NULL); } CEnhMetaFileDC(HDC hdcRef, LPCTSTR lpFilename, LPCRECT lpRect, LPCTSTR lpDescription) { Create(hdcRef, lpFilename, lpRect, lpDescription); ATLASSERT(m_hDC != NULL); } ~CEnhMetaFileDC() { HENHMETAFILE hEMF = Close(); if (hEMF != NULL) ::DeleteEnhMetaFile(hEMF); } // Operations void Create(HDC hdcRef, LPCTSTR lpFilename, LPCRECT lpRect, LPCTSTR lpDescription) { ATLASSERT(m_hDC == NULL); m_hDC = ::CreateEnhMetaFile(hdcRef, lpFilename, lpRect, lpDescription); } HENHMETAFILE Close() { HENHMETAFILE hEMF = NULL; if (m_hDC != NULL) { hEMF = ::CloseEnhMetaFile(m_hDC); m_hDC = NULL; } return hEMF; } }; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // WinCE compatible clipboard CF_DIB format support functions #ifndef _WTL_NO_DIB16 #define DIBINFO16_BITFIELDS { 31744, 992, 31 } // DIBINFO16 - To avoid color table problems in WinCE we only create this type of Dib struct DIBINFO16 // a BITMAPINFO with 2 additional color bitfields { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[3]; DIBINFO16(SIZE size) { BITMAPINFOHEADER bmih = { sizeof(BITMAPINFOHEADER), size.cx, size.cy, 1, 16, BI_BITFIELDS, 2 * size.cx * size.cy , 0, 0, 3 }; DWORD dw[3] = DIBINFO16_BITFIELDS ; bmiHeader = bmih; SecureHelper::memcpy_x(bmiColors, sizeof(bmiColors), dw, 3 * sizeof(DWORD)); } }; // AtlxxxDibxxx minimal packed DIB implementation and helpers to copy and paste CF_DIB inline bool AtlIsDib16(LPBITMAPINFOHEADER pbmih) { return (pbmih->biBitCount == 16) && (pbmih->biCompression == BI_BITFIELDS); } inline int AtlGetDibColorTableSize(LPBITMAPINFOHEADER pbmih) { switch (pbmih->biBitCount) { case 2: case 4: case 8: return pbmih->biClrUsed ? pbmih->biClrUsed : 1 << pbmih->biBitCount; case 24: break; case 16: case 32: return pbmih->biCompression == BI_BITFIELDS ? 3 : 0; default: ATLASSERT(FALSE); // should never come here } return 0; } inline int AtlGetDibNumColors(LPBITMAPINFOHEADER pbmih) { switch (pbmih->biBitCount) { case 2: case 4: case 8: if (pbmih->biClrUsed) return pbmih->biClrUsed; else break; case 16: if (pbmih->biCompression == BI_BITFIELDS ) return 1 << 15; else break; case 24: break; case 32: if (pbmih->biCompression == BI_BITFIELDS ) return 1 << 24; else break; default: ATLASSERT(FALSE); } return 1 << pbmih->biBitCount; } inline HBITMAP AtlGetDibBitmap(LPBITMAPINFO pbmi) { CDC dc(NULL); void* pBits = NULL; LPBYTE pDibBits = (LPBYTE)pbmi + sizeof(BITMAPINFOHEADER) + AtlGetDibColorTableSize(&pbmi->bmiHeader) * sizeof(RGBQUAD); HBITMAP hbm = CreateDIBSection(dc, pbmi, DIB_RGB_COLORS, &pBits, NULL, NULL); if (hbm != NULL) { int cbBits = pbmi->bmiHeader.biWidth * pbmi->bmiHeader.biHeight * pbmi->bmiHeader.biBitCount / 8; SecureHelper::memcpy_x(pBits, cbBits, pDibBits, pbmi->bmiHeader.biSizeImage); } return hbm; } inline HBITMAP AtlCopyBitmap(HBITMAP hbm, SIZE sizeDst, bool bAsBitmap = false) { CDC hdcSrc = CreateCompatibleDC(NULL); CDC hdcDst = CreateCompatibleDC(NULL); CBitmapHandle hbmOld = NULL, hbmOld2 = NULL, bmSrc = hbm; CBitmap bmNew = NULL; SIZE sizeSrc = { 0 }; bmSrc.GetSize(sizeSrc); hbmOld = hdcSrc.SelectBitmap(bmSrc); if (bAsBitmap) { bmNew.CreateCompatibleBitmap(hdcSrc, sizeDst.cx, sizeDst.cy); } else { DIBINFO16 dib16(sizeDst); LPVOID pBits = NULL; bmNew = CreateDIBSection(hdcDst, (const BITMAPINFO*)&dib16, DIB_RGB_COLORS, &pBits, NULL, NULL); } ATLASSERT(!bmNew.IsNull()); hbmOld2 = hdcDst.SelectBitmap(bmNew); BOOL bOK = FALSE; if ((sizeDst.cx == sizeSrc.cx) && (sizeDst.cy == sizeSrc.cy)) bOK = hdcDst.BitBlt(0, 0, sizeDst.cx, sizeDst.cy, hdcSrc, 0, 0, SRCCOPY); else bOK = hdcDst.StretchBlt(0, 0, sizeDst.cx, sizeDst.cy, hdcSrc, 0, 0, sizeSrc.cx, sizeSrc.cy, SRCCOPY); hdcSrc.SelectBitmap(hbmOld); hdcDst.SelectBitmap(hbmOld2); if (bOK == FALSE) bmNew.DeleteObject(); return bmNew.Detach(); } inline HLOCAL AtlCreatePackedDib16(HBITMAP hbm, SIZE size) { DIBSECTION ds = { 0 }; LPBYTE pDib = NULL; bool bCopied = false; bool bOK = GetObject(hbm, sizeof(ds), &ds) == sizeof(ds); if ((bOK == FALSE) || (ds.dsBm.bmBits == NULL) || (AtlIsDib16(&ds.dsBmih) == FALSE) || (ds.dsBmih.biWidth != size.cx ) || (ds.dsBmih.biHeight != size.cy )) { if ((hbm = AtlCopyBitmap(hbm, size)) != NULL) { bCopied = true; bOK = GetObject(hbm, sizeof(ds), &ds) == sizeof(ds); } else { bOK = FALSE; } } if((bOK != FALSE) && (AtlIsDib16(&ds.dsBmih) != FALSE) && (ds.dsBm.bmBits != NULL)) { pDib = (LPBYTE)LocalAlloc(LMEM_ZEROINIT, sizeof(DIBINFO16) + ds.dsBmih.biSizeImage); if (pDib != NULL) { SecureHelper::memcpy_x(pDib, sizeof(DIBINFO16) + ds.dsBmih.biSizeImage, &ds.dsBmih, sizeof(DIBINFO16)); SecureHelper::memcpy_x(pDib + sizeof(DIBINFO16), ds.dsBmih.biSizeImage, ds.dsBm.bmBits, ds.dsBmih.biSizeImage); } } if (bCopied == true) DeleteObject(hbm); return (HLOCAL)pDib; } inline bool AtlSetClipboardDib16(HBITMAP hbm, SIZE size, HWND hWnd) { ATLASSERT(::IsWindow(hWnd)); BOOL bOK = OpenClipboard(hWnd); if (bOK != FALSE) { bOK = EmptyClipboard(); if (bOK != FALSE) { HLOCAL hDib = AtlCreatePackedDib16(hbm, size); if (hDib != NULL) { bOK = SetClipboardData(CF_DIB, hDib) != NULL; if (bOK == FALSE) LocalFree(hDib); } else { bOK = FALSE; } } CloseClipboard(); } return (bOK != FALSE); } inline HBITMAP AtlGetClipboardDib(HWND hWnd) { ATLASSERT(::IsWindow(hWnd) != FALSE); HBITMAP hbm = NULL; if (OpenClipboard(hWnd) != FALSE) { LPBITMAPINFO pbmi = (LPBITMAPINFO)GetClipboardData(CF_DIB); if (pbmi != NULL) hbm = AtlGetDibBitmap(pbmi); CloseClipboard(); } return hbm; } #endif // _WTL_NO_DIB16 }; // namespace WTL #endif // __ATLGDI_H__ ================================================ FILE: src/Setup/wtl90/atlmisc.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLMISC_H__ #define __ATLMISC_H__ #pragma once #ifndef __ATLAPP_H__ #error atlmisc.h requires atlapp.h to be included first #endif #ifdef _ATL_TMP_NO_CSTRING #define _WTL_NO_CSTRING #endif #if defined(_WTL_USE_CSTRING) && defined(_WTL_NO_CSTRING) #error Conflicting options - both _WTL_USE_CSTRING and _WTL_NO_CSTRING are defined #endif // defined(_WTL_USE_CSTRING) && defined(_WTL_NO_CSTRING) #if !defined(_WTL_USE_CSTRING) && !defined(_WTL_NO_CSTRING) #define _WTL_USE_CSTRING #endif // !defined(_WTL_USE_CSTRING) && !defined(_WTL_NO_CSTRING) #ifndef _WTL_NO_CSTRING #if defined(_ATL_USE_CSTRING_FLOAT) && defined(_ATL_MIN_CRT) #error Cannot use CString floating point formatting with _ATL_MIN_CRT defined #endif // defined(_ATL_USE_CSTRING_FLOAT) && defined(_ATL_MIN_CRT) #endif // !_WTL_NO_CSTRING /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CSize // CPoint // CRect // CString // // CRecentDocumentListBase // CRecentDocumentList // CFindFile // // Global functions: // AtlGetStockPen() // AtlGetStockBrush() // AtlGetStockFont() // AtlGetStockPalette() // // AtlCompactPath() namespace WTL { #ifndef _WTL_NO_WTYPES // forward declarations class CSize; class CPoint; class CRect; /////////////////////////////////////////////////////////////////////////////// // CSize - Wrapper for Windows SIZE structure. class CSize : public SIZE { public: // Constructors CSize() { cx = 0; cy = 0; } CSize(int initCX, int initCY) { cx = initCX; cy = initCY; } CSize(SIZE initSize) { *(SIZE*)this = initSize; } CSize(POINT initPt) { *(POINT*)this = initPt; } CSize(DWORD dwSize) { cx = (short)LOWORD(dwSize); cy = (short)HIWORD(dwSize); } // Operations BOOL operator ==(SIZE size) const { return (cx == size.cx && cy == size.cy); } BOOL operator !=(SIZE size) const { return (cx != size.cx || cy != size.cy); } void operator +=(SIZE size) { cx += size.cx; cy += size.cy; } void operator -=(SIZE size) { cx -= size.cx; cy -= size.cy; } void SetSize(int CX, int CY) { cx = CX; cy = CY; } // Operators returning CSize values CSize operator +(SIZE size) const { return CSize(cx + size.cx, cy + size.cy); } CSize operator -(SIZE size) const { return CSize(cx - size.cx, cy - size.cy); } CSize operator -() const { return CSize(-cx, -cy); } // Operators returning CPoint values CPoint operator +(POINT point) const; CPoint operator -(POINT point) const; // Operators returning CRect values CRect operator +(const RECT* lpRect) const; CRect operator -(const RECT* lpRect) const; }; /////////////////////////////////////////////////////////////////////////////// // CPoint - Wrapper for Windows POINT structure. class CPoint : public POINT { public: // Constructors CPoint() { x = 0; y = 0; } CPoint(int initX, int initY) { x = initX; y = initY; } CPoint(POINT initPt) { *(POINT*)this = initPt; } CPoint(SIZE initSize) { *(SIZE*)this = initSize; } CPoint(DWORD dwPoint) { x = (short)LOWORD(dwPoint); y = (short)HIWORD(dwPoint); } // Operations void Offset(int xOffset, int yOffset) { x += xOffset; y += yOffset; } void Offset(POINT point) { x += point.x; y += point.y; } void Offset(SIZE size) { x += size.cx; y += size.cy; } BOOL operator ==(POINT point) const { return (x == point.x && y == point.y); } BOOL operator !=(POINT point) const { return (x != point.x || y != point.y); } void operator +=(SIZE size) { x += size.cx; y += size.cy; } void operator -=(SIZE size) { x -= size.cx; y -= size.cy; } void operator +=(POINT point) { x += point.x; y += point.y; } void operator -=(POINT point) { x -= point.x; y -= point.y; } void SetPoint(int X, int Y) { x = X; y = Y; } // Operators returning CPoint values CPoint operator +(SIZE size) const { return CPoint(x + size.cx, y + size.cy); } CPoint operator -(SIZE size) const { return CPoint(x - size.cx, y - size.cy); } CPoint operator -() const { return CPoint(-x, -y); } CPoint operator +(POINT point) const { return CPoint(x + point.x, y + point.y); } // Operators returning CSize values CSize operator -(POINT point) const { return CSize(x - point.x, y - point.y); } // Operators returning CRect values CRect operator +(const RECT* lpRect) const; CRect operator -(const RECT* lpRect) const; }; /////////////////////////////////////////////////////////////////////////////// // CRect - Wrapper for Windows RECT structure. class CRect : public RECT { public: // Constructors CRect() { left = 0; top = 0; right = 0; bottom = 0; } CRect(int l, int t, int r, int b) { left = l; top = t; right = r; bottom = b; } CRect(const RECT& srcRect) { ::CopyRect(this, &srcRect); } CRect(LPCRECT lpSrcRect) { ::CopyRect(this, lpSrcRect); } CRect(POINT point, SIZE size) { right = (left = point.x) + size.cx; bottom = (top = point.y) + size.cy; } CRect(POINT topLeft, POINT bottomRight) { left = topLeft.x; top = topLeft.y; right = bottomRight.x; bottom = bottomRight.y; } // Attributes (in addition to RECT members) int Width() const { return right - left; } int Height() const { return bottom - top; } CSize Size() const { return CSize(right - left, bottom - top); } CPoint& TopLeft() { return *((CPoint*)this); } CPoint& BottomRight() { return *((CPoint*)this + 1); } const CPoint& TopLeft() const { return *((CPoint*)this); } const CPoint& BottomRight() const { return *((CPoint*)this + 1); } CPoint CenterPoint() const { return CPoint((left + right) / 2, (top + bottom) / 2); } // convert between CRect and LPRECT/LPCRECT (no need for &) operator LPRECT() { return this; } operator LPCRECT() const { return this; } BOOL IsRectEmpty() const { return ::IsRectEmpty(this); } BOOL IsRectNull() const { return (left == 0 && right == 0 && top == 0 && bottom == 0); } BOOL PtInRect(POINT point) const { return ::PtInRect(this, point); } // Operations void SetRect(int x1, int y1, int x2, int y2) { ::SetRect(this, x1, y1, x2, y2); } void SetRect(POINT topLeft, POINT bottomRight) { ::SetRect(this, topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); } void SetRectEmpty() { ::SetRectEmpty(this); } void CopyRect(LPCRECT lpSrcRect) { ::CopyRect(this, lpSrcRect); } BOOL EqualRect(LPCRECT lpRect) const { return ::EqualRect(this, lpRect); } void InflateRect(int x, int y) { ::InflateRect(this, x, y); } void InflateRect(SIZE size) { ::InflateRect(this, size.cx, size.cy); } void InflateRect(LPCRECT lpRect) { left -= lpRect->left; top -= lpRect->top; right += lpRect->right; bottom += lpRect->bottom; } void InflateRect(int l, int t, int r, int b) { left -= l; top -= t; right += r; bottom += b; } void DeflateRect(int x, int y) { ::InflateRect(this, -x, -y); } void DeflateRect(SIZE size) { ::InflateRect(this, -size.cx, -size.cy); } void DeflateRect(LPCRECT lpRect) { left += lpRect->left; top += lpRect->top; right -= lpRect->right; bottom -= lpRect->bottom; } void DeflateRect(int l, int t, int r, int b) { left += l; top += t; right -= r; bottom -= b; } void OffsetRect(int x, int y) { ::OffsetRect(this, x, y); } void OffsetRect(SIZE size) { ::OffsetRect(this, size.cx, size.cy); } void OffsetRect(POINT point) { ::OffsetRect(this, point.x, point.y); } void NormalizeRect() { int nTemp; if (left > right) { nTemp = left; left = right; right = nTemp; } if (top > bottom) { nTemp = top; top = bottom; bottom = nTemp; } } // absolute position of rectangle void MoveToY(int y) { bottom = Height() + y; top = y; } void MoveToX(int x) { right = Width() + x; left = x; } void MoveToXY(int x, int y) { MoveToX(x); MoveToY(y); } void MoveToXY(POINT pt) { MoveToX(pt.x); MoveToY(pt.y); } // operations that fill '*this' with result BOOL IntersectRect(LPCRECT lpRect1, LPCRECT lpRect2) { return ::IntersectRect(this, lpRect1, lpRect2); } BOOL UnionRect(LPCRECT lpRect1, LPCRECT lpRect2) { return ::UnionRect(this, lpRect1, lpRect2); } BOOL SubtractRect(LPCRECT lpRectSrc1, LPCRECT lpRectSrc2) { return ::SubtractRect(this, lpRectSrc1, lpRectSrc2); } // Additional Operations void operator =(const RECT& srcRect) { ::CopyRect(this, &srcRect); } BOOL operator ==(const RECT& rect) const { return ::EqualRect(this, &rect); } BOOL operator !=(const RECT& rect) const { return !::EqualRect(this, &rect); } void operator +=(POINT point) { ::OffsetRect(this, point.x, point.y); } void operator +=(SIZE size) { ::OffsetRect(this, size.cx, size.cy); } void operator +=(LPCRECT lpRect) { InflateRect(lpRect); } void operator -=(POINT point) { ::OffsetRect(this, -point.x, -point.y); } void operator -=(SIZE size) { ::OffsetRect(this, -size.cx, -size.cy); } void operator -=(LPCRECT lpRect) { DeflateRect(lpRect); } void operator &=(const RECT& rect) { ::IntersectRect(this, this, &rect); } void operator |=(const RECT& rect) { ::UnionRect(this, this, &rect); } // Operators returning CRect values CRect operator +(POINT pt) const { CRect rect(*this); ::OffsetRect(&rect, pt.x, pt.y); return rect; } CRect operator -(POINT pt) const { CRect rect(*this); ::OffsetRect(&rect, -pt.x, -pt.y); return rect; } CRect operator +(LPCRECT lpRect) const { CRect rect(this); rect.InflateRect(lpRect); return rect; } CRect operator +(SIZE size) const { CRect rect(*this); ::OffsetRect(&rect, size.cx, size.cy); return rect; } CRect operator -(SIZE size) const { CRect rect(*this); ::OffsetRect(&rect, -size.cx, -size.cy); return rect; } CRect operator -(LPCRECT lpRect) const { CRect rect(this); rect.DeflateRect(lpRect); return rect; } CRect operator &(const RECT& rect2) const { CRect rect; ::IntersectRect(&rect, this, &rect2); return rect; } CRect operator |(const RECT& rect2) const { CRect rect; ::UnionRect(&rect, this, &rect2); return rect; } CRect MulDiv(int nMultiplier, int nDivisor) const { return CRect( ::MulDiv(left, nMultiplier, nDivisor), ::MulDiv(top, nMultiplier, nDivisor), ::MulDiv(right, nMultiplier, nDivisor), ::MulDiv(bottom, nMultiplier, nDivisor)); } }; // CSize implementation inline CPoint CSize::operator +(POINT point) const { return CPoint(cx + point.x, cy + point.y); } inline CPoint CSize::operator -(POINT point) const { return CPoint(cx - point.x, cy - point.y); } inline CRect CSize::operator +(const RECT* lpRect) const { return CRect(lpRect) + *this; } inline CRect CSize::operator -(const RECT* lpRect) const { return CRect(lpRect) - *this; } // CPoint implementation inline CRect CPoint::operator +(const RECT* lpRect) const { return CRect(lpRect) + *this; } inline CRect CPoint::operator -(const RECT* lpRect) const { return CRect(lpRect) - *this; } #endif // !_WTL_NO_WTYPES // WTL::CSize or ATL::CSize scalar operators #if !defined(_WTL_NO_SIZE_SCALAR) && (!defined(_WTL_NO_WTYPES) || defined(__ATLTYPES_H__)) template inline CSize operator *(SIZE s, Num n) { return CSize((int)(s.cx * n), (int)(s.cy * n)); }; template inline void operator *=(SIZE & s, Num n) { s = s * n; }; template inline CSize operator /(SIZE s, Num n) { return CSize((int)(s.cx / n), (int)(s.cy / n)); }; template inline void operator /=(SIZE & s, Num n) { s = s / n; }; #endif // !defined(_WTL_NO_SIZE_SCALAR) && (!defined(_WTL_NO_WTYPES) || defined(__ATLTYPES_H__)) /////////////////////////////////////////////////////////////////////////////// // CString - String class #ifndef _WTL_NO_CSTRING struct CStringData { long nRefs; // reference count int nDataLength; int nAllocLength; // TCHAR data[nAllocLength] TCHAR* data() { return (TCHAR*)(this + 1); } }; // Globals // For an empty string, m_pchData will point here // (note: avoids special case of checking for NULL m_pchData) // empty string data (and locked) _declspec(selectany) int rgInitData[] = { -1, 0, 0, 0 }; _declspec(selectany) CStringData* _atltmpDataNil = (CStringData*)&rgInitData; _declspec(selectany) LPCTSTR _atltmpPchNil = (LPCTSTR)(((BYTE*)&rgInitData) + sizeof(CStringData)); class CString { public: // Constructors CString() { Init(); } CString(const CString& stringSrc) { ATLASSERT(stringSrc.GetData()->nRefs != 0); if (stringSrc.GetData()->nRefs >= 0) { ATLASSERT(stringSrc.GetData() != _atltmpDataNil); m_pchData = stringSrc.m_pchData; InterlockedIncrement(&GetData()->nRefs); } else { Init(); *this = stringSrc.m_pchData; } } CString(TCHAR ch, int nRepeat = 1) { ATLASSERT(!_istlead(ch)); // can't create a lead byte string Init(); if (nRepeat >= 1) { if(AllocBuffer(nRepeat)) { #ifdef _UNICODE for (int i = 0; i < nRepeat; i++) m_pchData[i] = ch; #else memset(m_pchData, ch, nRepeat); #endif } } } CString(LPCTSTR lpsz) { Init(); if (lpsz != NULL && HIWORD(lpsz) == NULL) { UINT nID = LOWORD((DWORD_PTR)lpsz); if (!LoadString(nID)) ATLTRACE2(atlTraceUI, 0, _T("Warning: implicit LoadString(%u) in CString failed\n"), nID); } else { int nLen = SafeStrlen(lpsz); if (nLen != 0) { if(AllocBuffer(nLen)) SecureHelper::memcpy_x(m_pchData, (nLen + 1) * sizeof(TCHAR), lpsz, nLen * sizeof(TCHAR)); } } } #ifdef _UNICODE CString(LPCSTR lpsz) { Init(); #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800) int nSrcLen = (lpsz != NULL) ? ATL::lstrlenA(lpsz) : 0; #else int nSrcLen = (lpsz != NULL) ? lstrlenA(lpsz) : 0; #endif if (nSrcLen != 0) { if(AllocBuffer(nSrcLen)) { _mbstowcsz(m_pchData, lpsz, nSrcLen + 1); ReleaseBuffer(); } } } #else // !_UNICODE CString(LPCWSTR lpsz) { Init(); int nSrcLen = (lpsz != NULL) ? (int)wcslen(lpsz) : 0; if (nSrcLen != 0) { if(AllocBuffer(nSrcLen * 2)) { _wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1); ReleaseBuffer(); } } } #endif // !_UNICODE CString(LPCTSTR lpch, int nLength) { Init(); if (nLength != 0) { if(AllocBuffer(nLength)) SecureHelper::memcpy_x(m_pchData, (nLength + 1) * sizeof(TCHAR), lpch, nLength * sizeof(TCHAR)); } } #ifdef _UNICODE CString(LPCSTR lpsz, int nLength) { Init(); if (nLength != 0) { if(AllocBuffer(nLength)) { int n = ::MultiByteToWideChar(CP_ACP, 0, lpsz, nLength, m_pchData, nLength + 1); ReleaseBuffer((n >= 0) ? n : -1); } } } #else // !_UNICODE CString(LPCWSTR lpsz, int nLength) { Init(); if (nLength != 0) { if(((nLength * 2) > nLength) && AllocBuffer(nLength * 2)) { int n = ::WideCharToMultiByte(CP_ACP, 0, lpsz, nLength, m_pchData, (nLength * 2) + 1, NULL, NULL); ReleaseBuffer((n >= 0) ? n : -1); } } } #endif // !_UNICODE CString(const unsigned char* lpsz) { Init(); *this = (LPCSTR)lpsz; } // Attributes & Operations int GetLength() const // as an array of characters { return GetData()->nDataLength; } BOOL IsEmpty() const { return GetData()->nDataLength == 0; } void Empty() // free up the data { if (GetData()->nDataLength == 0) return; if (GetData()->nRefs >= 0) Release(); else *this = _T(""); ATLASSERT(GetData()->nDataLength == 0); ATLASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0); } TCHAR GetAt(int nIndex) const // 0 based { ATLASSERT(nIndex >= 0); ATLASSERT(nIndex < GetData()->nDataLength); return m_pchData[nIndex]; } TCHAR operator [](int nIndex) const // same as GetAt { // same as GetAt ATLASSERT(nIndex >= 0); ATLASSERT(nIndex < GetData()->nDataLength); return m_pchData[nIndex]; } void SetAt(int nIndex, TCHAR ch) { ATLASSERT(nIndex >= 0); ATLASSERT(nIndex < GetData()->nDataLength); CopyBeforeWrite(); m_pchData[nIndex] = ch; } operator LPCTSTR() const // as a C string { return m_pchData; } // overloaded assignment CString& operator =(const CString& stringSrc) { if (m_pchData != stringSrc.m_pchData) { if ((GetData()->nRefs < 0 && GetData() != _atltmpDataNil) || stringSrc.GetData()->nRefs < 0) { // actual copy necessary since one of the strings is locked AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData); } else { // can just copy references around Release(); ATLASSERT(stringSrc.GetData() != _atltmpDataNil); m_pchData = stringSrc.m_pchData; InterlockedIncrement(&GetData()->nRefs); } } return *this; } CString& operator =(TCHAR ch) { ATLASSERT(!_istlead(ch)); // can't set single lead byte AssignCopy(1, &ch); return *this; } #ifdef _UNICODE CString& operator =(char ch) { *this = (TCHAR)ch; return *this; } #endif CString& operator =(LPCTSTR lpsz) { ATLASSERT(lpsz == NULL || _IsValidString(lpsz)); AssignCopy(SafeStrlen(lpsz), lpsz); return *this; } #ifdef _UNICODE CString& operator =(LPCSTR lpsz) { #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800) int nSrcLen = (lpsz != NULL) ? ATL::lstrlenA(lpsz) : 0; #else int nSrcLen = (lpsz != NULL) ? lstrlenA(lpsz) : 0; #endif if(AllocBeforeWrite(nSrcLen)) { _mbstowcsz(m_pchData, lpsz, nSrcLen + 1); ReleaseBuffer(); } return *this; } #else // !_UNICODE CString& operator =(LPCWSTR lpsz) { int nSrcLen = (lpsz != NULL) ? (int)wcslen(lpsz) : 0; if(AllocBeforeWrite(nSrcLen * 2)) { _wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1); ReleaseBuffer(); } return *this; } #endif // !_UNICODE CString& operator =(const unsigned char* lpsz) { *this = (LPCSTR)lpsz; return *this; } // string concatenation CString& operator +=(const CString& string) { ConcatInPlace(string.GetData()->nDataLength, string.m_pchData); return *this; } CString& operator +=(TCHAR ch) { ConcatInPlace(1, &ch); return *this; } #ifdef _UNICODE CString& operator +=(char ch) { *this += (TCHAR)ch; return *this; } #endif CString& operator +=(LPCTSTR lpsz) { ATLASSERT(lpsz == NULL || _IsValidString(lpsz)); ConcatInPlace(SafeStrlen(lpsz), lpsz); return *this; } friend CString __stdcall operator +(const CString& string1, const CString& string2); friend CString __stdcall operator +(const CString& string, TCHAR ch); friend CString __stdcall operator +(TCHAR ch, const CString& string); #ifdef _UNICODE friend CString __stdcall operator +(const CString& string, char ch); friend CString __stdcall operator +(char ch, const CString& string); #endif friend CString __stdcall operator +(const CString& string, LPCTSTR lpsz); friend CString __stdcall operator +(LPCTSTR lpsz, const CString& string); // string comparison int Compare(LPCTSTR lpsz) const // straight character (MBCS/Unicode aware) { return _cstrcmp(m_pchData, lpsz); } int CompareNoCase(LPCTSTR lpsz) const // ignore case (MBCS/Unicode aware) { return _cstrcmpi(m_pchData, lpsz); } #ifndef _WIN32_WCE // CString::Collate is often slower than Compare but is MBSC/Unicode // aware as well as locale-sensitive with respect to sort order. int Collate(LPCTSTR lpsz) const // NLS aware { return _cstrcoll(m_pchData, lpsz); } int CollateNoCase(LPCTSTR lpsz) const // ignore case { return _cstrcolli(m_pchData, lpsz); } #endif // !_WIN32_WCE // simple sub-string extraction CString Mid(int nFirst, int nCount) const { // out-of-bounds requests return sensible things if (nFirst < 0) nFirst = 0; if (nCount < 0) nCount = 0; if (nFirst + nCount > GetData()->nDataLength) nCount = GetData()->nDataLength - nFirst; if (nFirst > GetData()->nDataLength) nCount = 0; CString dest; AllocCopy(dest, nCount, nFirst, 0); return dest; } CString Mid(int nFirst) const { return Mid(nFirst, GetData()->nDataLength - nFirst); } CString Left(int nCount) const { if (nCount < 0) nCount = 0; else if (nCount > GetData()->nDataLength) nCount = GetData()->nDataLength; CString dest; AllocCopy(dest, nCount, 0, 0); return dest; } CString Right(int nCount) const { if (nCount < 0) nCount = 0; else if (nCount > GetData()->nDataLength) nCount = GetData()->nDataLength; CString dest; AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0); return dest; } CString SpanIncluding(LPCTSTR lpszCharSet) const // strspn equivalent { ATLASSERT(_IsValidString(lpszCharSet)); return Left(_cstrspn(m_pchData, lpszCharSet)); } CString SpanExcluding(LPCTSTR lpszCharSet) const // strcspn equivalent { ATLASSERT(_IsValidString(lpszCharSet)); return Left(_cstrcspn(m_pchData, lpszCharSet)); } // upper/lower/reverse conversion void MakeUpper() { CopyBeforeWrite(); CharUpper(m_pchData); } void MakeLower() { CopyBeforeWrite(); CharLower(m_pchData); } void MakeReverse() { CopyBeforeWrite(); _cstrrev(m_pchData); } // trimming whitespace (either side) void TrimRight() { CopyBeforeWrite(); // find beginning of trailing spaces by starting at beginning (DBCS aware) LPTSTR lpsz = m_pchData; LPTSTR lpszLast = NULL; while (*lpsz != _T('\0')) { if (_cstrisspace(*lpsz)) { if (lpszLast == NULL) lpszLast = lpsz; } else { lpszLast = NULL; } lpsz = ::CharNext(lpsz); } if (lpszLast != NULL) { // truncate at trailing space start *lpszLast = _T('\0'); GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData); } } void TrimLeft() { CopyBeforeWrite(); // find first non-space character LPCTSTR lpsz = m_pchData; while (_cstrisspace(*lpsz)) lpsz = ::CharNext(lpsz); // fix up data and length int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData); SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR)); GetData()->nDataLength = nDataLength; } // remove continuous occurrences of chTarget starting from right void TrimRight(TCHAR chTarget) { // find beginning of trailing matches // by starting at beginning (DBCS aware) CopyBeforeWrite(); LPTSTR lpsz = m_pchData; LPTSTR lpszLast = NULL; while (*lpsz != _T('\0')) { if (*lpsz == chTarget) { if (lpszLast == NULL) lpszLast = lpsz; } else lpszLast = NULL; lpsz = ::CharNext(lpsz); } if (lpszLast != NULL) { // truncate at left-most matching character *lpszLast = _T('\0'); GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData); } } // remove continuous occcurrences of characters in passed string, starting from right void TrimRight(LPCTSTR lpszTargetList) { // find beginning of trailing matches by starting at beginning (DBCS aware) CopyBeforeWrite(); LPTSTR lpsz = m_pchData; LPTSTR lpszLast = NULL; while (*lpsz != _T('\0')) { TCHAR* pNext = ::CharNext(lpsz); if(pNext > lpsz + 1) { if (_cstrchr_db(lpszTargetList, *lpsz, *(lpsz + 1)) != NULL) { if (lpszLast == NULL) lpszLast = lpsz; } else { lpszLast = NULL; } } else { if (_cstrchr(lpszTargetList, *lpsz) != NULL) { if (lpszLast == NULL) lpszLast = lpsz; } else { lpszLast = NULL; } } lpsz = pNext; } if (lpszLast != NULL) { // truncate at left-most matching character *lpszLast = _T('\0'); GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData); } } // remove continuous occurrences of chTarget starting from left void TrimLeft(TCHAR chTarget) { // find first non-matching character CopyBeforeWrite(); LPCTSTR lpsz = m_pchData; while (chTarget == *lpsz) lpsz = ::CharNext(lpsz); if (lpsz != m_pchData) { // fix up data and length int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData); SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR)); GetData()->nDataLength = nDataLength; } } // remove continuous occcurrences of characters in passed string, starting from left void TrimLeft(LPCTSTR lpszTargets) { // if we're not trimming anything, we're not doing any work if (SafeStrlen(lpszTargets) == 0) return; CopyBeforeWrite(); LPCTSTR lpsz = m_pchData; while (*lpsz != _T('\0')) { TCHAR* pNext = ::CharNext(lpsz); if(pNext > lpsz + 1) { if (_cstrchr_db(lpszTargets, *lpsz, *(lpsz + 1)) == NULL) break; } else { if (_cstrchr(lpszTargets, *lpsz) == NULL) break; } lpsz = pNext; } if (lpsz != m_pchData) { // fix up data and length int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData); SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR)); GetData()->nDataLength = nDataLength; } } // advanced manipulation // replace occurrences of chOld with chNew int Replace(TCHAR chOld, TCHAR chNew) { int nCount = 0; // short-circuit the nop case if (chOld != chNew) { // otherwise modify each character that matches in the string CopyBeforeWrite(); LPTSTR psz = m_pchData; LPTSTR pszEnd = psz + GetData()->nDataLength; while (psz < pszEnd) { // replace instances of the specified character only if (*psz == chOld) { *psz = chNew; nCount++; } psz = ::CharNext(psz); } } return nCount; } // replace occurrences of substring lpszOld with lpszNew; // empty lpszNew removes instances of lpszOld int Replace(LPCTSTR lpszOld, LPCTSTR lpszNew) { // can't have empty or NULL lpszOld int nSourceLen = SafeStrlen(lpszOld); if (nSourceLen == 0) return 0; int nReplacementLen = SafeStrlen(lpszNew); // loop once to figure out the size of the result string int nCount = 0; LPTSTR lpszStart = m_pchData; LPTSTR lpszEnd = m_pchData + GetData()->nDataLength; LPTSTR lpszTarget = NULL; while (lpszStart < lpszEnd) { while ((lpszTarget = (TCHAR*)_cstrstr(lpszStart, lpszOld)) != NULL) { nCount++; lpszStart = lpszTarget + nSourceLen; } lpszStart += lstrlen(lpszStart) + 1; } // if any changes were made, make them if (nCount > 0) { CopyBeforeWrite(); // if the buffer is too small, just allocate a new buffer (slow but sure) int nOldLength = GetData()->nDataLength; int nNewLength = nOldLength + (nReplacementLen - nSourceLen) * nCount; if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1) { CStringData* pOldData = GetData(); LPTSTR pstr = m_pchData; if(!AllocBuffer(nNewLength)) return -1; SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstr, pOldData->nDataLength * sizeof(TCHAR)); CString::Release(pOldData); } // else, we just do it in-place lpszStart = m_pchData; lpszEnd = m_pchData + GetData()->nDataLength; // loop again to actually do the work while (lpszStart < lpszEnd) { while ((lpszTarget = (TCHAR*)_cstrstr(lpszStart, lpszOld)) != NULL) { int nBalance = nOldLength - ((int)(DWORD_PTR)(lpszTarget - m_pchData) + nSourceLen); int cchBuffLen = GetData()->nAllocLength - (int)(DWORD_PTR)(lpszTarget - m_pchData); SecureHelper::memmove_x(lpszTarget + nReplacementLen, (cchBuffLen - nReplacementLen + 1) * sizeof(TCHAR), lpszTarget + nSourceLen, nBalance * sizeof(TCHAR)); SecureHelper::memcpy_x(lpszTarget, (cchBuffLen + 1) * sizeof(TCHAR), lpszNew, nReplacementLen * sizeof(TCHAR)); lpszStart = lpszTarget + nReplacementLen; lpszStart[nBalance] = _T('\0'); nOldLength += (nReplacementLen - nSourceLen); } lpszStart += lstrlen(lpszStart) + 1; } ATLASSERT(m_pchData[nNewLength] == _T('\0')); GetData()->nDataLength = nNewLength; } return nCount; } // remove occurrences of chRemove int Remove(TCHAR chRemove) { CopyBeforeWrite(); LPTSTR pstrSource = m_pchData; LPTSTR pstrDest = m_pchData; LPTSTR pstrEnd = m_pchData + GetData()->nDataLength; while (pstrSource < pstrEnd) { if (*pstrSource != chRemove) { *pstrDest = *pstrSource; pstrDest = ::CharNext(pstrDest); } pstrSource = ::CharNext(pstrSource); } *pstrDest = _T('\0'); int nCount = (int)(DWORD_PTR)(pstrSource - pstrDest); GetData()->nDataLength -= nCount; return nCount; } // insert character at zero-based index; concatenates if index is past end of string int Insert(int nIndex, TCHAR ch) { CopyBeforeWrite(); if (nIndex < 0) nIndex = 0; int nNewLength = GetData()->nDataLength; if (nIndex > nNewLength) nIndex = nNewLength; nNewLength++; if (GetData()->nAllocLength < nNewLength) { CStringData* pOldData = GetData(); LPTSTR pstr = m_pchData; if(!AllocBuffer(nNewLength)) return -1; SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR)); CString::Release(pOldData); } // move existing bytes down SecureHelper::memmove_x(m_pchData + nIndex + 1, (GetData()->nAllocLength - nIndex) * sizeof(TCHAR), m_pchData + nIndex, (nNewLength - nIndex) * sizeof(TCHAR)); m_pchData[nIndex] = ch; GetData()->nDataLength = nNewLength; return nNewLength; } // insert substring at zero-based index; concatenates if index is past end of string int Insert(int nIndex, LPCTSTR pstr) { if (nIndex < 0) nIndex = 0; int nInsertLength = SafeStrlen(pstr); int nNewLength = GetData()->nDataLength; if (nInsertLength > 0) { CopyBeforeWrite(); if (nIndex > nNewLength) nIndex = nNewLength; nNewLength += nInsertLength; if (GetData()->nAllocLength < nNewLength) { CStringData* pOldData = GetData(); LPTSTR pstrTmp = m_pchData; if(!AllocBuffer(nNewLength)) return -1; SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstrTmp, (pOldData->nDataLength + 1) * sizeof(TCHAR)); CString::Release(pOldData); } // move existing bytes down SecureHelper::memmove_x(m_pchData + nIndex + nInsertLength, (GetData()->nAllocLength + 1 - nIndex - nInsertLength) * sizeof(TCHAR), m_pchData + nIndex, (nNewLength - nIndex - nInsertLength + 1) * sizeof(TCHAR)); SecureHelper::memcpy_x(m_pchData + nIndex, (GetData()->nAllocLength + 1 - nIndex) * sizeof(TCHAR), pstr, nInsertLength * sizeof(TCHAR)); GetData()->nDataLength = nNewLength; } return nNewLength; } // delete nCount characters starting at zero-based index int Delete(int nIndex, int nCount = 1) { if (nIndex < 0) nIndex = 0; int nLength = GetData()->nDataLength; if (nCount > 0 && nIndex < nLength) { if((nIndex + nCount) > nLength) nCount = nLength - nIndex; CopyBeforeWrite(); int nBytesToCopy = nLength - (nIndex + nCount) + 1; SecureHelper::memmove_x(m_pchData + nIndex, (GetData()->nAllocLength + 1 - nIndex) * sizeof(TCHAR), m_pchData + nIndex + nCount, nBytesToCopy * sizeof(TCHAR)); nLength -= nCount; GetData()->nDataLength = nLength; } return nLength; } // searching (return starting index, or -1 if not found) // look for a single character match int Find(TCHAR ch) const // like "C" strchr { return Find(ch, 0); } int ReverseFind(TCHAR ch) const { // find last single character LPCTSTR lpsz = _cstrrchr(m_pchData, (_TUCHAR)ch); // return -1 if not found, distance from beginning otherwise return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } int Find(TCHAR ch, int nStart) const // starting at index { int nLength = GetData()->nDataLength; if (nStart < 0 || nStart >= nLength) return -1; // find first single character LPCTSTR lpsz = _cstrchr(m_pchData + nStart, (_TUCHAR)ch); // return -1 if not found and index otherwise return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } int FindOneOf(LPCTSTR lpszCharSet) const { ATLASSERT(_IsValidString(lpszCharSet)); LPCTSTR lpsz = _cstrpbrk(m_pchData, lpszCharSet); return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } // look for a specific sub-string // find a sub-string (like strstr) int Find(LPCTSTR lpszSub) const // like "C" strstr { return Find(lpszSub, 0); } int Find(LPCTSTR lpszSub, int nStart) const // starting at index { ATLASSERT(_IsValidString(lpszSub)); int nLength = GetData()->nDataLength; if (nStart < 0 || nStart > nLength) return -1; // find first matching substring LPCTSTR lpsz = _cstrstr(m_pchData + nStart, lpszSub); // return -1 for not found, distance from beginning otherwise return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } // Concatentation for non strings CString& Append(int n) { const int cchBuff = 12; TCHAR szBuffer[cchBuff] = { 0 }; SecureHelper::wsprintf_x(szBuffer, cchBuff, _T("%d"), n); ConcatInPlace(SafeStrlen(szBuffer), szBuffer); return *this; } // simple formatting // formatting (using wsprintf style formatting) BOOL __cdecl Format(LPCTSTR lpszFormat, ...) { ATLASSERT(_IsValidString(lpszFormat)); va_list argList; va_start(argList, lpszFormat); BOOL bRet = FormatV(lpszFormat, argList); va_end(argList); return bRet; } BOOL __cdecl Format(UINT nFormatID, ...) { CString strFormat; BOOL bRet = strFormat.LoadString(nFormatID); ATLASSERT(bRet != 0); va_list argList; va_start(argList, nFormatID); bRet = FormatV(strFormat, argList); va_end(argList); return bRet; } BOOL FormatV(LPCTSTR lpszFormat, va_list argList) { ATLASSERT(_IsValidString(lpszFormat)); enum _FormatModifiers { FORCE_ANSI = 0x10000, FORCE_UNICODE = 0x20000, FORCE_INT64 = 0x40000 }; va_list argListSave = argList; // make a guess at the maximum length of the resulting string int nMaxLen = 0; for (LPCTSTR lpsz = lpszFormat; *lpsz != _T('\0'); lpsz = ::CharNext(lpsz)) { // handle '%' character, but watch out for '%%' if (*lpsz != _T('%') || *(lpsz = ::CharNext(lpsz)) == _T('%')) { nMaxLen += (int)(::CharNext(lpsz) - lpsz); continue; } int nItemLen = 0; // handle '%' character with format int nWidth = 0; for (; *lpsz != _T('\0'); lpsz = ::CharNext(lpsz)) { // check for valid flags if (*lpsz == _T('#')) nMaxLen += 2; // for '0x' else if (*lpsz == _T('*')) nWidth = va_arg(argList, int); else if (*lpsz == _T('-') || *lpsz == _T('+') || *lpsz == _T('0') || *lpsz == _T(' ')) ; else // hit non-flag character break; } // get width and skip it if (nWidth == 0) { // width indicated by nWidth = _cstrtoi(lpsz); for (; *lpsz != _T('\0') && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz)) ; } ATLASSERT(nWidth >= 0); int nPrecision = 0; if (*lpsz == _T('.')) { // skip past '.' separator (width.precision) lpsz = ::CharNext(lpsz); // get precision and skip it if (*lpsz == _T('*')) { nPrecision = va_arg(argList, int); lpsz = ::CharNext(lpsz); } else { nPrecision = _cstrtoi(lpsz); for (; *lpsz != _T('\0') && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz)) ; } ATLASSERT(nPrecision >= 0); } // should be on type modifier or specifier int nModifier = 0; if(lpsz[0] == _T('I')) { if((lpsz[1] == _T('6')) && (lpsz[2] == _T('4'))) { lpsz += 3; nModifier = FORCE_INT64; } else if((lpsz[1] == _T('3')) && (lpsz[2] == _T('2'))) { lpsz += 3; } else { lpsz++; if(sizeof(size_t) == 8) nModifier = FORCE_INT64; } } else { switch (*lpsz) { // modifiers that affect size case _T('h'): nModifier = FORCE_ANSI; lpsz = ::CharNext(lpsz); break; case _T('l'): nModifier = FORCE_UNICODE; lpsz = ::CharNext(lpsz); break; // modifiers that do not affect size case _T('F'): case _T('N'): case _T('L'): lpsz = ::CharNext(lpsz); break; } } // now should be on specifier switch (*lpsz | nModifier) { // single characters case _T('c'): case _T('C'): nItemLen = 2; va_arg(argList, TCHAR); break; case _T('c') | FORCE_ANSI: case _T('C') | FORCE_ANSI: nItemLen = 2; va_arg(argList, char); break; case _T('c') | FORCE_UNICODE: case _T('C') | FORCE_UNICODE: nItemLen = 2; va_arg(argList, WCHAR); break; // strings case _T('s'): { LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR); if (pstrNextArg == NULL) { nItemLen = 6; // "(null)" } else { nItemLen = lstrlen(pstrNextArg); nItemLen = __max(1, nItemLen); } break; } case _T('S'): { #ifndef _UNICODE LPWSTR pstrNextArg = va_arg(argList, LPWSTR); if (pstrNextArg == NULL) { nItemLen = 6; // "(null)" } else { nItemLen = (int)wcslen(pstrNextArg); nItemLen = __max(1, nItemLen); } #else // _UNICODE LPCSTR pstrNextArg = va_arg(argList, LPCSTR); if (pstrNextArg == NULL) { nItemLen = 6; // "(null)" } else { #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800) nItemLen = ATL::lstrlenA(pstrNextArg); #else nItemLen = lstrlenA(pstrNextArg); #endif nItemLen = __max(1, nItemLen); } #endif // _UNICODE break; } case _T('s') | FORCE_ANSI: case _T('S') | FORCE_ANSI: { LPCSTR pstrNextArg = va_arg(argList, LPCSTR); if (pstrNextArg == NULL) { nItemLen = 6; // "(null)" } else { #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800) nItemLen = ATL::lstrlenA(pstrNextArg); #else nItemLen = lstrlenA(pstrNextArg); #endif nItemLen = __max(1, nItemLen); } break; } case _T('s') | FORCE_UNICODE: case _T('S') | FORCE_UNICODE: { LPWSTR pstrNextArg = va_arg(argList, LPWSTR); if (pstrNextArg == NULL) { nItemLen = 6; // "(null)" } else { nItemLen = (int)wcslen(pstrNextArg); nItemLen = __max(1, nItemLen); } break; } } // adjust nItemLen for strings if (nItemLen != 0) { nItemLen = __max(nItemLen, nWidth); if (nPrecision != 0) nItemLen = __min(nItemLen, nPrecision); } else { switch (*lpsz) { // integers case _T('d'): case _T('i'): case _T('u'): case _T('x'): case _T('X'): case _T('o'): if (nModifier & FORCE_INT64) va_arg(argList, __int64); else va_arg(argList, int); nItemLen = 32; nItemLen = __max(nItemLen, nWidth + nPrecision); break; #ifndef _ATL_USE_CSTRING_FLOAT case _T('e'): case _T('E'): case _T('f'): case _T('g'): case _T('G'): ATLASSERT(!"Floating point (%%e, %%E, %%f, %%g, and %%G) is not supported by the WTL::CString class."); #ifndef _DEBUG ::OutputDebugString(_T("Floating point (%%e, %%f, %%g, and %%G) is not supported by the WTL::CString class.")); #ifndef _WIN32_WCE ::DebugBreak(); #else // CE specific DebugBreak(); #endif // _WIN32_WCE #endif // !_DEBUG break; #else // _ATL_USE_CSTRING_FLOAT case _T('e'): case _T('E'): case _T('g'): case _T('G'): va_arg(argList, double); nItemLen = 128; nItemLen = __max(nItemLen, nWidth + nPrecision); break; case _T('f'): { double f = va_arg(argList, double); // 312 == strlen("-1+(309 zeroes).") // 309 zeroes == max precision of a double // 6 == adjustment in case precision is not specified, // which means that the precision defaults to 6 int cchLen = __max(nWidth, 312 + nPrecision + 6); CTempBuffer buff; LPTSTR pszTemp = buff.Allocate(cchLen); if(pszTemp != NULL) { SecureHelper::sprintf_x(pszTemp, cchLen, _T("%*.*f"), nWidth, nPrecision + 6, f); nItemLen = (int)_tcslen(pszTemp); } else { nItemLen = cchLen; } } break; #endif // _ATL_USE_CSTRING_FLOAT case _T('p'): va_arg(argList, void*); nItemLen = 32; nItemLen = __max(nItemLen, nWidth + nPrecision); break; // no output case _T('n'): va_arg(argList, int*); break; default: ATLASSERT(FALSE); // unknown formatting option } } // adjust nMaxLen for output nItemLen nMaxLen += nItemLen; } if(GetBuffer(nMaxLen) == NULL) return FALSE; #ifndef _ATL_USE_CSTRING_FLOAT int nRet = SecureHelper::wvsprintf_x(m_pchData, GetAllocLength() + 1, lpszFormat, argListSave); #else // _ATL_USE_CSTRING_FLOAT int nRet = SecureHelper::vsprintf_x(m_pchData, GetAllocLength() + 1, lpszFormat, argListSave); #endif // _ATL_USE_CSTRING_FLOAT nRet; // ref ATLASSERT(nRet <= GetAllocLength()); ReleaseBuffer(); va_end(argListSave); return TRUE; } // formatting for localization (uses FormatMessage API) // formatting (using FormatMessage style formatting) BOOL __cdecl FormatMessage(LPCTSTR lpszFormat, ...) { // format message into temporary buffer lpszTemp va_list argList; va_start(argList, lpszFormat); LPTSTR lpszTemp = NULL; BOOL bRet = TRUE; if ((::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, lpszFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0) || (lpszTemp == NULL)) bRet = FALSE; // assign lpszTemp into the resulting string and free the temporary *this = lpszTemp; LocalFree(lpszTemp); va_end(argList); return bRet; } BOOL __cdecl FormatMessage(UINT nFormatID, ...) { // get format string from string table CString strFormat; BOOL bRetTmp = strFormat.LoadString(nFormatID); bRetTmp; // ref ATLASSERT(bRetTmp != 0); // format message into temporary buffer lpszTemp va_list argList; va_start(argList, nFormatID); LPTSTR lpszTemp = NULL; BOOL bRet = TRUE; if ((::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0) || (lpszTemp == NULL)) bRet = FALSE; // assign lpszTemp into the resulting string and free lpszTemp *this = lpszTemp; LocalFree(lpszTemp); va_end(argList); return bRet; } // Windows support BOOL LoadString(UINT nID) // load from string resource (255 chars max.) { #ifdef _UNICODE const int CHAR_FUDGE = 1; // one TCHAR unused is good enough #else const int CHAR_FUDGE = 2; // two BYTES unused for case of DBC last char #endif // try fixed buffer first (to avoid wasting space in the heap) TCHAR szTemp[256] = { 0 }; int nCount = sizeof(szTemp) / sizeof(szTemp[0]); int nLen = _LoadString(nID, szTemp, nCount); if (nCount - nLen > CHAR_FUDGE) { *this = szTemp; return (nLen > 0); } // try buffer size of 512, then larger size until entire string is retrieved int nSize = 256; do { nSize += 256; LPTSTR lpstr = GetBuffer(nSize - 1); if(lpstr == NULL) { nLen = 0; break; } nLen = _LoadString(nID, lpstr, nSize); } while (nSize - nLen <= CHAR_FUDGE); ReleaseBuffer(); return (nLen > 0); } #ifndef _UNICODE // ANSI <-> OEM support (convert string in place) void AnsiToOem() { CopyBeforeWrite(); ::AnsiToOem(m_pchData, m_pchData); } void OemToAnsi() { CopyBeforeWrite(); ::OemToAnsi(m_pchData, m_pchData); } #endif #ifndef _ATL_NO_COM // OLE BSTR support (use for OLE automation) BSTR AllocSysString() const { #if defined(_UNICODE) || defined(OLE2ANSI) BSTR bstr = ::SysAllocStringLen(m_pchData, GetData()->nDataLength); #else int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, NULL, NULL); BSTR bstr = ::SysAllocStringLen(NULL, nLen); if(bstr != NULL) MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, bstr, nLen); #endif return bstr; } BSTR SetSysString(BSTR* pbstr) const { #if defined(_UNICODE) || defined(OLE2ANSI) ::SysReAllocStringLen(pbstr, m_pchData, GetData()->nDataLength); #else int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, NULL, NULL); if(::SysReAllocStringLen(pbstr, NULL, nLen)) MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, *pbstr, nLen); #endif ATLASSERT(*pbstr != NULL); return *pbstr; } #endif // !_ATL_NO_COM // Access to string implementation buffer as "C" character array LPTSTR GetBuffer(int nMinBufLength) { ATLASSERT(nMinBufLength >= 0); if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength) { // we have to grow the buffer CStringData* pOldData = GetData(); int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it if (nMinBufLength < nOldLen) nMinBufLength = nOldLen; if(!AllocBuffer(nMinBufLength)) return NULL; SecureHelper::memcpy_x(m_pchData, (nMinBufLength + 1) * sizeof(TCHAR), pOldData->data(), (nOldLen + 1) * sizeof(TCHAR)); GetData()->nDataLength = nOldLen; CString::Release(pOldData); } ATLASSERT(GetData()->nRefs <= 1); // return a pointer to the character storage for this string ATLASSERT(m_pchData != NULL); return m_pchData; } void ReleaseBuffer(int nNewLength = -1) { CopyBeforeWrite(); // just in case GetBuffer was not called if (nNewLength == -1) nNewLength = lstrlen(m_pchData); // zero terminated ATLASSERT(nNewLength <= GetData()->nAllocLength); GetData()->nDataLength = nNewLength; m_pchData[nNewLength] = _T('\0'); } LPTSTR GetBufferSetLength(int nNewLength) { ATLASSERT(nNewLength >= 0); if(GetBuffer(nNewLength) == NULL) return NULL; GetData()->nDataLength = nNewLength; m_pchData[nNewLength] = _T('\0'); return m_pchData; } void FreeExtra() { ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength); if (GetData()->nDataLength != GetData()->nAllocLength) { CStringData* pOldData = GetData(); if(AllocBuffer(GetData()->nDataLength)) { SecureHelper::memcpy_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), pOldData->data(), pOldData->nDataLength * sizeof(TCHAR)); ATLASSERT(m_pchData[GetData()->nDataLength] == _T('\0')); CString::Release(pOldData); } } ATLASSERT(GetData() != NULL); } // Use LockBuffer/UnlockBuffer to turn refcounting off LPTSTR LockBuffer() { LPTSTR lpsz = GetBuffer(0); if(lpsz != NULL) GetData()->nRefs = -1; return lpsz; } void UnlockBuffer() { ATLASSERT(GetData()->nRefs == -1); if (GetData() != _atltmpDataNil) GetData()->nRefs = 1; } // Implementation public: ~CString() // free any attached data { if (GetData() != _atltmpDataNil) { if (InterlockedDecrement(&GetData()->nRefs) <= 0) delete[] (BYTE*)GetData(); } } int GetAllocLength() const { return GetData()->nAllocLength; } static BOOL __stdcall _IsValidString(LPCTSTR lpsz, int /*nLength*/ = -1) { return (lpsz != NULL) ? TRUE : FALSE; } protected: LPTSTR m_pchData; // pointer to ref counted string data // implementation helpers CStringData* GetData() const { ATLASSERT(m_pchData != NULL); return ((CStringData*)m_pchData) - 1; } void Init() { m_pchData = _GetEmptyString().m_pchData; } BOOL AllocCopy(CString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const { // will clone the data attached to this string // allocating 'nExtraLen' characters // Places results in uninitialized string 'dest' // Will copy the part or all of original data to start of new string BOOL bRet = FALSE; int nNewLen = nCopyLen + nExtraLen; if (nNewLen == 0) { dest.Init(); bRet = TRUE; } else if(nNewLen >= nCopyLen) { if(dest.AllocBuffer(nNewLen)) { SecureHelper::memcpy_x(dest.m_pchData, (nNewLen + 1) * sizeof(TCHAR), m_pchData + nCopyIndex, nCopyLen * sizeof(TCHAR)); bRet = TRUE; } } return bRet; } // always allocate one extra character for '\0' termination // assumes [optimistically] that data length will equal allocation length BOOL AllocBuffer(int nLen) { ATLASSERT(nLen >= 0); ATLASSERT(nLen <= INT_MAX - 1); // max size (enough room for 1 extra) if (nLen == 0) { Init(); } else { CStringData* pData = NULL; ATLTRY(pData = (CStringData*)new BYTE[sizeof(CStringData) + (nLen + 1) * sizeof(TCHAR)]); if(pData == NULL) return FALSE; pData->nRefs = 1; pData->data()[nLen] = _T('\0'); pData->nDataLength = nLen; pData->nAllocLength = nLen; m_pchData = pData->data(); } return TRUE; } // Assignment operators // All assign a new value to the string // (a) first see if the buffer is big enough // (b) if enough room, copy on top of old buffer, set size and type // (c) otherwise free old string data, and create a new one // // All routines return the new string (but as a 'const CString&' so that // assigning it again will cause a copy, eg: s1 = s2 = "hi there". // void AssignCopy(int nSrcLen, LPCTSTR lpszSrcData) { if(AllocBeforeWrite(nSrcLen)) { SecureHelper::memcpy_x(m_pchData, (nSrcLen + 1) * sizeof(TCHAR), lpszSrcData, nSrcLen * sizeof(TCHAR)); GetData()->nDataLength = nSrcLen; m_pchData[nSrcLen] = _T('\0'); } } // Concatenation // NOTE: "operator +" is done as friend functions for simplicity // There are three variants: // CString + CString // and for ? = TCHAR, LPCTSTR // CString + ? // ? + CString BOOL ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data) { // -- master concatenation routine // Concatenate two sources // -- assume that 'this' is a new CString object BOOL bRet = TRUE; int nNewLen = nSrc1Len + nSrc2Len; if(nNewLen < nSrc1Len || nNewLen < nSrc2Len) { bRet = FALSE; } else if(nNewLen != 0) { bRet = AllocBuffer(nNewLen); if (bRet) { SecureHelper::memcpy_x(m_pchData, (nNewLen + 1) * sizeof(TCHAR), lpszSrc1Data, nSrc1Len * sizeof(TCHAR)); SecureHelper::memcpy_x(m_pchData + nSrc1Len, (nNewLen + 1 - nSrc1Len) * sizeof(TCHAR), lpszSrc2Data, nSrc2Len * sizeof(TCHAR)); } } return bRet; } void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData) { // -- the main routine for += operators // concatenating an empty string is a no-op! if (nSrcLen == 0) return; // if the buffer is too small, or we have a width mis-match, just // allocate a new buffer (slow but sure) if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength) { // we have to grow the buffer, use the ConcatCopy routine CStringData* pOldData = GetData(); if (ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData)) { ATLASSERT(pOldData != NULL); CString::Release(pOldData); } } else { // fast concatenation when buffer big enough SecureHelper::memcpy_x(m_pchData + GetData()->nDataLength, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpszSrcData, nSrcLen * sizeof(TCHAR)); GetData()->nDataLength += nSrcLen; ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength); m_pchData[GetData()->nDataLength] = _T('\0'); } } void CopyBeforeWrite() { if (GetData()->nRefs > 1) { CStringData* pData = GetData(); Release(); if(AllocBuffer(pData->nDataLength)) SecureHelper::memcpy_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), pData->data(), (pData->nDataLength + 1) * sizeof(TCHAR)); } ATLASSERT(GetData()->nRefs <= 1); } BOOL AllocBeforeWrite(int nLen) { BOOL bRet = TRUE; if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength) { Release(); bRet = AllocBuffer(nLen); } ATLASSERT(GetData()->nRefs <= 1); return bRet; } void Release() { if (GetData() != _atltmpDataNil) { ATLASSERT(GetData()->nRefs != 0); if (InterlockedDecrement(&GetData()->nRefs) <= 0) delete[] (BYTE*)GetData(); Init(); } } static void PASCAL Release(CStringData* pData) { if (pData != _atltmpDataNil) { ATLASSERT(pData->nRefs != 0); if (InterlockedDecrement(&pData->nRefs) <= 0) delete[] (BYTE*)pData; } } static int PASCAL SafeStrlen(LPCTSTR lpsz) { return (lpsz == NULL) ? 0 : lstrlen(lpsz); } static int __stdcall _LoadString(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf) { #ifdef _DEBUG // LoadString without annoying warning from the Debug kernel if the // segment containing the string is not present if (::FindResource(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE((nID >> 4) + 1), RT_STRING) == NULL) { lpszBuf[0] = _T('\0'); return 0; // not found } #endif // _DEBUG int nLen = ::LoadString(ModuleHelper::GetResourceInstance(), nID, lpszBuf, nMaxBuf); if (nLen == 0) lpszBuf[0] = _T('\0'); return nLen; } static const CString& __stdcall _GetEmptyString() { return *(CString*)&_atltmpPchNil; } // CString conversion helpers static int __cdecl _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count) { if (count == 0 && mbstr != NULL) return 0; int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1, mbstr, (int)count, NULL, NULL); ATLASSERT(mbstr == NULL || result <= (int)count); if ((mbstr != NULL) && (result > 0)) mbstr[result - 1] = 0; return result; } static int __cdecl _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count) { if (count == 0 && wcstr != NULL) return 0; int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1, wcstr, (int)count); ATLASSERT(wcstr == NULL || result <= (int)count); if ((wcstr != NULL) && (result > 0)) wcstr[result - 1] = 0; return result; } // Helpers to avoid CRT startup code #ifdef _ATL_MIN_CRT static const TCHAR* _cstrchr(const TCHAR* p, TCHAR ch) { // strchr for '\0' should succeed while (*p != 0) { if (*p == ch) break; p = ::CharNext(p); } return (*p == ch) ? p : NULL; } static TCHAR* _cstrrev(TCHAR* pStr) { // optimize NULL, zero-length, and single-char case if ((pStr == NULL) || (pStr[0] == _T('\0')) || (pStr[1] == _T('\0'))) return pStr; TCHAR* p = pStr; while (*p != 0) { TCHAR* pNext = ::CharNext(p); if(pNext > p + 1) { char p1 = *(char*)p; *(char*)p = *(char*)(p + 1); *(char*)(p + 1) = p1; } p = pNext; } p--; TCHAR* q = pStr; while (q < p) { TCHAR t = *q; *q = *p; *p = t; q++; p--; } return pStr; } static const TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet) { int nLen = lstrlen(pCharSet); if (nLen == 0) return (TCHAR*)pStr; const TCHAR* pRet = NULL; const TCHAR* pCur = pStr; while((pCur = _cstrchr(pCur, *pCharSet)) != NULL) { if(memcmp(pCur, pCharSet, nLen * sizeof(TCHAR)) == 0) { pRet = pCur; break; } pCur = ::CharNext(pCur); } return pRet; } static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet) { int nRet = 0; const TCHAR* p = pStr; while (*p != 0) { const TCHAR* pNext = ::CharNext(p); if(pNext > p + 1) { if(_cstrchr_db(pCharSet, *p, *(p + 1)) == NULL) break; nRet += 2; } else { if(_cstrchr(pCharSet, *p) == NULL) break; nRet++; } p = pNext; } return nRet; } static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet) { int nRet = 0; TCHAR* p = (TCHAR*)pStr; while (*p != 0) { TCHAR* pNext = ::CharNext(p); if(pNext > p + 1) { if(_cstrchr_db(pCharSet, *p, *(p + 1)) != NULL) break; nRet += 2; } else { if(_cstrchr(pCharSet, *p) != NULL) break; nRet++; } p = pNext; } return nRet; } static const TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet) { int n = _cstrcspn(p, lpszCharSet); return (p[n] != 0) ? &p[n] : NULL; } static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther) { return lstrcmp(pstrOne, pstrOther); } static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther) { return lstrcmpi(pstrOne, pstrOther); } static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther) { int nRet = CompareString(GetThreadLocale(), 0, pstrOne, -1, pstrOther, -1); ATLASSERT(nRet != 0); return nRet - 2; // convert to strcmp convention } static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther) { int nRet = CompareString(GetThreadLocale(), NORM_IGNORECASE, pstrOne, -1, pstrOther, -1); ATLASSERT(nRet != 0); return nRet - 2; // convert to strcmp convention } #else // !_ATL_MIN_CRT static const TCHAR* _cstrchr(const TCHAR* p, TCHAR ch) { return _tcschr(p, ch); } static TCHAR* _cstrrev(TCHAR* pStr) { return _tcsrev(pStr); } static const TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet) { return _tcsstr(pStr, pCharSet); } static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet) { return (int)_tcsspn(pStr, pCharSet); } static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet) { return (int)_tcscspn(pStr, pCharSet); } static const TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet) { return _tcspbrk(p, lpszCharSet); } static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther) { return _tcscmp(pstrOne, pstrOther); } static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther) { return _tcsicmp(pstrOne, pstrOther); } #ifndef _WIN32_WCE static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther) { return _tcscoll(pstrOne, pstrOther); } static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther) { return _tcsicoll(pstrOne, pstrOther); } #endif // !_WIN32_WCE #endif // !_ATL_MIN_CRT static const TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch) { return MinCrtHelper::_strrchr(p, ch); } static int _cstrisdigit(TCHAR ch) { return MinCrtHelper::_isdigit(ch); } static int _cstrisspace(TCHAR ch) { return MinCrtHelper::_isspace(ch); } static int _cstrtoi(const TCHAR* nptr) { return MinCrtHelper::_atoi(nptr); } static const TCHAR* _cstrchr_db(const TCHAR* p, TCHAR ch1, TCHAR ch2) { const TCHAR* lpsz = NULL; while (*p != 0) { if (*p == ch1 && *(p + 1) == ch2) { lpsz = p; break; } p = ::CharNext(p); } return lpsz; } }; // Compare helpers inline bool __stdcall operator ==(const CString& s1, const CString& s2) { return s1.Compare(s2) == 0; } inline bool __stdcall operator ==(const CString& s1, LPCTSTR s2) { return s1.Compare(s2) == 0; } inline bool __stdcall operator ==(LPCTSTR s1, const CString& s2) { return s2.Compare(s1) == 0; } inline bool __stdcall operator !=(const CString& s1, const CString& s2) { return s1.Compare(s2) != 0; } inline bool __stdcall operator !=(const CString& s1, LPCTSTR s2) { return s1.Compare(s2) != 0; } inline bool __stdcall operator !=(LPCTSTR s1, const CString& s2) { return s2.Compare(s1) != 0; } inline bool __stdcall operator <(const CString& s1, const CString& s2) { return s1.Compare(s2) < 0; } inline bool __stdcall operator <(const CString& s1, LPCTSTR s2) { return s1.Compare(s2) < 0; } inline bool __stdcall operator <(LPCTSTR s1, const CString& s2) { return s2.Compare(s1) > 0; } inline bool __stdcall operator >(const CString& s1, const CString& s2) { return s1.Compare(s2) > 0; } inline bool __stdcall operator >(const CString& s1, LPCTSTR s2) { return s1.Compare(s2) > 0; } inline bool __stdcall operator >(LPCTSTR s1, const CString& s2) { return s2.Compare(s1) < 0; } inline bool __stdcall operator <=(const CString& s1, const CString& s2) { return s1.Compare(s2) <= 0; } inline bool __stdcall operator <=(const CString& s1, LPCTSTR s2) { return s1.Compare(s2) <= 0; } inline bool __stdcall operator <=(LPCTSTR s1, const CString& s2) { return s2.Compare(s1) >= 0; } inline bool __stdcall operator >=(const CString& s1, const CString& s2) { return s1.Compare(s2) >= 0; } inline bool __stdcall operator >=(const CString& s1, LPCTSTR s2) { return s1.Compare(s2) >= 0; } inline bool __stdcall operator >=(LPCTSTR s1, const CString& s2) { return s2.Compare(s1) <= 0; } // CString "operator +" functions inline CString __stdcall operator +(const CString& string1, const CString& string2) { CString s; s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, string2.GetData()->nDataLength, string2.m_pchData); return s; } inline CString __stdcall operator +(const CString& string, TCHAR ch) { CString s; s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, 1, &ch); return s; } inline CString __stdcall operator +(TCHAR ch, const CString& string) { CString s; s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData); return s; } #ifdef _UNICODE inline CString __stdcall operator +(const CString& string, char ch) { return string + (TCHAR)ch; } inline CString __stdcall operator +(char ch, const CString& string) { return (TCHAR)ch + string; } #endif // _UNICODE inline CString __stdcall operator +(const CString& string, LPCTSTR lpsz) { ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz)); CString s; s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, CString::SafeStrlen(lpsz), lpsz); return s; } inline CString __stdcall operator +(LPCTSTR lpsz, const CString& string) { ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz)); CString s; s.ConcatCopy(CString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength, string.m_pchData); return s; } #endif // !_WTL_NO_CSTRING /////////////////////////////////////////////////////////////////////////////// // CRecentDocumentList - MRU List Support #ifndef _WIN32_WCE #ifndef _WTL_MRUEMPTY_TEXT #define _WTL_MRUEMPTY_TEXT _T("(empty)") #endif // forward declaration inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen); template class CRecentDocumentListBase { public: // Declarations struct _DocEntry { TCHAR szDocName[t_cchItemLen]; bool operator ==(const _DocEntry& de) const { return (lstrcmpi(szDocName, de.szDocName) == 0); } }; enum { m_nMaxEntries_Min = 2, m_nMaxEntries_Max = t_nLastID - t_nFirstID + 1, m_cchMaxItemLen_Min = 6, m_cchMaxItemLen_Max = t_cchItemLen, m_cchItemNameLen = 11 }; // Data members ATL::CSimpleArray<_DocEntry> m_arrDocs; int m_nMaxEntries; // default is 4 HMENU m_hMenu; TCHAR m_szNoEntries[t_cchItemLen]; int m_cchMaxItemLen; // Constructor CRecentDocumentListBase() : m_hMenu(NULL), m_nMaxEntries(4), m_cchMaxItemLen(-1) { // These ASSERTs verify values of the template arguments ATLASSERT(t_cchItemLen > m_cchMaxItemLen_Min); ATLASSERT(m_nMaxEntries_Max > m_nMaxEntries_Min); } // Attributes HMENU GetMenuHandle() const { return m_hMenu; } void SetMenuHandle(HMENU hMenu) { ATLASSERT(hMenu == NULL || ::IsMenu(hMenu)); m_hMenu = hMenu; if(m_hMenu == NULL || (::GetMenuString(m_hMenu, t_nFirstID, m_szNoEntries, t_cchItemLen, MF_BYCOMMAND) == 0)) { T* pT = static_cast(this); pT; // avoid level 4 warning SecureHelper::strncpy_x(m_szNoEntries, _countof(m_szNoEntries), pT->GetMRUEmptyText(), _TRUNCATE); } } int GetMaxEntries() const { return m_nMaxEntries; } void SetMaxEntries(int nMaxEntries) { ATLASSERT(nMaxEntries >= m_nMaxEntries_Min && nMaxEntries <= m_nMaxEntries_Max); if(nMaxEntries < m_nMaxEntries_Min) nMaxEntries = m_nMaxEntries_Min; else if(nMaxEntries > m_nMaxEntries_Max) nMaxEntries = m_nMaxEntries_Max; m_nMaxEntries = nMaxEntries; } int GetMaxItemLength() const { return m_cchMaxItemLen; } void SetMaxItemLength(int cchMaxLen) { ATLASSERT((cchMaxLen >= m_cchMaxItemLen_Min && cchMaxLen <= m_cchMaxItemLen_Max) || cchMaxLen == -1); if(cchMaxLen != -1) { if(cchMaxLen < m_cchMaxItemLen_Min) cchMaxLen = m_cchMaxItemLen_Min; else if(cchMaxLen > m_cchMaxItemLen_Max) cchMaxLen = m_cchMaxItemLen_Max; } m_cchMaxItemLen = cchMaxLen; T* pT = static_cast(this); pT->UpdateMenu(); } // Operations BOOL AddToList(LPCTSTR lpstrDocName) { _DocEntry de; errno_t nRet = SecureHelper::strncpy_x(de.szDocName, _countof(de.szDocName), lpstrDocName, _TRUNCATE); if(nRet != 0 && nRet != STRUNCATE) return FALSE; for(int i = 0; i < m_arrDocs.GetSize(); i++) { if(lstrcmpi(m_arrDocs[i].szDocName, lpstrDocName) == 0) { m_arrDocs.RemoveAt(i); break; } } if(m_arrDocs.GetSize() == m_nMaxEntries) m_arrDocs.RemoveAt(0); BOOL bRet = m_arrDocs.Add(de); if(bRet) { T* pT = static_cast(this); bRet = pT->UpdateMenu(); } return bRet; } // This function is deprecated because it is not safe. // Use the version below that accepts the buffer length. #if (_MSC_VER >= 1300) __declspec(deprecated) #endif BOOL GetFromList(int /*nItemID*/, LPTSTR /*lpstrDocName*/) { ATLASSERT(FALSE); return FALSE; } BOOL GetFromList(int nItemID, LPTSTR lpstrDocName, int cchLength) { int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1; if(nIndex < 0 || nIndex >= m_arrDocs.GetSize()) return FALSE; if(lstrlen(m_arrDocs[nIndex].szDocName) >= cchLength) return FALSE; SecureHelper::strcpy_x(lpstrDocName, cchLength, m_arrDocs[nIndex].szDocName); return TRUE; } #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) BOOL GetFromList(int nItemID, _CSTRING_NS::CString& strDocName) { int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1; if(nIndex < 0 || nIndex >= m_arrDocs.GetSize()) return FALSE; strDocName = m_arrDocs[nIndex].szDocName; return TRUE; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) BOOL RemoveFromList(int nItemID) { int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1; BOOL bRet = m_arrDocs.RemoveAt(nIndex); if(bRet) { T* pT = static_cast(this); bRet = pT->UpdateMenu(); } return bRet; } BOOL MoveToTop(int nItemID) { int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1; if(nIndex < 0 || nIndex >= m_arrDocs.GetSize()) return FALSE; _DocEntry de; de = m_arrDocs[nIndex]; m_arrDocs.RemoveAt(nIndex); BOOL bRet = m_arrDocs.Add(de); if(bRet) { T* pT = static_cast(this); bRet = pT->UpdateMenu(); } return bRet; } BOOL ReadFromRegistry(LPCTSTR lpstrRegKey) { T* pT = static_cast(this); CRegKeyEx rkParent; CRegKeyEx rk; LONG lRet = rkParent.Open(HKEY_CURRENT_USER, lpstrRegKey); if(lRet != ERROR_SUCCESS) return FALSE; lRet = rk.Open(rkParent, pT->GetRegKeyName()); if(lRet != ERROR_SUCCESS) return FALSE; DWORD dwRet = 0; lRet = rk.QueryDWORDValue(pT->GetRegCountName(), dwRet); if(lRet != ERROR_SUCCESS) return FALSE; SetMaxEntries(dwRet); m_arrDocs.RemoveAll(); TCHAR szRetString[t_cchItemLen] = { 0 }; _DocEntry de; for(int nItem = m_nMaxEntries; nItem > 0; nItem--) { TCHAR szBuff[m_cchItemNameLen] = { 0 }; SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem); ULONG ulCount = t_cchItemLen; lRet = rk.QueryStringValue(szBuff, szRetString, &ulCount); if(lRet == ERROR_SUCCESS) { SecureHelper::strcpy_x(de.szDocName, _countof(de.szDocName), szRetString); m_arrDocs.Add(de); } } rk.Close(); rkParent.Close(); return pT->UpdateMenu(); } BOOL WriteToRegistry(LPCTSTR lpstrRegKey) { T* pT = static_cast(this); pT; // avoid level 4 warning CRegKeyEx rkParent; CRegKeyEx rk; LONG lRet = rkParent.Create(HKEY_CURRENT_USER, lpstrRegKey); if(lRet != ERROR_SUCCESS) return FALSE; lRet = rk.Create(rkParent, pT->GetRegKeyName()); if(lRet != ERROR_SUCCESS) return FALSE; lRet = rk.SetDWORDValue(pT->GetRegCountName(), m_nMaxEntries); ATLASSERT(lRet == ERROR_SUCCESS); // set new values int nItem; for(nItem = m_arrDocs.GetSize(); nItem > 0; nItem--) { TCHAR szBuff[m_cchItemNameLen] = { 0 }; SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem); TCHAR szDocName[t_cchItemLen] = { 0 }; GetFromList(t_nFirstID + nItem - 1, szDocName, t_cchItemLen); lRet = rk.SetStringValue(szBuff, szDocName); ATLASSERT(lRet == ERROR_SUCCESS); } // delete unused keys for(nItem = m_arrDocs.GetSize() + 1; nItem <= m_nMaxEntries_Max; nItem++) { TCHAR szBuff[m_cchItemNameLen] = { 0 }; SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem); rk.DeleteValue(szBuff); } rk.Close(); rkParent.Close(); return TRUE; } // Implementation BOOL UpdateMenu() { if(m_hMenu == NULL) return FALSE; ATLASSERT(::IsMenu(m_hMenu)); int nItems = ::GetMenuItemCount(m_hMenu); int nInsertPoint = 0; for(int i = 0; i < nItems; i++) { CMenuItemInfo mi; mi.fMask = MIIM_ID; ::GetMenuItemInfo(m_hMenu, i, TRUE, &mi); if (mi.wID == t_nFirstID) { nInsertPoint = i; break; } } ATLASSERT(nInsertPoint < nItems && "You need a menu item with an ID = t_nFirstID"); for(int j = t_nFirstID; j < (t_nFirstID + m_nMaxEntries); j++) { // keep the first one as an insertion point if (j != t_nFirstID) ::DeleteMenu(m_hMenu, j, MF_BYCOMMAND); } TCHAR szItemText[t_cchItemLen + 6] = { 0 }; // add space for &, 2 digits, and a space int nSize = m_arrDocs.GetSize(); int nItem = 0; if(nSize > 0) { for(nItem = 0; nItem < nSize; nItem++) { if(m_cchMaxItemLen == -1) { SecureHelper::wsprintf_x(szItemText, t_cchItemLen + 6, _T("&%i %s"), nItem + 1, m_arrDocs[nSize - 1 - nItem].szDocName); } else { TCHAR szBuff[t_cchItemLen] = { 0 }; T* pT = static_cast(this); pT; // avoid level 4 warning bool bRet = pT->CompactDocumentName(szBuff, m_arrDocs[nSize - 1 - nItem].szDocName, m_cchMaxItemLen); bRet; // avoid level 4 warning ATLASSERT(bRet); SecureHelper::wsprintf_x(szItemText, t_cchItemLen + 6, _T("&%i %s"), nItem + 1, szBuff); } ::InsertMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION | MF_STRING, t_nFirstID + nItem, szItemText); } } else // empty { ::InsertMenu(m_hMenu, nInsertPoint, MF_BYPOSITION | MF_STRING, t_nFirstID, m_szNoEntries); ::EnableMenuItem(m_hMenu, t_nFirstID, MF_GRAYED); nItem++; } ::DeleteMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION); return TRUE; } // Overrideables // override to provide a different method of compacting document names static bool CompactDocumentName(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen) { return AtlCompactPath(lpstrOut, lpstrIn, cchLen); } static LPCTSTR GetRegKeyName() { return _T("Recent Document List"); } static LPCTSTR GetRegCountName() { return _T("DocumentCount"); } static LPCTSTR GetRegItemName() { // Note: This string is a format string used with wsprintf(). // Resulting formatted string must be m_cchItemNameLen or less // characters long, including the terminating null character. return _T("Document%i"); } static LPCTSTR GetMRUEmptyText() { return _WTL_MRUEMPTY_TEXT; } }; class CRecentDocumentList : public CRecentDocumentListBase { public: // nothing here }; #endif // _WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CFindFile - file search helper class class CFindFile { public: // Data members WIN32_FIND_DATA m_fd; TCHAR m_lpszRoot[MAX_PATH]; TCHAR m_chDirSeparator; HANDLE m_hFind; BOOL m_bFound; // Constructor/destructor CFindFile() : m_hFind(NULL), m_chDirSeparator(_T('\\')), m_bFound(FALSE) { } ~CFindFile() { Close(); } // Attributes ULONGLONG GetFileSize() const { ATLASSERT(m_hFind != NULL); ULARGE_INTEGER nFileSize = { 0 }; if(m_bFound) { nFileSize.LowPart = m_fd.nFileSizeLow; nFileSize.HighPart = m_fd.nFileSizeHigh; } else { nFileSize.QuadPart = 0; } return nFileSize.QuadPart; } BOOL GetFileName(LPTSTR lpstrFileName, int cchLength) const { ATLASSERT(m_hFind != NULL); if(lstrlen(m_fd.cFileName) >= cchLength) return FALSE; if(m_bFound) SecureHelper::strcpy_x(lpstrFileName, cchLength, m_fd.cFileName); return m_bFound; } BOOL GetFilePath(LPTSTR lpstrFilePath, int cchLength) const { ATLASSERT(m_hFind != NULL); int nLen = lstrlen(m_lpszRoot); #ifndef _WIN32_WCE ATLASSERT(nLen > 0); if(nLen == 0) return FALSE; bool bAddSep = (m_lpszRoot[nLen - 1] != _T('\\') && m_lpszRoot[nLen - 1] !=_T('/')); #else // CE specific // allow diskless devices (nLen == 0) bool bAddSep = ((nLen == 0) || (m_lpszRoot[nLen - 1] != _T('\\') && m_lpszRoot[nLen - 1] !=_T('/'))); #endif // _WIN32_WCE if((lstrlen(m_lpszRoot) + (bAddSep ? 1 : 0)) >= cchLength) return FALSE; SecureHelper::strcpy_x(lpstrFilePath, cchLength, m_lpszRoot); if(bAddSep) { TCHAR szSeparator[2] = { m_chDirSeparator, 0 }; SecureHelper::strcat_x(lpstrFilePath, cchLength, szSeparator); } SecureHelper::strcat_x(lpstrFilePath, cchLength, m_fd.cFileName); return TRUE; } #ifndef _WIN32_WCE BOOL GetFileTitle(LPTSTR lpstrFileTitle, int cchLength) const { ATLASSERT(m_hFind != NULL); TCHAR szBuff[MAX_PATH] = { 0 }; if(!GetFileName(szBuff, MAX_PATH)) return FALSE; if(lstrlen(szBuff) >= cchLength || cchLength < 1) return FALSE; // find the last dot LPTSTR pstrDot = MinCrtHelper::_strrchr(szBuff, _T('.')); if(pstrDot != NULL) *pstrDot = 0; SecureHelper::strcpy_x(lpstrFileTitle, cchLength, szBuff); return TRUE; } #endif // !_WIN32_WCE BOOL GetFileURL(LPTSTR lpstrFileURL, int cchLength) const { ATLASSERT(m_hFind != NULL); TCHAR szBuff[MAX_PATH] = { 0 }; if(!GetFilePath(szBuff, MAX_PATH)) return FALSE; LPCTSTR lpstrFileURLPrefix = _T("file://"); if(lstrlen(szBuff) + lstrlen(lpstrFileURLPrefix) >= cchLength) return FALSE; SecureHelper::strcpy_x(lpstrFileURL, cchLength, lpstrFileURLPrefix); SecureHelper::strcat_x(lpstrFileURL, cchLength, szBuff); return TRUE; } BOOL GetRoot(LPTSTR lpstrRoot, int cchLength) const { ATLASSERT(m_hFind != NULL); if(lstrlen(m_lpszRoot) >= cchLength) return FALSE; SecureHelper::strcpy_x(lpstrRoot, cchLength, m_lpszRoot); return TRUE; } #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) _CSTRING_NS::CString GetFileName() const { ATLASSERT(m_hFind != NULL); _CSTRING_NS::CString ret; if(m_bFound) ret = m_fd.cFileName; return ret; } _CSTRING_NS::CString GetFilePath() const { ATLASSERT(m_hFind != NULL); _CSTRING_NS::CString strResult = m_lpszRoot; int nLen = strResult.GetLength(); #ifndef _WIN32_WCE ATLASSERT(nLen > 0); if(nLen == 0) return strResult; if((strResult[nLen - 1] != _T('\\')) && (strResult[nLen - 1] != _T('/'))) #else // CE specific // allow diskless devices (nLen == 0) if((nLen == 0) || ((strResult[nLen - 1] != _T('\\')) && (strResult[nLen - 1] != _T('/')))) #endif // _WIN32_WCE strResult += m_chDirSeparator; strResult += GetFileName(); return strResult; } #ifndef _WIN32_WCE _CSTRING_NS::CString GetFileTitle() const { ATLASSERT(m_hFind != NULL); _CSTRING_NS::CString strResult; GetFileTitle(strResult.GetBuffer(MAX_PATH), MAX_PATH); strResult.ReleaseBuffer(); return strResult; } #endif // !_WIN32_WCE _CSTRING_NS::CString GetFileURL() const { ATLASSERT(m_hFind != NULL); _CSTRING_NS::CString strResult("file://"); strResult += GetFilePath(); return strResult; } _CSTRING_NS::CString GetRoot() const { ATLASSERT(m_hFind != NULL); _CSTRING_NS::CString str = m_lpszRoot; return str; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) BOOL GetLastWriteTime(FILETIME* pTimeStamp) const { ATLASSERT(m_hFind != NULL); ATLASSERT(pTimeStamp != NULL); if(m_bFound && pTimeStamp != NULL) { *pTimeStamp = m_fd.ftLastWriteTime; return TRUE; } return FALSE; } BOOL GetLastAccessTime(FILETIME* pTimeStamp) const { ATLASSERT(m_hFind != NULL); ATLASSERT(pTimeStamp != NULL); if(m_bFound && pTimeStamp != NULL) { *pTimeStamp = m_fd.ftLastAccessTime; return TRUE; } return FALSE; } BOOL GetCreationTime(FILETIME* pTimeStamp) const { ATLASSERT(m_hFind != NULL); if(m_bFound && pTimeStamp != NULL) { *pTimeStamp = m_fd.ftCreationTime; return TRUE; } return FALSE; } BOOL MatchesMask(DWORD dwMask) const { ATLASSERT(m_hFind != NULL); if(m_bFound) return ((m_fd.dwFileAttributes & dwMask) != 0); return FALSE; } BOOL IsDots() const { ATLASSERT(m_hFind != NULL); // return TRUE if the file name is "." or ".." and // the file is a directory BOOL bResult = FALSE; if(m_bFound && IsDirectory()) { if(m_fd.cFileName[0] == _T('.') && (m_fd.cFileName[1] == _T('\0') || (m_fd.cFileName[1] == _T('.') && m_fd.cFileName[2] == _T('\0')))) bResult = TRUE; } return bResult; } BOOL IsReadOnly() const { return MatchesMask(FILE_ATTRIBUTE_READONLY); } BOOL IsDirectory() const { return MatchesMask(FILE_ATTRIBUTE_DIRECTORY); } BOOL IsCompressed() const { return MatchesMask(FILE_ATTRIBUTE_COMPRESSED); } BOOL IsSystem() const { return MatchesMask(FILE_ATTRIBUTE_SYSTEM); } BOOL IsHidden() const { return MatchesMask(FILE_ATTRIBUTE_HIDDEN); } BOOL IsTemporary() const { return MatchesMask(FILE_ATTRIBUTE_TEMPORARY); } BOOL IsNormal() const { return MatchesMask(FILE_ATTRIBUTE_NORMAL); } BOOL IsArchived() const { return MatchesMask(FILE_ATTRIBUTE_ARCHIVE); } // Operations BOOL FindFile(LPCTSTR pstrName = NULL) { Close(); if(pstrName == NULL) { pstrName = _T("*.*"); } else if(lstrlen(pstrName) >= MAX_PATH) { ATLASSERT(FALSE); return FALSE; } SecureHelper::strcpy_x(m_fd.cFileName, _countof(m_fd.cFileName), pstrName); m_hFind = ::FindFirstFile(pstrName, &m_fd); if(m_hFind == INVALID_HANDLE_VALUE) return FALSE; #ifndef _WIN32_WCE bool bFullPath = (::GetFullPathName(pstrName, MAX_PATH, m_lpszRoot, NULL) != 0); #else // CE specific errno_t nRet = SecureHelper::strncpy_x(m_lpszRoot, _countof(m_lpszRoot), pstrName, _TRUNCATE); bool bFullPath = (nRet == 0 || nRet == STRUNCATE); #endif // _WIN32_WCE // passed name isn't a valid path but was found by the API ATLASSERT(bFullPath); if(!bFullPath) { Close(); ::SetLastError(ERROR_INVALID_NAME); return FALSE; } else { // find the last forward or backward whack LPTSTR pstrBack = MinCrtHelper::_strrchr(m_lpszRoot, _T('\\')); LPTSTR pstrFront = MinCrtHelper::_strrchr(m_lpszRoot, _T('/')); if(pstrFront != NULL || pstrBack != NULL) { if(pstrFront == NULL) pstrFront = m_lpszRoot; if(pstrBack == NULL) pstrBack = m_lpszRoot; // from the start to the last whack is the root if(pstrFront >= pstrBack) *pstrFront = _T('\0'); else *pstrBack = _T('\0'); } } m_bFound = TRUE; return TRUE; } BOOL FindNextFile() { ATLASSERT(m_hFind != NULL); if(m_hFind == NULL) return FALSE; if(!m_bFound) return FALSE; m_bFound = ::FindNextFile(m_hFind, &m_fd); return m_bFound; } void Close() { m_bFound = FALSE; if(m_hFind != NULL && m_hFind != INVALID_HANDLE_VALUE) { ::FindClose(m_hFind); m_hFind = NULL; } } }; /////////////////////////////////////////////////////////////////////////////// // Global functions for stock GDI objects inline HPEN AtlGetStockPen(int nPen) { #if (_WIN32_WINNT >= 0x0500) && !defined(_WIN32_WCE) ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN || nPen == DC_PEN); #else ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN); #endif return (HPEN)::GetStockObject(nPen); } inline HBRUSH AtlGetStockBrush(int nBrush) { #if (_WIN32_WINNT >= 0x0500) && !defined(_WIN32_WCE) ATLASSERT((nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH) || nBrush == DC_BRUSH); #else ATLASSERT(nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH); #endif return (HBRUSH)::GetStockObject(nBrush); } inline HFONT AtlGetStockFont(int nFont) { #ifndef _WIN32_WCE ATLASSERT((nFont >= OEM_FIXED_FONT && nFont <= SYSTEM_FIXED_FONT) || nFont == DEFAULT_GUI_FONT); #else // CE specific ATLASSERT(nFont == SYSTEM_FONT); #endif // _WIN32_WCE return (HFONT)::GetStockObject(nFont); } inline HPALETTE AtlGetStockPalette(int nPalette) { ATLASSERT(nPalette == DEFAULT_PALETTE); // the only one supported return (HPALETTE)::GetStockObject(nPalette); } /////////////////////////////////////////////////////////////////////////////// // Global function for compacting a path by replacing parts with ellipsis // helper for multi-byte character sets inline bool _IsDBCSTrailByte(LPCTSTR lpstr, int nChar) { #ifndef _UNICODE int i = nChar; for( ; i > 0; i--) { if(!::IsDBCSLeadByte(lpstr[i - 1])) break; } return ((nChar > 0) && (((nChar - i) & 1) != 0)); #else // _UNICODE lpstr; nChar; return false; #endif // _UNICODE } inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen) { ATLASSERT(lpstrOut != NULL); ATLASSERT(lpstrIn != NULL); ATLASSERT(cchLen > 0); LPCTSTR szEllipsis = _T("..."); const int cchEndEllipsis = 3; const int cchMidEllipsis = 4; if(lstrlen(lpstrIn) < cchLen) { SecureHelper::strcpy_x(lpstrOut, cchLen, lpstrIn); return true; } lpstrOut[0] = 0; // check if the separator is a slash or a backslash TCHAR chSlash = _T('\\'); for(LPTSTR lpstr = (LPTSTR)lpstrIn; *lpstr != 0; lpstr = ::CharNext(lpstr)) { if((*lpstr == _T('/')) || (*lpstr == _T('\\'))) chSlash = *lpstr; } // find the filename portion of the path LPCTSTR lpstrFileName = lpstrIn; for(LPCTSTR pPath = lpstrIn; *pPath; pPath = ::CharNext(pPath)) { if((pPath[0] == _T('\\') || pPath[0] == _T(':') || pPath[0] == _T('/')) && pPath[1] && pPath[1] != _T('\\') && pPath[1] != _T('/')) lpstrFileName = pPath + 1; } int cchFileName = lstrlen(lpstrFileName); // handle just the filename without a path if(lpstrFileName == lpstrIn && cchLen > cchEndEllipsis) { bool bRet = (SecureHelper::strncpy_x(lpstrOut, cchLen, lpstrIn, cchLen - cchEndEllipsis - 1) == 0); if(bRet) { #ifndef _UNICODE if(_IsDBCSTrailByte(lpstrIn, cchLen - cchEndEllipsis)) lpstrOut[cchLen - cchEndEllipsis - 1] = 0; #endif // _UNICODE SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis); } return bRet; } // handle just ellipsis if((cchLen < (cchMidEllipsis + cchEndEllipsis))) { for(int i = 0; i < cchLen - 1; i++) lpstrOut[i] = ((i + 1) == cchMidEllipsis) ? chSlash : _T('.'); lpstrOut[cchLen - 1] = 0; return true; } // calc how much we have to copy int cchToCopy = cchLen - (cchMidEllipsis + cchFileName) - 1; if(cchToCopy < 0) cchToCopy = 0; #ifndef _UNICODE if(cchToCopy > 0 && _IsDBCSTrailByte(lpstrIn, cchToCopy)) cchToCopy--; #endif // _UNICODE bool bRet = (SecureHelper::strncpy_x(lpstrOut, cchLen, lpstrIn, cchToCopy) == 0); if(!bRet) return false; // add ellipsis SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis); if(!bRet) return false; TCHAR szSlash[2] = { chSlash, 0 }; SecureHelper::strcat_x(lpstrOut, cchLen, szSlash); if(!bRet) return false; // add filename (and ellipsis, if needed) if(cchLen > (cchMidEllipsis + cchFileName)) { SecureHelper::strcat_x(lpstrOut, cchLen, lpstrFileName); } else { cchToCopy = cchLen - cchMidEllipsis - cchEndEllipsis - 1; #ifndef _UNICODE if(cchToCopy > 0 && _IsDBCSTrailByte(lpstrFileName, cchToCopy)) cchToCopy--; #endif // _UNICODE bRet = (SecureHelper::strncpy_x(&lpstrOut[cchMidEllipsis], cchLen - cchMidEllipsis, lpstrFileName, cchToCopy) == 0); if(bRet) SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis); } return bRet; } }; // namespace WTL #endif // __ATLMISC_H__ ================================================ FILE: src/Setup/wtl90/atlprint.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLPRINT_H__ #define __ATLPRINT_H__ #pragma once #ifdef _WIN32_WCE #error atlprint.h is not supported on Windows CE #endif #ifndef __ATLAPP_H__ #error atlprint.h requires atlapp.h to be included first #endif #ifndef __ATLWIN_H__ #error atlprint.h requires atlwin.h to be included first #endif /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CPrinterInfo // CPrinterT // CDevModeT // CPrinterDC // CPrintJobInfo // CPrintJob // CPrintPreview // CPrintPreviewWindowImpl // CPrintPreviewWindow // CZoomPrintPreviewWindowImpl // CZoomPrintPreviewWindow namespace WTL { /////////////////////////////////////////////////////////////////////////////// // CPrinterInfo - This class wraps all of the PRINTER_INFO_* structures // and provided by ::GetPrinter. template class _printer_info { public: typedef void infotype; }; template <> class _printer_info<1> { public: typedef PRINTER_INFO_1 infotype; }; template <> class _printer_info<2> { public: typedef PRINTER_INFO_2 infotype; }; template <> class _printer_info<3> { public: typedef PRINTER_INFO_3 infotype; }; template <> class _printer_info<4> { public: typedef PRINTER_INFO_4 infotype; }; template <> class _printer_info<5> { public: typedef PRINTER_INFO_5 infotype; }; template <> class _printer_info<6> { public: typedef PRINTER_INFO_6 infotype; }; template <> class _printer_info<7> { public: typedef PRINTER_INFO_7 infotype; }; // these are not in the old (vc6.0) headers #ifdef _ATL_USE_NEW_PRINTER_INFO template <> class _printer_info<8> { public: typedef PRINTER_INFO_8 infotype; }; template <> class _printer_info<9> { public: typedef PRINTER_INFO_9 infotype; }; #endif // _ATL_USE_NEW_PRINTER_INFO template class CPrinterInfo { public: // Data members typename _printer_info::infotype* m_pi; // Constructor/destructor CPrinterInfo() : m_pi(NULL) { } CPrinterInfo(HANDLE hPrinter) : m_pi(NULL) { GetPrinterInfo(hPrinter); } ~CPrinterInfo() { Cleanup(); } // Operations bool GetPrinterInfo(HANDLE hPrinter) { Cleanup(); return GetPrinterInfoHelper(hPrinter, (BYTE**)&m_pi, t_nInfo); } // Implementation void Cleanup() { delete [] (BYTE*)m_pi; m_pi = NULL; } static bool GetPrinterInfoHelper(HANDLE hPrinter, BYTE** pi, int nIndex) { ATLASSERT(pi != NULL); DWORD dw = 0; BYTE* pb = NULL; ::GetPrinter(hPrinter, nIndex, NULL, 0, &dw); if (dw > 0) { ATLTRY(pb = new BYTE[dw]); if (pb != NULL) { memset(pb, 0, dw); DWORD dwNew; if (!::GetPrinter(hPrinter, nIndex, pb, dw, &dwNew)) { delete [] pb; pb = NULL; } } } *pi = pb; return (pb != NULL); } }; /////////////////////////////////////////////////////////////////////////////// // CPrinter - Wrapper class for a HANDLE to a printer template class CPrinterT { public: // Data members HANDLE m_hPrinter; // Constructor/destructor CPrinterT(HANDLE hPrinter = NULL) : m_hPrinter(hPrinter) { } ~CPrinterT() { ClosePrinter(); } // Operations CPrinterT& operator =(HANDLE hPrinter) { if (hPrinter != m_hPrinter) { ClosePrinter(); m_hPrinter = hPrinter; } return *this; } bool IsNull() const { return (m_hPrinter == NULL); } bool OpenPrinter(HANDLE hDevNames, const DEVMODE* pDevMode = NULL) { bool b = false; DEVNAMES* pdn = (DEVNAMES*)::GlobalLock(hDevNames); if (pdn != NULL) { LPTSTR lpszPrinterName = (LPTSTR)pdn + pdn->wDeviceOffset; b = OpenPrinter(lpszPrinterName, pDevMode); ::GlobalUnlock(hDevNames); } return b; } bool OpenPrinter(LPCTSTR lpszPrinterName, const DEVMODE* pDevMode = NULL) { ClosePrinter(); PRINTER_DEFAULTS pdefs = { NULL, (DEVMODE*)pDevMode, PRINTER_ACCESS_USE }; ::OpenPrinter((LPTSTR) lpszPrinterName, &m_hPrinter, (pDevMode == NULL) ? NULL : &pdefs); return (m_hPrinter != NULL); } bool OpenPrinter(LPCTSTR lpszPrinterName, PRINTER_DEFAULTS* pprintdefs) { ClosePrinter(); ::OpenPrinter((LPTSTR) lpszPrinterName, &m_hPrinter, pprintdefs); return (m_hPrinter != NULL); } bool OpenDefaultPrinter(const DEVMODE* pDevMode = NULL) { ClosePrinter(); const int cchBuff = 512; TCHAR buffer[cchBuff] = { 0 }; ::GetProfileString(_T("windows"), _T("device"), _T(",,,"), buffer, cchBuff); int nLen = lstrlen(buffer); if (nLen != 0) { LPTSTR lpsz = buffer; while (*lpsz) { if (*lpsz == _T(',')) { *lpsz = 0; break; } lpsz = CharNext(lpsz); } PRINTER_DEFAULTS pdefs = { NULL, (DEVMODE*)pDevMode, PRINTER_ACCESS_USE }; ::OpenPrinter(buffer, &m_hPrinter, (pDevMode == NULL) ? NULL : &pdefs); } return m_hPrinter != NULL; } void ClosePrinter() { if (m_hPrinter != NULL) { if (t_bManaged) ::ClosePrinter(m_hPrinter); m_hPrinter = NULL; } } bool PrinterProperties(HWND hWnd = NULL) { if (hWnd == NULL) hWnd = ::GetActiveWindow(); return !!::PrinterProperties(hWnd, m_hPrinter); } HANDLE CopyToHDEVNAMES() const { HANDLE h = NULL; CPrinterInfo<5> pinfon5; CPrinterInfo<2> pinfon2; LPTSTR lpszPrinterName = NULL; // Some printers fail for PRINTER_INFO_5 in some situations if (pinfon5.GetPrinterInfo(m_hPrinter)) lpszPrinterName = pinfon5.m_pi->pPrinterName; else if (pinfon2.GetPrinterInfo(m_hPrinter)) lpszPrinterName = pinfon2.m_pi->pPrinterName; if (lpszPrinterName != NULL) { int nLen = sizeof(DEVNAMES) + (lstrlen(lpszPrinterName) + 1) * sizeof(TCHAR); h = ::GlobalAlloc(GMEM_MOVEABLE, nLen); BYTE* pv = (BYTE*)::GlobalLock(h); DEVNAMES* pdev = (DEVNAMES*)pv; if (pv != NULL) { memset(pv, 0, nLen); pdev->wDeviceOffset = sizeof(DEVNAMES) / sizeof(TCHAR); pv = pv + sizeof(DEVNAMES); // now points to end SecureHelper::strcpy_x((LPTSTR)pv, lstrlen(lpszPrinterName) + 1, lpszPrinterName); ::GlobalUnlock(h); } } return h; } HDC CreatePrinterDC(const DEVMODE* pdm = NULL) const { CPrinterInfo<5> pinfo5; CPrinterInfo<2> pinfo2; HDC hDC = NULL; LPTSTR lpszPrinterName = NULL; // Some printers fail for PRINTER_INFO_5 in some situations if (pinfo5.GetPrinterInfo(m_hPrinter)) lpszPrinterName = pinfo5.m_pi->pPrinterName; else if (pinfo2.GetPrinterInfo(m_hPrinter)) lpszPrinterName = pinfo2.m_pi->pPrinterName; if (lpszPrinterName != NULL) hDC = ::CreateDC(NULL, lpszPrinterName, NULL, pdm); return hDC; } HDC CreatePrinterIC(const DEVMODE* pdm = NULL) const { CPrinterInfo<5> pinfo5; CPrinterInfo<2> pinfo2; HDC hDC = NULL; LPTSTR lpszPrinterName = NULL; // Some printers fail for PRINTER_INFO_5 in some situations if (pinfo5.GetPrinterInfo(m_hPrinter)) lpszPrinterName = pinfo5.m_pi->pPrinterName; else if (pinfo2.GetPrinterInfo(m_hPrinter)) lpszPrinterName = pinfo2.m_pi->pPrinterName; if (lpszPrinterName != NULL) hDC = ::CreateIC(NULL, lpszPrinterName, NULL, pdm); return hDC; } void Attach(HANDLE hPrinter) { ClosePrinter(); m_hPrinter = hPrinter; } HANDLE Detach() { HANDLE hPrinter = m_hPrinter; m_hPrinter = NULL; return hPrinter; } operator HANDLE() const { return m_hPrinter; } }; typedef CPrinterT CPrinterHandle; typedef CPrinterT CPrinter; /////////////////////////////////////////////////////////////////////////////// // CDevMode - Wrapper class for DEVMODE template class CDevModeT { public: // Data members HANDLE m_hDevMode; DEVMODE* m_pDevMode; // Constructor/destructor CDevModeT(HANDLE hDevMode = NULL) : m_hDevMode(hDevMode) { m_pDevMode = (m_hDevMode != NULL) ? (DEVMODE*)::GlobalLock(m_hDevMode) : NULL; } ~CDevModeT() { Cleanup(); } // Operations CDevModeT& operator =(HANDLE hDevMode) { Attach(hDevMode); return *this; } void Attach(HANDLE hDevModeNew) { Cleanup(); m_hDevMode = hDevModeNew; m_pDevMode = (m_hDevMode != NULL) ? (DEVMODE*)::GlobalLock(m_hDevMode) : NULL; } HANDLE Detach() { if (m_hDevMode != NULL) ::GlobalUnlock(m_hDevMode); HANDLE hDevMode = m_hDevMode; m_hDevMode = NULL; return hDevMode; } bool IsNull() const { return (m_hDevMode == NULL); } bool CopyFromPrinter(HANDLE hPrinter) { CPrinterInfo<2> pinfo; bool b = pinfo.GetPrinterInfo(hPrinter); if (b) b = CopyFromDEVMODE(pinfo.m_pi->pDevMode); return b; } bool CopyFromDEVMODE(const DEVMODE* pdm) { if (pdm == NULL) return false; int nSize = pdm->dmSize + pdm->dmDriverExtra; HANDLE h = ::GlobalAlloc(GMEM_MOVEABLE, nSize); if (h != NULL) { void* p = ::GlobalLock(h); SecureHelper::memcpy_x(p, nSize, pdm, nSize); ::GlobalUnlock(h); } Attach(h); return (h != NULL); } bool CopyFromHDEVMODE(HANDLE hdm) { bool b = false; if (hdm != NULL) { DEVMODE* pdm = (DEVMODE*)::GlobalLock(hdm); b = CopyFromDEVMODE(pdm); ::GlobalUnlock(hdm); } return b; } HANDLE CopyToHDEVMODE() { if ((m_hDevMode == NULL) || (m_pDevMode == NULL)) return NULL; int nSize = m_pDevMode->dmSize + m_pDevMode->dmDriverExtra; HANDLE h = ::GlobalAlloc(GMEM_MOVEABLE, nSize); if (h != NULL) { void* p = ::GlobalLock(h); SecureHelper::memcpy_x(p, nSize, m_pDevMode, nSize); ::GlobalUnlock(h); } return h; } // If this devmode was for another printer, this will create a new devmode // based on the existing devmode, but retargeted at the new printer bool UpdateForNewPrinter(HANDLE hPrinter) { bool bRet = false; LONG nLen = ::DocumentProperties(NULL, hPrinter, NULL, NULL, NULL, 0); CTempBuffer buff; DEVMODE* pdm = buff.AllocateBytes(nLen); if(pdm != NULL) { memset(pdm, 0, nLen); LONG l = ::DocumentProperties(NULL, hPrinter, NULL, pdm, m_pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER); if (l == IDOK) bRet = CopyFromDEVMODE(pdm); } return bRet; } bool DocumentProperties(HANDLE hPrinter, HWND hWnd = NULL) { CPrinterInfo<1> pi; pi.GetPrinterInfo(hPrinter); if (hWnd == NULL) hWnd = ::GetActiveWindow(); bool bRet = false; LONG nLen = ::DocumentProperties(hWnd, hPrinter, pi.m_pi->pName, NULL, NULL, 0); CTempBuffer buff; DEVMODE* pdm = buff.AllocateBytes(nLen); if(pdm != NULL) { memset(pdm, 0, nLen); LONG l = ::DocumentProperties(hWnd, hPrinter, pi.m_pi->pName, pdm, m_pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER | DM_PROMPT); if (l == IDOK) bRet = CopyFromDEVMODE(pdm); } return bRet; } operator HANDLE() const { return m_hDevMode; } operator DEVMODE*() const { return m_pDevMode; } // Implementation void Cleanup() { if (m_hDevMode != NULL) { ::GlobalUnlock(m_hDevMode); if(t_bManaged) ::GlobalFree(m_hDevMode); m_hDevMode = NULL; } } }; typedef CDevModeT CDevModeHandle; typedef CDevModeT CDevMode; /////////////////////////////////////////////////////////////////////////////// // CPrinterDC class CPrinterDC : public CDC { public: // Constructors/destructor CPrinterDC() { CPrinter printer; printer.OpenDefaultPrinter(); Attach(printer.CreatePrinterDC()); ATLASSERT(m_hDC != NULL); } CPrinterDC(HANDLE hPrinter, const DEVMODE* pdm = NULL) { CPrinterHandle p; p.Attach(hPrinter); Attach(p.CreatePrinterDC(pdm)); ATLASSERT(m_hDC != NULL); } ~CPrinterDC() { DeleteDC(); } }; /////////////////////////////////////////////////////////////////////////////// // CPrintJob - Wraps a set of tasks for a specific printer (StartDoc/EndDoc) // Handles aborting, background printing // Defines callbacks used by CPrintJob (not a COM interface) class ATL_NO_VTABLE IPrintJobInfo { public: virtual void BeginPrintJob(HDC hDC) = 0; // allocate handles needed, etc. virtual void EndPrintJob(HDC hDC, bool bAborted) = 0; // free handles, etc. virtual void PrePrintPage(UINT nPage, HDC hDC) = 0; virtual bool PrintPage(UINT nPage, HDC hDC) = 0; virtual void PostPrintPage(UINT nPage, HDC hDC) = 0; // If you want per page devmodes, return the DEVMODE* to use for nPage. // You can optimize by only returning a new DEVMODE* when it is different // from the one for nLastPage, otherwise return NULL. // When nLastPage==0, the current DEVMODE* will be the default passed to // StartPrintJob. // Note: During print preview, nLastPage will always be "0". virtual DEVMODE* GetNewDevModeForPage(UINT nLastPage, UINT nPage) = 0; virtual bool IsValidPage(UINT nPage) = 0; }; // Provides a default implementatin for IPrintJobInfo // Typically, MI'd into a document or view class class ATL_NO_VTABLE CPrintJobInfo : public IPrintJobInfo { public: virtual void BeginPrintJob(HDC /*hDC*/) // allocate handles needed, etc { } virtual void EndPrintJob(HDC /*hDC*/, bool /*bAborted*/) // free handles, etc { } virtual void PrePrintPage(UINT /*nPage*/, HDC hDC) { m_nPJState = ::SaveDC(hDC); } virtual bool PrintPage(UINT /*nPage*/, HDC /*hDC*/) = 0; virtual void PostPrintPage(UINT /*nPage*/, HDC hDC) { RestoreDC(hDC, m_nPJState); } virtual DEVMODE* GetNewDevModeForPage(UINT /*nLastPage*/, UINT /*nPage*/) { return NULL; } virtual bool IsValidPage(UINT /*nPage*/) { return true; } // Implementation - data int m_nPJState; }; class CPrintJob { public: // Data members CPrinterHandle m_printer; IPrintJobInfo* m_pInfo; DEVMODE* m_pDefDevMode; DOCINFO m_docinfo; int m_nJobID; bool m_bCancel; bool m_bComplete; unsigned long m_nStartPage; unsigned long m_nEndPage; // Constructor/destructor CPrintJob() : m_nJobID(0), m_bCancel(false), m_bComplete(true) { } ~CPrintJob() { ATLASSERT(IsJobComplete()); // premature destruction? } // Operations bool IsJobComplete() const { return m_bComplete; } bool StartPrintJob(bool bBackground, HANDLE hPrinter, DEVMODE* pDefaultDevMode, IPrintJobInfo* pInfo, LPCTSTR lpszDocName, unsigned long nStartPage, unsigned long nEndPage, bool bPrintToFile = false, LPCTSTR lpstrOutputFile = NULL) { ATLASSERT(m_bComplete); // previous job not done yet? if (pInfo == NULL) return false; memset(&m_docinfo, 0, sizeof(m_docinfo)); m_docinfo.cbSize = sizeof(m_docinfo); m_docinfo.lpszDocName = lpszDocName; m_pInfo = pInfo; m_nStartPage = nStartPage; m_nEndPage = nEndPage; m_printer.Attach(hPrinter); m_pDefDevMode = pDefaultDevMode; m_bComplete = false; if(bPrintToFile) m_docinfo.lpszOutput = (lpstrOutputFile != NULL) ? lpstrOutputFile : _T("FILE:"); if (!bBackground) { m_bComplete = true; return StartHelper(); } // Create a thread and return DWORD dwThreadID = 0; #if !defined(_ATL_MIN_CRT) && defined(_MT) HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, (UINT (WINAPI*)(void*))StartProc, this, 0, (UINT*)&dwThreadID); #else HANDLE hThread = ::CreateThread(NULL, 0, StartProc, (void*)this, 0, &dwThreadID); #endif if (hThread == NULL) return false; ::CloseHandle(hThread); return true; } // Implementation static DWORD WINAPI StartProc(void* p) { CPrintJob* pThis = (CPrintJob*)p; pThis->StartHelper(); pThis->m_bComplete = true; return 0; } bool StartHelper() { CDC dcPrinter; dcPrinter.Attach(m_printer.CreatePrinterDC(m_pDefDevMode)); if (dcPrinter.IsNull()) return false; m_nJobID = ::StartDoc(dcPrinter, &m_docinfo); if (m_nJobID <= 0) return false; m_pInfo->BeginPrintJob(dcPrinter); // print all the pages now unsigned long nLastPage = 0; for (unsigned long nPage = m_nStartPage; nPage <= m_nEndPage; nPage++) { if (!m_pInfo->IsValidPage(nPage)) break; DEVMODE* pdm = m_pInfo->GetNewDevModeForPage(nLastPage, nPage); if (pdm != NULL) dcPrinter.ResetDC(pdm); dcPrinter.StartPage(); m_pInfo->PrePrintPage(nPage, dcPrinter); if (!m_pInfo->PrintPage(nPage, dcPrinter)) m_bCancel = true; m_pInfo->PostPrintPage(nPage, dcPrinter); dcPrinter.EndPage(); if (m_bCancel) break; nLastPage = nPage; } m_pInfo->EndPrintJob(dcPrinter, m_bCancel); if (m_bCancel) ::AbortDoc(dcPrinter); else ::EndDoc(dcPrinter); m_nJobID = 0; return true; } // Cancels a print job. Can be called asynchronously. void CancelPrintJob() { m_bCancel = true; } }; /////////////////////////////////////////////////////////////////////////////// // CPrintPreview - Adds print preview support to an existing window class CPrintPreview { public: // Data members IPrintJobInfo* m_pInfo; CPrinterHandle m_printer; CEnhMetaFile m_meta; DEVMODE* m_pDefDevMode; DEVMODE* m_pCurDevMode; SIZE m_sizeCurPhysOffset; // Constructor CPrintPreview() : m_pInfo(NULL), m_pDefDevMode(NULL), m_pCurDevMode(NULL) { m_sizeCurPhysOffset.cx = 0; m_sizeCurPhysOffset.cy = 0; } // Operations void SetPrintPreviewInfo(HANDLE hPrinter, DEVMODE* pDefaultDevMode, IPrintJobInfo* pji) { m_printer.Attach(hPrinter); m_pDefDevMode = pDefaultDevMode; m_pInfo = pji; m_nCurPage = 0; m_pCurDevMode = NULL; } void SetEnhMetaFile(HENHMETAFILE hEMF) { m_meta = hEMF; } void SetPage(int nPage) { if (!m_pInfo->IsValidPage(nPage)) return; m_nCurPage = nPage; m_pCurDevMode = m_pInfo->GetNewDevModeForPage(0, nPage); if (m_pCurDevMode == NULL) m_pCurDevMode = m_pDefDevMode; CDC dcPrinter = m_printer.CreatePrinterDC(m_pCurDevMode); int iWidth = dcPrinter.GetDeviceCaps(PHYSICALWIDTH); int iHeight = dcPrinter.GetDeviceCaps(PHYSICALHEIGHT); int nLogx = dcPrinter.GetDeviceCaps(LOGPIXELSX); int nLogy = dcPrinter.GetDeviceCaps(LOGPIXELSY); RECT rcMM = { 0, 0, ::MulDiv(iWidth, 2540, nLogx), ::MulDiv(iHeight, 2540, nLogy) }; m_sizeCurPhysOffset.cx = dcPrinter.GetDeviceCaps(PHYSICALOFFSETX); m_sizeCurPhysOffset.cy = dcPrinter.GetDeviceCaps(PHYSICALOFFSETY); CEnhMetaFileDC dcMeta(dcPrinter, &rcMM); m_pInfo->PrePrintPage(nPage, dcMeta); m_pInfo->PrintPage(nPage, dcMeta); m_pInfo->PostPrintPage(nPage, dcMeta); m_meta.Attach(dcMeta.Close()); } void GetPageRect(RECT& rc, LPRECT prc) { int x1 = rc.right-rc.left; int y1 = rc.bottom - rc.top; if ((x1 < 0) || (y1 < 0)) return; CEnhMetaFileInfo emfinfo(m_meta); ENHMETAHEADER* pmh = emfinfo.GetEnhMetaFileHeader(); // Compute whether we are OK vertically or horizontally int x2 = pmh->szlDevice.cx; int y2 = pmh->szlDevice.cy; int y1p = MulDiv(x1, y2, x2); int x1p = MulDiv(y1, x2, y2); ATLASSERT((x1p <= x1) || (y1p <= y1)); if (x1p <= x1) { prc->left = rc.left + (x1 - x1p) / 2; prc->right = prc->left + x1p; prc->top = rc.top; prc->bottom = rc.bottom; } else { prc->left = rc.left; prc->right = rc.right; prc->top = rc.top + (y1 - y1p) / 2; prc->bottom = prc->top + y1p; } } // Painting helpers void DoPaint(CDCHandle dc) { // this one is not used } void DoPaint(CDCHandle dc, RECT& rc) { CEnhMetaFileInfo emfinfo(m_meta); ENHMETAHEADER* pmh = emfinfo.GetEnhMetaFileHeader(); int nOffsetX = MulDiv(m_sizeCurPhysOffset.cx, rc.right-rc.left, pmh->szlDevice.cx); int nOffsetY = MulDiv(m_sizeCurPhysOffset.cy, rc.bottom-rc.top, pmh->szlDevice.cy); dc.OffsetWindowOrg(-nOffsetX, -nOffsetY); dc.PlayMetaFile(m_meta, &rc); } // Implementation - data int m_nCurPage; }; /////////////////////////////////////////////////////////////////////////////// // CPrintPreviewWindow - Implements a print preview window template class ATL_NO_VTABLE CPrintPreviewWindowImpl : public ATL::CWindowImpl, public CPrintPreview { public: DECLARE_WND_CLASS_EX(NULL, CS_VREDRAW | CS_HREDRAW, -1) enum { m_cxOffset = 10, m_cyOffset = 10 }; // Constructor CPrintPreviewWindowImpl() : m_nMaxPage(0), m_nMinPage(0) { } // Operations void SetPrintPreviewInfo(HANDLE hPrinter, DEVMODE* pDefaultDevMode, IPrintJobInfo* pji, int nMinPage, int nMaxPage) { CPrintPreview::SetPrintPreviewInfo(hPrinter, pDefaultDevMode, pji); m_nMinPage = nMinPage; m_nMaxPage = nMaxPage; } bool NextPage() { if (m_nCurPage == m_nMaxPage) return false; SetPage(m_nCurPage + 1); Invalidate(); return true; } bool PrevPage() { if (m_nCurPage == m_nMinPage) return false; if (m_nCurPage == 0) return false; SetPage(m_nCurPage - 1); Invalidate(); return true; } // Message map and handlers BEGIN_MSG_MAP(CPrintPreviewWindowImpl) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) MESSAGE_HANDLER(WM_PAINT, OnPaint) MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint) END_MSG_MAP() LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return 1; // no need for the background } LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); RECT rc = { 0 }; if(wParam != NULL) { pT->DoPrePaint((HDC)wParam, rc); pT->DoPaint((HDC)wParam, rc); } else { CPaintDC dc(m_hWnd); pT->DoPrePaint(dc.m_hDC, rc); pT->DoPaint(dc.m_hDC, rc); } return 0; } // Painting helper void DoPrePaint(CDCHandle dc, RECT& rc) { RECT rcClient = { 0 }; GetClientRect(&rcClient); RECT rcArea = rcClient; T* pT = static_cast(this); pT; // avoid level 4 warning ::InflateRect(&rcArea, -pT->m_cxOffset, -pT->m_cyOffset); if (rcArea.left > rcArea.right) rcArea.right = rcArea.left; if (rcArea.top > rcArea.bottom) rcArea.bottom = rcArea.top; GetPageRect(rcArea, &rc); CRgn rgn1, rgn2; rgn1.CreateRectRgnIndirect(&rc); rgn2.CreateRectRgnIndirect(&rcClient); rgn2.CombineRgn(rgn1, RGN_DIFF); dc.SelectClipRgn(rgn2); dc.FillRect(&rcClient, COLOR_BTNSHADOW); dc.SelectClipRgn(NULL); dc.FillRect(&rc, (HBRUSH)::GetStockObject(WHITE_BRUSH)); } // Implementation - data int m_nMinPage; int m_nMaxPage; }; class CPrintPreviewWindow : public CPrintPreviewWindowImpl { public: DECLARE_WND_CLASS_EX(_T("WTL_PrintPreview"), CS_VREDRAW | CS_HREDRAW, -1) }; /////////////////////////////////////////////////////////////////////////////// // CZoomPrintPreviewWindowImpl - Implements print preview window with zooming #ifdef __ATLSCRL_H__ template class ATL_NO_VTABLE CZoomPrintPreviewWindowImpl : public CPrintPreviewWindowImpl< T, TBase, TWinTraits >, public CZoomScrollImpl< T > { public: bool m_bSized; CZoomPrintPreviewWindowImpl() { SetScrollExtendedStyle(SCRL_DISABLENOSCROLL); InitZoom(); } // should be called to reset data members before recreating window void InitZoom() { m_bSized = false; m_nZoomMode = ZOOMMODE_OFF; m_fZoomScaleMin = 1.0; m_fZoomScale = 1.0; } BEGIN_MSG_MAP(CZoomPrintPreviewWindowImpl) MESSAGE_HANDLER(WM_SETCURSOR, CZoomScrollImpl< T >::OnSetCursor) MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll) MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll) MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel) #if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) MESSAGE_HANDLER(m_uMsgMouseWheel, CScrollImpl< T >::OnMouseWheel) #endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel) MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange) MESSAGE_HANDLER(WM_LBUTTONDOWN, CZoomScrollImpl< T >::OnLButtonDown) MESSAGE_HANDLER(WM_MOUSEMOVE, CZoomScrollImpl< T >::OnMouseMove) MESSAGE_HANDLER(WM_LBUTTONUP, CZoomScrollImpl< T >::OnLButtonUp) MESSAGE_HANDLER(WM_CAPTURECHANGED, CZoomScrollImpl< T >::OnCaptureChanged) MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) MESSAGE_HANDLER(WM_PAINT, OnPaint) MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint) ALT_MSG_MAP(1) COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp) COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown) COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop) COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom) COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft) COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight) COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft) COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight) END_MSG_MAP() LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { SIZE sizeClient = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; POINT ptOffset = m_ptOffset; SIZE sizeAll = m_sizeAll; SetScrollSize(sizeClient); if(sizeAll.cx > 0) ptOffset.x = ::MulDiv(ptOffset.x, m_sizeAll.cx, sizeAll.cx); if(sizeAll.cy > 0) ptOffset.y = ::MulDiv(ptOffset.y, m_sizeAll.cy, sizeAll.cy); SetScrollOffset(ptOffset); CScrollImpl< T >::OnSize(uMsg, wParam, lParam, bHandled); if(!m_bSized) { m_bSized = true; T* pT = static_cast(this); pT->ShowScrollBar(SB_HORZ, TRUE); pT->ShowScrollBar(SB_VERT, TRUE); } return 0; } LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return 1; } LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); RECT rc = { 0 }; if(wParam != NULL) { CDCHandle dc = (HDC)wParam; int nMapModeSav = dc.GetMapMode(); dc.SetMapMode(MM_ANISOTROPIC); SIZE szWindowExt = { 0, 0 }; dc.SetWindowExt(m_sizeLogAll, &szWindowExt); SIZE szViewportExt = { 0, 0 }; dc.SetViewportExt(m_sizeAll, &szViewportExt); POINT ptViewportOrg = { 0, 0 }; dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y, &ptViewportOrg); pT->DoPrePaint(dc, rc); pT->DoPaint(dc, rc); dc.SetMapMode(nMapModeSav); dc.SetWindowExt(szWindowExt); dc.SetViewportExt(szViewportExt); dc.SetViewportOrg(ptViewportOrg); } else { CPaintDC dc(pT->m_hWnd); pT->PrepareDC(dc.m_hDC); pT->DoPrePaint(dc.m_hDC, rc); pT->DoPaint(dc.m_hDC, rc); } return 0; } // Painting helpers void DoPaint(CDCHandle dc) { // this one is not used } void DoPrePaint(CDCHandle dc, RECT& rc) { RECT rcClient = { 0 }; GetClientRect(&rcClient); RECT rcArea = rcClient; T* pT = static_cast(this); pT; // avoid level 4 warning ::InflateRect(&rcArea, -pT->m_cxOffset, -pT->m_cyOffset); if (rcArea.left > rcArea.right) rcArea.right = rcArea.left; if (rcArea.top > rcArea.bottom) rcArea.bottom = rcArea.top; GetPageRect(rcArea, &rc); HBRUSH hbrOld = dc.SelectBrush(::GetSysColorBrush(COLOR_BTNSHADOW)); dc.PatBlt(rcClient.left, rcClient.top, rc.left - rcClient.left, rcClient.bottom - rcClient.top, PATCOPY); dc.PatBlt(rc.left, rcClient.top, rc.right - rc.left, rc.top - rcClient.top, PATCOPY); dc.PatBlt(rc.right, rcClient.top, rcClient.right - rc.right, rcClient.bottom - rcClient.top, PATCOPY); dc.PatBlt(rc.left, rc.bottom, rc.right - rc.left, rcClient.bottom - rc.bottom, PATCOPY); dc.SelectBrush((HBRUSH)::GetStockObject(WHITE_BRUSH)); dc.PatBlt(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY); dc.SelectBrush(::GetSysColorBrush(COLOR_3DDKSHADOW)); dc.PatBlt(rc.right, rc.top + 4, 4, rc.bottom - rc.top, PATCOPY); dc.PatBlt(rc.left + 4, rc.bottom, rc.right - rc.left, 4, PATCOPY); dc.SelectBrush(hbrOld); } void DoPaint(CDCHandle dc, RECT& rc) { CEnhMetaFileInfo emfinfo(m_meta); ENHMETAHEADER* pmh = emfinfo.GetEnhMetaFileHeader(); int nOffsetX = MulDiv(m_sizeCurPhysOffset.cx, rc.right-rc.left, pmh->szlDevice.cx); int nOffsetY = MulDiv(m_sizeCurPhysOffset.cy, rc.bottom-rc.top, pmh->szlDevice.cy); dc.OffsetWindowOrg(-nOffsetX, -nOffsetY); dc.PlayMetaFile(m_meta, &rc); } }; class CZoomPrintPreviewWindow : public CZoomPrintPreviewWindowImpl { public: DECLARE_WND_CLASS_EX(_T("WTL_ZoomPrintPreview"), CS_VREDRAW | CS_HREDRAW, -1) }; #endif // __ATLSCRL_H__ }; // namespace WTL #endif // __ATLPRINT_H__ ================================================ FILE: src/Setup/wtl90/atlres.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLRES_H__ #define __ATLRES_H__ #pragma once #if defined(_WIN32_WCE) && !defined(__ATLRESCE_H__) #error Use atlresCE.h instead of atlres.h for Windows CE #endif #ifdef RC_INVOKED #ifndef _INC_WINDOWS #define _INC_WINDOWS #ifndef _WIN32_WCE #define VS_VERSION_INFO 1 #ifdef APSTUDIO_INVOKED #define APSTUDIO_HIDDEN_SYMBOLS // Ignore following symbols #endif // APSTUDIO_INVOKED #ifndef WINVER #define WINVER 0x0400 // default to Windows Version 4.0 #endif // !WINVER #include // operation messages sent to DLGINIT #define LB_ADDSTRING (WM_USER+1) #define CB_ADDSTRING (WM_USER+3) #endif // !_WIN32_WCE #ifdef APSTUDIO_INVOKED #undef APSTUDIO_HIDDEN_SYMBOLS #endif // APSTUDIO_INVOKED #ifdef IDC_STATIC #undef IDC_STATIC #endif // IDC_STATIC #define IDC_STATIC (-1) #endif // !_INC_WINDOWS #endif // RC_INVOKED #ifdef APSTUDIO_INVOKED #define APSTUDIO_HIDDEN_SYMBOLS #endif // APSTUDIO_INVOKED /////////////////////////////////////////////////////////////////////////////// // ATL resource types #ifndef RC_INVOKED #define RT_DLGINIT MAKEINTRESOURCE(240) #define RT_TOOLBAR MAKEINTRESOURCE(241) #endif // RC_INVOKED /////////////////////////////////////////////////////////////////////////////// #ifdef APSTUDIO_INVOKED #undef APSTUDIO_HIDDEN_SYMBOLS #endif // APSTUDIO_INVOKED /////////////////////////////////////////////////////////////////////////////// // Standard window components #define ID_SEPARATOR 0 // special separator value #define ID_DEFAULT_PANE 0 // default status bar pane #ifndef RC_INVOKED // code only // standard control bars (IDW = window ID) #define ATL_IDW_TOOLBAR 0xE800 // main Toolbar for window #define ATL_IDW_STATUS_BAR 0xE801 // Status bar window #define ATL_IDW_COMMAND_BAR 0xE802 // Command bar window // parts of a frame window #define ATL_IDW_CLIENT 0xE900 #define ATL_IDW_PANE_FIRST 0xE900 // first pane (256 max) #define ATL_IDW_PANE_LAST 0xE9FF #define ATL_IDW_HSCROLL_FIRST 0xEA00 // first Horz scrollbar (16 max) #define ATL_IDW_VSCROLL_FIRST 0xEA10 // first Vert scrollbar (16 max) #define ATL_IDW_SIZE_BOX 0xEA20 // size box for splitters #define ATL_IDW_PANE_SAVE 0xEA21 // to shift ATL_IDW_PANE_FIRST // bands for a rebar #define ATL_IDW_BAND_FIRST 0xEB00 #define ATL_IDW_BAND_LAST 0xEBFF #endif // !RC_INVOKED /////////////////////////////////////////////////////////////////////////////// // Standard Commands // File commands #define ID_FILE_NEW 0xE100 #define ID_FILE_OPEN 0xE101 #define ID_FILE_CLOSE 0xE102 #define ID_FILE_SAVE 0xE103 #define ID_FILE_SAVE_AS 0xE104 #define ID_FILE_PAGE_SETUP 0xE105 #define ID_FILE_PRINT_SETUP 0xE106 #define ID_FILE_PRINT 0xE107 #define ID_FILE_PRINT_DIRECT 0xE108 #define ID_FILE_PRINT_PREVIEW 0xE109 #define ID_FILE_UPDATE 0xE10A #define ID_FILE_SAVE_COPY_AS 0xE10B #define ID_FILE_SEND_MAIL 0xE10C #define ID_FILE_MRU_FIRST 0xE110 #define ID_FILE_MRU_FILE1 0xE110 // range - 16 max #define ID_FILE_MRU_FILE2 0xE111 #define ID_FILE_MRU_FILE3 0xE112 #define ID_FILE_MRU_FILE4 0xE113 #define ID_FILE_MRU_FILE5 0xE114 #define ID_FILE_MRU_FILE6 0xE115 #define ID_FILE_MRU_FILE7 0xE116 #define ID_FILE_MRU_FILE8 0xE117 #define ID_FILE_MRU_FILE9 0xE118 #define ID_FILE_MRU_FILE10 0xE119 #define ID_FILE_MRU_FILE11 0xE11A #define ID_FILE_MRU_FILE12 0xE11B #define ID_FILE_MRU_FILE13 0xE11C #define ID_FILE_MRU_FILE14 0xE11D #define ID_FILE_MRU_FILE15 0xE11E #define ID_FILE_MRU_FILE16 0xE11F #define ID_FILE_MRU_LAST 0xE11F // Edit commands #define ID_EDIT_CLEAR 0xE120 #define ID_EDIT_CLEAR_ALL 0xE121 #define ID_EDIT_COPY 0xE122 #define ID_EDIT_CUT 0xE123 #define ID_EDIT_FIND 0xE124 #define ID_EDIT_PASTE 0xE125 #define ID_EDIT_PASTE_LINK 0xE126 #define ID_EDIT_PASTE_SPECIAL 0xE127 #define ID_EDIT_REPEAT 0xE128 #define ID_EDIT_REPLACE 0xE129 #define ID_EDIT_SELECT_ALL 0xE12A #define ID_EDIT_UNDO 0xE12B #define ID_EDIT_REDO 0xE12C #define ID_EDIT_DELETE ID_EDIT_CLEAR #define ID_EDIT_FIND_NEXT ID_EDIT_REPEAT #define ID_EDIT_FIND_PREVIOUS 0xE12D // Window commands #define ID_WINDOW_NEW 0xE130 #define ID_WINDOW_ARRANGE 0xE131 #define ID_WINDOW_CASCADE 0xE132 #define ID_WINDOW_TILE_HORZ 0xE133 #define ID_WINDOW_TILE_VERT 0xE134 #define ID_WINDOW_SPLIT 0xE135 #ifndef RC_INVOKED // code only #define ATL_IDM_WINDOW_FIRST 0xE130 #define ATL_IDM_WINDOW_LAST 0xE13F #define ATL_IDM_FIRST_MDICHILD 0xFF00 // window list starts here #define ATL_IDM_LAST_MDICHILD 0xFFFD #endif // !RC_INVOKED // TabView #define ID_WINDOW_TABFIRST 0xFF00 // = ATL_IDM_FIRST_MDICHILD #define ID_WINDOW_TABLAST 0xFFFD #define ID_WINDOW_SHOWTABLIST 0xFFFE // Help and App commands #define ID_APP_ABOUT 0xE140 #define ID_APP_EXIT 0xE141 #define ID_HELP_INDEX 0xE142 #define ID_HELP_FINDER 0xE143 #define ID_HELP_USING 0xE144 #define ID_CONTEXT_HELP 0xE145 // shift-F1 // special commands for processing help #define ID_HELP 0xE146 // first attempt for F1 #define ID_DEFAULT_HELP 0xE147 // last attempt // Misc #define ID_NEXT_PANE 0xE150 #define ID_PREV_PANE 0xE151 #define ID_PANE_CLOSE 0xE152 #define ID_PANE_NEXT ID_NEXT_PANE #define ID_PANE_PREVIOUS ID_PREV_PANE // Format #define ID_FORMAT_FONT 0xE160 // Scroll #define ID_SCROLL_UP 0xE170 #define ID_SCROLL_DOWN 0xE171 #define ID_SCROLL_PAGE_UP 0xE172 #define ID_SCROLL_PAGE_DOWN 0xE173 #define ID_SCROLL_TOP 0xE174 #define ID_SCROLL_BOTTOM 0xE175 #define ID_SCROLL_LEFT 0xE176 #define ID_SCROLL_RIGHT 0xE177 #define ID_SCROLL_PAGE_LEFT 0xE178 #define ID_SCROLL_PAGE_RIGHT 0xE179 #define ID_SCROLL_ALL_LEFT 0xE17A #define ID_SCROLL_ALL_RIGHT 0xE17B // OLE commands #define ID_OLE_INSERT_NEW 0xE200 #define ID_OLE_EDIT_LINKS 0xE201 #define ID_OLE_EDIT_CONVERT 0xE202 #define ID_OLE_EDIT_CHANGE_ICON 0xE203 #define ID_OLE_EDIT_PROPERTIES 0xE204 #define ID_OLE_VERB_FIRST 0xE210 // range - 16 max #ifndef RC_INVOKED // code only #define ID_OLE_VERB_LAST 0xE21F #endif // !RC_INVOKED // View commands (same number used as IDW used for toolbar and status bar) #define ID_VIEW_TOOLBAR 0xE800 #define ID_VIEW_STATUS_BAR 0xE801 #define ID_VIEW_REFRESH 0xE803 #define ID_VIEW_RIBBON 0xE804 /////////////////////////////////////////////////////////////////////////////// // Standard control IDs #ifdef IDC_STATIC #undef IDC_STATIC #endif // IDC_STATIC #define IDC_STATIC (-1) // all static controls /////////////////////////////////////////////////////////////////////////////// // Standard string error/warnings // idle status bar message #define ATL_IDS_IDLEMESSAGE 0xE001 #ifndef RC_INVOKED // code only #define ATL_IDS_SCFIRST 0xEF00 #endif // !RC_INVOKED #define ATL_IDS_SCSIZE 0xEF00 #define ATL_IDS_SCMOVE 0xEF01 #define ATL_IDS_SCMINIMIZE 0xEF02 #define ATL_IDS_SCMAXIMIZE 0xEF03 #define ATL_IDS_SCNEXTWINDOW 0xEF04 #define ATL_IDS_SCPREVWINDOW 0xEF05 #define ATL_IDS_SCCLOSE 0xEF06 #define ATL_IDS_SCRESTORE 0xEF12 #define ATL_IDS_SCTASKLIST 0xEF13 #define ATL_IDS_MDICHILD 0xEF1F #define ATL_IDS_MRU_FILE 0xEFDA /////////////////////////////////////////////////////////////////////////////// // Misc. control IDs // Property Sheet control id's (determined with Spy++) #define ID_APPLY_NOW 0x3021 #define ID_WIZBACK 0x3023 #define ID_WIZNEXT 0x3024 #define ID_WIZFINISH 0x3025 #define ATL_IDC_TAB_CONTROL 0x3020 #endif // __ATLRES_H__ ================================================ FILE: src/Setup/wtl90/atlresce.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLRESCE_H__ #define __ATLRESCE_H__ #pragma once #ifndef _WIN32_WCE #error atlresCE.h is only for Windows CE #endif #ifdef RC_INVOKED #ifndef _INC_WINDOWS #define VS_VERSION_INFO 1 #ifdef APSTUDIO_INVOKED #define APSTUDIO_HIDDEN_SYMBOLS // Ignore following symbols #endif // APSTUDIO_INVOKED #ifndef WINVER #define WINVER 0x0400 // default to Windows Version 4.0 #endif // !WINVER #if !defined(WCEOLE_ENABLE_DIALOGEX) #define DIALOGEX DIALOG DISCARDABLE #endif #include #define SHMENUBAR RCDATA #if defined(SHELLSDK_MODULES_AYGSHELL) #include #else #define NOMENU 0xFFFF #define IDS_SHNEW 1 #define IDM_SHAREDNEW 10 #define IDM_SHAREDNEWDEFAULT 11 #endif #ifndef I_IMAGENONE #define I_IMAGENONE (-2) #endif #include #endif // !_INC_WINDOWS #endif // RC_INVOKED #include "atlres.h" #ifdef APSTUDIO_INVOKED #undef APSTUDIO_HIDDEN_SYMBOLS #endif // APSTUDIO_INVOKED // Visual Studio dialog editor bug fix #ifndef DS_FIXEDSYS #define DS_FIXEDSYS 0 #endif #define IDC_INFOSTATIC 0xFFFE // == IDC_STATIC -1 /////////////////////////////////////////////////////////////////////////////// // Smartphone and PPC 2005 Resource IDs // Command and associated string resource IDs #define ID_MENU_OK 0xE790 #define ID_MENU_CANCEL 0xE791 #define ID_MENU 0xE792 #define ID_ACTION 0xE793 #define ID_VIEW_FULLSCREEN 0xE802 // MenuBar resource IDs #define ATL_IDM_MENU_DONE 0xE701 #define ATL_IDM_MENU_CANCEL 0xE702 #define ATL_IDM_MENU_DONECANCEL 0xE703 // Default device MenuBar control ID and MenuBar resource ID #define ATL_IDW_MENU_BAR 0xE802 // SmartPhone spinned controls ID offset for CSpinCtrl #define ATL_IDW_SPIN_ID 9999 #endif // __ATLRESCE_H__ ================================================ FILE: src/Setup/wtl90/atlribbon.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLRIBBON_H__ #define __ATLRIBBON_H__ #pragma once #if (_MSC_VER < 1500) #error atlribbon.h requires Visual C++ 2008 compiler or higher #endif #ifndef _UNICODE #error atlribbon.h requires the Unicode character set #endif #if !defined(NTDDI_WIN7) || (NTDDI_VERSION < NTDDI_WIN7) #error atlribbon.h requires the Windows 7 SDK or higher #endif #ifdef _WIN32_WCE #error atlribbon.h is not supported on Windows CE #endif #ifndef __ATLAPP_H__ #error atlribbon.h requires atlapp.h to be included first #endif #if (_ATL_VER < 0x0700) #include #pragma comment(lib, "shlwapi.lib") #endif #include // for RecentDocumentList classes #include // for Frame and UpdateUI classes #include // required for atlctrlw.h #include // for CCommandBarCtrl #if !defined(_WTL_USE_CSTRING) && !defined(__ATLSTR_H__) #pragma warning(push) #pragma warning(disable: 4530) // unwind semantics not enabled #include #pragma warning(pop) #endif #include #pragma comment(lib, "dwmapi.lib") #include #include #pragma comment(lib, "propsys.lib") #include // for CHARFORMAT2 /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CRibbonUpdateUI : Automatic mapping of ribbon UI elements // // RibbonUI::Text // RibbonUI::CharFormat // RibbonUI::ICtrl // RibbonUI::CtrlImpl // RibbonUI::CommandCtrlImpl // RibbonUI::ItemProperty // RibbonUI::CollectionImplBase // RibbonUI::CollectionImpl // RibbonUI::TextCollectionImpl // RibbonUI::ItemCollectionImpl // RibbonUI::ComboCollectionImpl // RibbonUI::CommandCollectionImpl // RibbonUI::ToolbarCollectionImpl // RibbonUI::SimpleCollectionImpl // RibbonUI::CollectionCtrlImpl // RibbonUI::ToolbarGalleryCtrlImpl // RibbonUI::SimpleCollectionCtrlImpl // RibbonUI::RecentItemsCtrlImpl // RibbonUI::FontCtrlImpl // RibbonUI::ColorCtrlImpl // RibbonUI::SpinnerCtrlImpl // // RibbonUI::CRibbonImpl // CRibbonImpl::CRibbonComboCtrl // CRibbonImpl::CRibbonItemGalleryCtrl // CRibbonImpl::CRibbonCommandGalleryCtrl // CRibbonImpl::CRibbonToolbarGalleryCtrl // CRibbonImpl::CRibbonSimpleComboCtrl // CRibbonImpl::CRibbonSimpleGalleryCtrl // CRibbonImpl::CRibbonRecentItemsCtrl // CRibbonImpl::CRibbonColorCtrl // CRibbonImpl::CRibbonFontCtrl // CRibbonImpl::CRibbonSpinnerCtrl // CRibbonImpl::CRibbonFloatSpinnerCtrl // CRibbonImpl::CRibbonCommandCtrl // // CRibbonFrameWindowImplBase // CRibbonFrameWindowImpl // CRibbonMDIFrameWindowImpl // CRibbonPersist // // Global functions: // RibbonUI::SetPropertyVal() // RibbonUI::GetImage() // Constants #ifndef RIBBONUI_MAX_TEXT #define RIBBONUI_MAX_TEXT 128 #endif #define TWIPS_PER_POINT 20 // For font size namespace WTL { /////////////////////////////////////////////////////////////////////////////// // CRibbonUpdateUI : Automatic mapping of ribbon UI elements template class CRibbonUpdateUI : public CAutoUpdateUI { public: enum { UPDUI_RIBBON = 0x0080, UPDUI_PERSIST = 0x0020 }; bool IsRibbonElement(const _AtlUpdateUIMap& UIMap) { return (UIMap.m_wType & UPDUI_RIBBON) != 0; } bool IsRibbonID(UINT nID) { for(int i = 0; i < m_arrUIMap.GetSize(); i++) { if(m_arrUIMap[i].m_nID == nID) return IsRibbonElement(m_arrUIMap[i]); } return false; } // Element bool UIAddRibbonElement(UINT nID) { return UIAddElement(nID); } bool UIRemoveRibbonElement(UINT nID) { return UIRemoveElement(nID); } bool UIPersistElement(UINT nID, bool bPersist = true) { return bPersist ? UIAddElement(nID) : UIRemoveElement(nID); } // methods for Ribbon elements BOOL UISetText(int nID, LPCWSTR sText, BOOL bForceUpdate = FALSE) { T* pT = static_cast(this); BOOL bRes = CUpdateUIBase::UISetText(nID, sText, bForceUpdate); if (pT->IsRibbonUI() && IsRibbonID(nID)) bRes = SUCCEEDED(pT->InvalidateProperty(nID, UI_PKEY_Label)); return bRes; } BOOL UISetText(int nID, UINT uIdResource, BOOL bForceUpdate = FALSE) { CTempBuffer sText(RIBBONUI_MAX_TEXT); int nRet = AtlLoadString(uIdResource, sText, RIBBONUI_MAX_TEXT); if(nRet > 0) UISetText(nID, sText, bForceUpdate); return (nRet > 0) ? TRUE : FALSE; } LPCTSTR UIGetText(int nID) { T* pT = static_cast(this); LPCTSTR sUI = CAutoUpdateUI::UIGetText(nID); // replace 'tab' by 'space' for RibbonUI elements if (sUI && pT->IsRibbonUI() && IsRibbonID(nID) && wcschr(sUI, L'\t')) { static WCHAR sText[RIBBONUI_MAX_TEXT] = { 0 }; wcscpy_s(sText, sUI); WCHAR* pch = wcschr(sText, L'\t'); if (pch != NULL) *pch = L' '; return sText; } else { return sUI; } } BOOL UIEnable(int nID, BOOL bEnable, BOOL bForceUpdate = FALSE) { T* pT = static_cast(this); BOOL bRes = CUpdateUIBase::UIEnable(nID, bEnable, bForceUpdate); if (pT->IsRibbonUI() && IsRibbonID(nID)) bRes = SUCCEEDED(pT->SetProperty((WORD)nID, UI_PKEY_Enabled, bEnable)); return bRes; } BOOL UISetCheck(int nID, INT nCheck, BOOL bForceUpdate = FALSE) { if ((nCheck == 0) || (nCheck == 1)) return UISetCheck(nID, nCheck != 0, bForceUpdate); else return CUpdateUIBase::UISetCheck(nID, nCheck, bForceUpdate); } BOOL UISetCheck(int nID, bool bCheck, BOOL bForceUpdate = FALSE) { T* pT = static_cast(this); BOOL bRes = CUpdateUIBase::UISetCheck(nID, bCheck, bForceUpdate); if (bRes && pT->IsRibbonUI() && IsRibbonID(nID)) bRes = SUCCEEDED(pT->SetProperty((WORD)nID, UI_PKEY_BooleanValue, bCheck)); return bRes; } }; /////////////////////////////////////////////////////////////////////////////// // RibbonUI namespace // namespace RibbonUI { // Minimal string allocation support for various PROPERTYKEY values #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) typedef _CSTRING_NS::CString Text; #else class Text : public std::wstring { public: Text(std::wstring& s) : std::wstring(s) { } Text(LPCWSTR s) : std::wstring(s) { } Text() { } bool IsEmpty() { return empty(); } operator LPCWSTR() { return c_str(); } Text& operator =(LPCWSTR s) { return static_cast(std::wstring::operator =(s)); } }; #endif // PROPERTYKEY enum and helpers enum k_KEY { // state k_Enabled = 1, k_BooleanValue = 200, // text properties k_LabelDescription = 2, k_Keytip = 3, k_Label = 4, k_TooltipDescription = 5, k_TooltipTitle = 6, // image properties k_LargeImage = 7, k_LargeHighContrastImage = 8, k_SmallImage = 9, k_SmallHighContrastImage = 10, // collection properties k_ItemsSource = 101, k_Categories = 102, k_SelectedItem = 104, // collection item properties k_CommandId = 100, k_CategoryId = 103, k_CommandType = 105, k_ItemImage = 106, // combo control property k_StringValue = 202, // spinner control properties k_DecimalValue = 201, k_MaxValue = 203, k_MinValue, k_Increment, k_DecimalPlaces, k_FormatString, k_RepresentativeString = 208, // font control properties k_FontProperties = 300, k_FontProperties_Family, k_FontProperties_Size, k_FontProperties_Bold, k_FontProperties_Italic = 304, k_FontProperties_Underline = 305, k_FontProperties_Strikethrough, k_FontProperties_VerticalPositioning, k_FontProperties_ForegroundColor = 308, k_FontProperties_BackgroundColor = 309, k_FontProperties_ForegroundColorType, k_FontProperties_BackgroundColorType, k_FontProperties_ChangedProperties = 312, k_FontProperties_DeltaSize = 313, // recent items properties k_RecentItems = 350, k_Pinned = 351, // color control properties k_Color = 400, k_ColorType = 401, k_ColorMode, k_ThemeColorsCategoryLabel = 403, k_StandardColorsCategoryLabel, k_RecentColorsCategoryLabel = 405, k_AutomaticColorLabel = 406, k_NoColorLabel = 407, k_MoreColorsLabel = 408, k_ThemeColors = 409, k_StandardColors = 410, k_ThemeColorsTooltips = 411, k_StandardColorsTooltips = 412, // Ribbon state k_Viewable = 1000, k_Minimized = 1001, k_QuickAccessToolbarDock = 1002, k_ContextAvailable = 1100, // Ribbon UI colors k_GlobalBackgroundColor = 2000, k_GlobalHighlightColor, k_GlobalTextColor = 2002 }; inline k_KEY k_(REFPROPERTYKEY key) { return (k_KEY)key.fmtid.Data1; } // PROPERTYKEY value assignment and specializations // template HRESULT SetPropertyVal(REFPROPERTYKEY key, V val, PROPVARIANT* ppv) { switch (k_(key)) { case k_Enabled: case k_BooleanValue: return InitPropVariantFromBoolean(val, ppv); default: return UIInitPropertyFromUInt32(key, val, ppv); } } inline HRESULT SetPropertyVal(REFPROPERTYKEY key, DOUBLE val, PROPVARIANT* ppv) { return SetPropertyVal(key, (LONG)val, ppv); } inline HRESULT SetPropertyVal(REFPROPERTYKEY key, IUIImage* val, PROPVARIANT* ppv) { HRESULT hr = UIInitPropertyFromImage(key, val, ppv); ATLVERIFY(val->Release() == 1); return hr; } inline HRESULT SetPropertyVal(REFPROPERTYKEY key, IUnknown* val, PROPVARIANT* ppv) { return UIInitPropertyFromInterface(key, val, ppv); } inline HRESULT SetPropertyVal(REFPROPERTYKEY key, IPropertyStore* val, PROPVARIANT* ppv) { return UIInitPropertyFromInterface(key, val, ppv); } inline HRESULT SetPropertyVal(REFPROPERTYKEY key, SAFEARRAY* val, PROPVARIANT* ppv) { return UIInitPropertyFromIUnknownArray(key, val, ppv); } inline HRESULT SetPropertyVal(REFPROPERTYKEY key, DECIMAL* val, PROPVARIANT* ppv) { return UIInitPropertyFromDecimal(key, *val, ppv); } inline HRESULT SetPropertyVal(REFPROPERTYKEY key, bool val, PROPVARIANT* ppv) { return UIInitPropertyFromBoolean(key, val, ppv); } inline HRESULT SetPropertyVal(REFPROPERTYKEY key, LPCWSTR val, PROPVARIANT* ppv) { return UIInitPropertyFromString(key, val, ppv); } // CharFormat helper struct for RibbonUI font control // struct CharFormat : CHARFORMAT2 { // Default constructor CharFormat() { cbSize = sizeof CHARFORMAT2; Reset(); } // Copy constructor CharFormat(const CharFormat& cf) { CopyMemory(this, &cf, sizeof CHARFORMAT2); } // Assign operator CharFormat& operator =(const CharFormat& cf) { CopyMemory(this, &cf, sizeof CHARFORMAT2); return (*this); } void Reset() { uValue = dwMask = dwEffects = 0; PropVariantInit(&propvar); } void operator <<(IPropertyStore* pStore) { if (pStore == NULL) { ATLASSERT(FALSE); return; } static void (CharFormat::*Getk_[])(IPropertyStore*) = { &CharFormat::Getk_Family, &CharFormat::Getk_FontProperties_Size, &CharFormat::Getk_MaskEffect, &CharFormat::Getk_MaskEffect, &CharFormat::Getk_MaskEffect, &CharFormat::Getk_MaskEffect, &CharFormat::Getk_VerticalPositioning, &CharFormat::Getk_Color, &CharFormat::Getk_Color, &CharFormat::Getk_ColorType, &CharFormat::Getk_ColorType, }; DWORD nProps = 0; Reset(); ATLVERIFY(SUCCEEDED(pStore->GetCount(&nProps))); for (DWORD iProp = 0; iProp < nProps; iProp++) { PROPERTYKEY key; ATLVERIFY(SUCCEEDED(pStore->GetAt(iProp, &key))); ATLASSERT(k_(key) >= k_FontProperties_Family); if (k_(key) <= k_FontProperties_BackgroundColorType) (this->*Getk_[k_(key) - k_FontProperties_Family])(pStore); } } void operator >>(IPropertyStore* pStore) { if (pStore == NULL) { ATLASSERT(FALSE); return; } PutFace(pStore); PutSize(pStore); PutMaskEffect(CFM_BOLD, CFE_BOLD, UI_PKEY_FontProperties_Bold, pStore); PutMaskEffect(CFM_ITALIC, CFE_ITALIC, UI_PKEY_FontProperties_Italic, pStore); PutMaskEffect(CFM_UNDERLINE, CFE_UNDERLINE, UI_PKEY_FontProperties_Underline, pStore); PutMaskEffect(CFM_STRIKEOUT, CFE_STRIKEOUT, UI_PKEY_FontProperties_Strikethrough, pStore); PutVerticalPos(pStore); PutColor(pStore); PutBackColor(pStore); } private: PROPVARIANT propvar; UINT uValue; // Getk_ functions void Getk_Family(IPropertyStore* pStore) { if (SUCCEEDED(pStore->GetValue(UI_PKEY_FontProperties_Family, &propvar))) { PropVariantToString(propvar, szFaceName, LF_FACESIZE); if (*szFaceName) dwMask |= CFM_FACE; } } void Getk_FontProperties_Size(IPropertyStore* pStore) { if (SUCCEEDED(pStore->GetValue(UI_PKEY_FontProperties_Size, &propvar))) { DECIMAL decSize = { 0 }; UIPropertyToDecimal(UI_PKEY_FontProperties_Size, propvar, &decSize); DOUBLE dSize = 0; VarR8FromDec(&decSize, &dSize); if (dSize > 0) { dwMask |= CFM_SIZE; yHeight = (LONG)(dSize * TWIPS_PER_POINT); } } } template void Getk_MaskEffect(IPropertyStore* pStore) { if (SUCCEEDED(pStore->GetValue(key, &propvar))) { UIPropertyToUInt32(key, propvar, &uValue); if ((UI_FONTPROPERTIES)uValue != UI_FONTPROPERTIES_NOTAVAILABLE) { dwMask |= t_dwMask; dwEffects |= ((UI_FONTPROPERTIES) uValue == UI_FONTPROPERTIES_SET) ? t_dwEffects : 0; } } } void Getk_VerticalPositioning(IPropertyStore* pStore) { if (SUCCEEDED(pStore->GetValue(UI_PKEY_FontProperties_VerticalPositioning, &propvar))) { UIPropertyToUInt32(UI_PKEY_FontProperties_VerticalPositioning, propvar, &uValue); UI_FONTVERTICALPOSITION uVerticalPosition = (UI_FONTVERTICALPOSITION) uValue; if ((uVerticalPosition != UI_FONTVERTICALPOSITION_NOTAVAILABLE)) { dwMask |= (CFM_SUPERSCRIPT | CFM_SUBSCRIPT); if (uVerticalPosition != UI_FONTVERTICALPOSITION_NOTSET) { dwEffects |= (uVerticalPosition == UI_FONTVERTICALPOSITION_SUPERSCRIPT) ? CFE_SUPERSCRIPT : CFE_SUBSCRIPT; } } } } template void Getk_Color(IPropertyStore* pStore) { UINT32 color = 0; if (SUCCEEDED(pStore->GetValue(key, &propvar))) { UIPropertyToUInt32(key, propvar, &color); dwMask |= t_dwMask; if (t_dwMask == CFM_COLOR) crTextColor = color; else crBackColor = color; } } template void Getk_ColorType(IPropertyStore* pStore) { if (SUCCEEDED(pStore->GetValue(key, &propvar))) { UIPropertyToUInt32(key, propvar, &uValue); if (t_type == (UI_SWATCHCOLORTYPE)uValue) { dwMask |= t_dwMask; dwEffects |= t_dwEffects; } } } // Put functions void PutMaskEffect(WORD dwMaskVal, WORD dwEffectVal, REFPROPERTYKEY key, IPropertyStore* pStore) { PROPVARIANT propvar; UI_FONTPROPERTIES uProp = UI_FONTPROPERTIES_NOTAVAILABLE; if ((dwMask & dwMaskVal) != 0) uProp = dwEffects & dwEffectVal ? UI_FONTPROPERTIES_SET : UI_FONTPROPERTIES_NOTSET; SetPropertyVal(key, uProp, &propvar); pStore->SetValue(key, propvar); } void PutVerticalPos(IPropertyStore* pStore) { PROPVARIANT propvar; UI_FONTVERTICALPOSITION uProp = UI_FONTVERTICALPOSITION_NOTAVAILABLE; if ((dwMask & CFE_SUBSCRIPT) != 0) { if ((dwMask & CFM_SUBSCRIPT) && (dwEffects & CFE_SUBSCRIPT)) uProp = UI_FONTVERTICALPOSITION_SUBSCRIPT; else uProp = UI_FONTVERTICALPOSITION_SUPERSCRIPT; } else if ((dwMask & CFM_OFFSET) != 0) { if (yOffset > 0) uProp = UI_FONTVERTICALPOSITION_SUPERSCRIPT; else if (yOffset < 0) uProp = UI_FONTVERTICALPOSITION_SUBSCRIPT; } SetPropertyVal(UI_PKEY_FontProperties_VerticalPositioning, uProp, &propvar); pStore->SetValue(UI_PKEY_FontProperties_VerticalPositioning, propvar); } void PutFace(IPropertyStore* pStore) { PROPVARIANT propvar; SetPropertyVal(UI_PKEY_FontProperties_Family, dwMask & CFM_FACE ? szFaceName : L"", &propvar); pStore->SetValue(UI_PKEY_FontProperties_Family, propvar); } void PutSize(IPropertyStore* pStore) { PROPVARIANT propvar; DECIMAL decVal; if ((dwMask & CFM_SIZE) != 0) VarDecFromR8((DOUBLE)yHeight / TWIPS_PER_POINT, &decVal); else VarDecFromI4(0, &decVal); SetPropertyVal(UI_PKEY_FontProperties_Size, &decVal, &propvar); pStore->SetValue(UI_PKEY_FontProperties_Size, propvar); } void PutColor(IPropertyStore* pStore) { if ((dwMask & CFM_COLOR) != 0) if ((dwEffects & CFE_AUTOCOLOR) == 0) { SetPropertyVal(UI_PKEY_FontProperties_ForegroundColorType, UI_SWATCHCOLORTYPE_RGB, &propvar); pStore->SetValue(UI_PKEY_FontProperties_ForegroundColorType, propvar); SetPropertyVal(UI_PKEY_FontProperties_ForegroundColor, crTextColor, &propvar); pStore->SetValue(UI_PKEY_FontProperties_ForegroundColor, propvar); } else { SetPropertyVal(UI_PKEY_FontProperties_ForegroundColorType, UI_SWATCHCOLORTYPE_AUTOMATIC, &propvar); pStore->SetValue(UI_PKEY_FontProperties_ForegroundColorType, propvar); } } void PutBackColor(IPropertyStore* pStore) { if (((dwMask & CFM_BACKCOLOR) != 0) && ((dwEffects & CFE_AUTOBACKCOLOR) == 0)) { SetPropertyVal(UI_PKEY_FontProperties_BackgroundColorType, UI_SWATCHCOLORTYPE_RGB, &propvar); pStore->SetValue(UI_PKEY_FontProperties_BackgroundColorType, propvar); SetPropertyVal(UI_PKEY_FontProperties_BackgroundColor, crBackColor, &propvar); pStore->SetValue(UI_PKEY_FontProperties_BackgroundColor, propvar); } else { SetPropertyVal(UI_PKEY_FontProperties_BackgroundColorType, UI_SWATCHCOLORTYPE_NOCOLOR, &propvar); pStore->SetValue(UI_PKEY_FontProperties_BackgroundColorType, propvar); } } }; // IUIImage helper // inline IUIImage* GetImage(HBITMAP hbm, UI_OWNERSHIP owner) { ATLASSERT(hbm); IUIImage* pIUII = NULL; ATL::CComPtr pIFB; if SUCCEEDED(pIFB.CoCreateInstance(CLSID_UIRibbonImageFromBitmapFactory)) ATLVERIFY(SUCCEEDED(pIFB->CreateImage(hbm, owner, &pIUII))); return pIUII; } /////////////////////////////////////////////////////////////////////////////// // Ribbon control classes // RibbonUI::ICtrl abstract interface of RibbonUI::CRibbonImpl and all RibbonUI control classes // struct ICtrl { virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue, IUISimplePropertySet* pCommandExecutionProperties) = 0; virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue) = 0; }; // RibbonUI::CtrlImpl base class for all ribbon controls // template class ATL_NO_VTABLE CtrlImpl : public ICtrl { protected: T* m_pWndRibbon; public: typedef T WndRibbon; CtrlImpl() : m_pWndRibbon(T::pWndRibbon) { } WndRibbon& GetWndRibbon() { return *m_pWndRibbon; } static WORD GetID() { return t_ID; } Text m_sTxt[5]; // Operations HRESULT Invalidate() { return GetWndRibbon().InvalidateCtrl(GetID()); } HRESULT Invalidate(REFPROPERTYKEY key, UI_INVALIDATIONS flags = UI_INVALIDATIONS_PROPERTY) { return GetWndRibbon().InvalidateProperty(GetID(), key, flags); } HRESULT SetText(REFPROPERTYKEY key, LPCWSTR sTxt, bool bUpdate = false) { ATLASSERT((k_(key) <= k_TooltipTitle) && (k_(key) >= k_LabelDescription)); m_sTxt[k_(key) - k_LabelDescription] = sTxt; return bUpdate ? GetWndRibbon().InvalidateProperty(GetID(), key) : S_OK; } // Implementation template HRESULT SetProperty(REFPROPERTYKEY key, V val) { return GetWndRibbon().SetProperty(GetID(), key, val); } HRESULT OnGetText(REFPROPERTYKEY key, PROPVARIANT* ppv) { ATLASSERT((k_(key) <= k_TooltipTitle) && (k_(key) >= k_LabelDescription)); const INT iText = k_(key) - k_LabelDescription; if (m_sTxt[iText].IsEmpty()) if (LPCWSTR sText = GetWndRibbon().OnRibbonQueryText(GetID(), key)) m_sTxt[iText] = sText; return !m_sTxt[iText].IsEmpty() ? SetPropertyVal(key, (LPCWSTR)m_sTxt[iText], ppv) : S_OK; } virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue, IUISimplePropertySet* pCommandExecutionProperties) { ATLASSERT(nCmdID == t_ID); return GetWndRibbon().DoExecute(nCmdID, verb, key, ppropvarValue, pCommandExecutionProperties); } virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue) { ATLASSERT(nCmdID == t_ID); const INT iMax = k_TooltipTitle - k_LabelDescription; const INT iVal = k_(key) - k_LabelDescription; return (iVal <= iMax) && (iVal >= 0) ? OnGetText(key, ppropvarNewValue) : GetWndRibbon().DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue); } }; // CommandCtrlImpl base class for most ribbon controls // template class CommandCtrlImpl : public CtrlImpl { public: CBitmap m_hbm[4]; HRESULT SetImage(REFPROPERTYKEY key, HBITMAP hbm, bool bUpdate = false) { ATLASSERT((k_(key) <= k_SmallHighContrastImage) && (k_(key) >= k_LargeImage)); m_hbm[k_(key) - k_LargeImage].Attach(hbm); return bUpdate ? GetWndRibbon().InvalidateProperty(GetID(), key) : S_OK; } HRESULT OnGetImage(REFPROPERTYKEY key, PROPVARIANT* ppv) { ATLASSERT((k_(key) <= k_SmallHighContrastImage) && (k_(key) >= k_LargeImage)); const INT iImage = k_(key) - k_LargeImage; if (m_hbm[iImage].IsNull()) m_hbm[iImage] = GetWndRibbon().OnRibbonQueryImage(GetID(), key); return m_hbm[iImage].IsNull() ? E_NOTIMPL : SetPropertyVal(key, GetImage(m_hbm[iImage], UI_OWNERSHIP_COPY), ppv); } virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue) { ATLASSERT (nCmdID == GetID()); return (k_(key) <= k_SmallHighContrastImage) && (k_(key) >= k_LargeImage) ? OnGetImage(key, ppropvarNewValue) : CtrlImpl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue); } }; /////////////////////////////////////////////////////////////////////////////// // Ribbon collection base classes // ItemProperty class: ribbon callback for each item in a collection // #pragma warning(push) #pragma warning(disable: 4512) // assignment operator could not be generated template class ItemProperty : public IUISimplePropertySet { public: ItemProperty(UINT i, TCollection* pCollection) : m_Index(i), m_pCollection(pCollection) { } const UINT m_Index; TCollection* m_pCollection; // IUISimplePropertySet method. STDMETHODIMP GetValue(REFPROPERTYKEY key, PROPVARIANT *value) { return m_pCollection->OnGetItem(m_Index, key, value); } // IUnknown methods. STDMETHODIMP_(ULONG) AddRef() { return 1; } STDMETHODIMP_(ULONG) Release() { return 1; } STDMETHODIMP QueryInterface(REFIID iid, void** ppv) { if ((iid == __uuidof(IUnknown)) || (iid == __uuidof(IUISimplePropertySet))) { *ppv = this; return S_OK; } else { return E_NOINTERFACE; } } }; #pragma warning(pop) // CollectionImplBase: base class for all RibbonUI collections // template class CollectionImplBase { typedef CollectionImplBase thisClass; public: CollectionImplBase() { for (int i = 0; i < t_size; i++) m_apItems[i] = new ItemProperty(i, static_cast(this)); } ~CollectionImplBase() { for (int i = 0; i < t_size; i++) delete m_apItems[i]; } // Data members ItemProperty* m_apItems[t_size]; }; // CollectionImpl: handles categories and collecton resizing // template class CollectionImpl : public CollectionImplBase, t_items + t_categories> { typedef CollectionImpl thisClass; public: typedef thisClass Collection; CollectionImpl() : m_size(t_items) { FillMemory(m_auItemCat, sizeof m_auItemCat, 0xff); // UI_COLLECTION_INVALIDINDEX } UINT32 m_auItemCat[t_items]; Text m_asCatName[__max(t_categories, 1)]; size_t m_size; // Operations HRESULT SetItemCategory(UINT uItem, UINT uCat, bool bUpdate = false) { ATLASSERT((uItem < t_items) && (uCat < t_categories)); m_auItemCat[uItem] = uCat; return bUpdate ? InvalidateItems() : S_OK; } HRESULT SetCategoryText(UINT uCat, LPCWSTR sText, bool bUpdate = false) { ATLASSERT(uCat < t_categories); m_asCatName[uCat] = sText; return bUpdate ? InvalidateCategories() : S_OK; } HRESULT Resize(size_t size, bool bUpdate = false) { ATLASSERT(size <= t_items); m_size = size; return bUpdate ? InvalidateItems() : S_OK; } // Implementation HRESULT OnGetItem(UINT uIndex, REFPROPERTYKEY key, PROPVARIANT *value) { ATLASSERT(uIndex < t_items + t_categories); TCtrl* pCtrl = static_cast(this); return uIndex < t_items ? pCtrl->DoGetItem(uIndex, key, value) : pCtrl->DoGetCategory(uIndex - t_items, key, value); } HRESULT DoGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value) { ATLASSERT(k_(key) == k_CategoryId); UINT32 uCat = UI_COLLECTION_INVALIDINDEX; if (t_categories != 0) { if (m_auItemCat[uItem] == UI_COLLECTION_INVALIDINDEX) { TCtrl::WndRibbon& ribbon = static_cast(this)->GetWndRibbon(); m_auItemCat[uItem] = ribbon.OnRibbonQueryItemCategory(TCtrl::GetID(), uItem); } uCat = m_auItemCat[uItem]; } return SetPropertyVal(key, uCat, value); } HRESULT DoGetCategory(UINT uCat, REFPROPERTYKEY key, PROPVARIANT *value) { HRESULT hr = S_OK; switch (k_(key)) { case k_Label: if (m_asCatName[uCat].IsEmpty()) { TCtrl::WndRibbon& ribbon = static_cast(this)->GetWndRibbon(); m_asCatName[uCat] = ribbon.OnRibbonQueryCategoryText(TCtrl::GetID(), uCat); } hr = SetPropertyVal(key, (LPCWSTR)m_asCatName[uCat], value); break; case k_CategoryId: hr = SetPropertyVal(key, uCat, value); break; default: ATLASSERT(FALSE); break; } return hr; } HRESULT InvalidateItems() { return static_cast(this)->Invalidate(UI_PKEY_ItemsSource); } HRESULT InvalidateCategories() { return static_cast(this)->Invalidate(UI_PKEY_Categories); } HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* /*ppropvarNewValue*/) { ATLASSERT(nCmdID == TCtrl::GetID()); nCmdID; // avoid level 4 warning HRESULT hr = E_NOTIMPL; switch (k_(key)) { case k_ItemsSource: { ATL::CComQIPtr pIUICollection(ppropvarCurrentValue->punkVal); ATLASSERT(pIUICollection); hr = pIUICollection->Clear(); for (UINT i = 0; i < m_size; i++) { if FAILED(hr = pIUICollection->Add(m_apItems[i])) break; } ATLASSERT(SUCCEEDED(hr)); } break; case k_Categories: if (t_categories != 0) { ATL::CComQIPtr pIUICategory(ppropvarCurrentValue->punkVal); ATLASSERT(pIUICategory.p); hr = pIUICategory->Clear(); for (UINT i = t_items; i < (t_items + t_categories); i++) { if FAILED(hr = pIUICategory->Add(m_apItems[i])) break; } ATLASSERT(SUCCEEDED(hr)); } break; } return hr; } }; // TextCollectionImpl: handles item labels and selection // template class TextCollectionImpl : public CollectionImpl { typedef TextCollectionImpl thisClass; public: typedef thisClass TextCollection; TextCollectionImpl() : m_uSelected(UI_COLLECTION_INVALIDINDEX) { } Text m_asText[t_items]; UINT m_uSelected; // Operations HRESULT SetItemText(UINT uItem, LPCWSTR sText, bool bUpdate = false) { ATLASSERT(uItem < t_items); m_asText[uItem] = sText; return bUpdate ? InvalidateItems() : S_OK; } UINT GetSelected() { return m_uSelected; } HRESULT Select(UINT uItem, bool bUpdate = false) { ATLASSERT((uItem < t_items) || (uItem == UI_COLLECTION_INVALIDINDEX)); m_uSelected = uItem; TCtrl::WndRibbon& ribbon = static_cast(this)->GetWndRibbon(); return bUpdate ? ribbon.SetProperty(TCtrl::GetID(), UI_PKEY_SelectedItem, uItem) : S_OK; } // Implementation HRESULT DoGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value) { ATLASSERT(uItem < t_items); if (k_(key) == k_Label) { if (m_asText[uItem].IsEmpty()) { TCtrl::WndRibbon& ribbon = static_cast(this)->GetWndRibbon(); m_asText[uItem] = ribbon.OnRibbonQueryItemText(TCtrl::GetID(), uItem); } return SetPropertyVal(key, (LPCWSTR)m_asText[uItem], value); } else { return Collection::DoGetItem(uItem, key, value); } } HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue) { ATLASSERT(nCmdID == TCtrl::GetID()); if (k_(key) == k_SelectedItem) { TCtrl::WndRibbon& ribbon = static_cast(this)->GetWndRibbon(); UINT uSel = UI_COLLECTION_INVALIDINDEX; if ((m_uSelected == UI_COLLECTION_INVALIDINDEX) && ribbon.OnRibbonQuerySelectedItem(TCtrl::GetID(), uSel)) m_uSelected = uSel; return SetPropertyVal(key, m_uSelected, ppropvarNewValue); } else { return Collection::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue); } } }; // ItemCollectionImpl: handles item image // template class ItemCollectionImpl : public TextCollectionImpl { typedef ItemCollectionImpl thisClass; public: typedef thisClass ItemCollection; ItemCollectionImpl() { ZeroMemory(m_aBitmap, sizeof m_aBitmap); } CBitmap m_aBitmap[t_items]; // Operations HRESULT SetItemImage(UINT uIndex, HBITMAP hbm, bool bUpdate = false) { ATLASSERT(uIndex < t_items); m_aBitmap[uIndex] = hbm; return bUpdate ? InvalidateItems() : S_OK; } // Implementation HRESULT DoGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value) { ATLASSERT(uItem < t_items); if (k_(key) == k_ItemImage) { if (m_aBitmap[uItem].IsNull()) { TCtrl::WndRibbon& ribbon = static_cast(this)->GetWndRibbon(); m_aBitmap[uItem] = ribbon.OnRibbonQueryItemImage(TCtrl::GetID(), uItem); } return m_aBitmap[uItem].IsNull() ? E_NOTIMPL : SetPropertyVal(key, GetImage(m_aBitmap[uItem], UI_OWNERSHIP_COPY), value); } else { return TextCollection::DoGetItem(uItem, key, value); } } }; // ComboCollectionImpl: handles combo text // template class ComboCollectionImpl : public ItemCollectionImpl { typedef ComboCollectionImpl thisClass; public: typedef thisClass ComboCollection; // Operations HRESULT SetComboText(LPCWSTR sText) { TCtrl::WndRibbon& ribbon = static_cast(this)->GetWndRibbon(); return ribbon.IsRibbonUI() ? ribbon.SetProperty(TCtrl::GetID(), UI_PKEY_StringValue, sText) : S_OK; } LPCWSTR GetComboText() { static WCHAR sCombo[RIBBONUI_MAX_TEXT] = { 0 }; TCtrl::WndRibbon& ribbon = static_cast(this)->GetWndRibbon(); PROPVARIANT var; if (ribbon.IsRibbonUI()) { HRESULT hr = ribbon.GetIUIFrameworkPtr()->GetUICommandProperty(TCtrl::GetID(), UI_PKEY_StringValue, &var); hr = PropVariantToString(var, sCombo, RIBBONUI_MAX_TEXT); return sCombo; } return NULL; } }; // CommandCollectionImpl: handles RibbonUI command collection controls // template class CommandCollectionImpl : public CollectionImpl { typedef CommandCollectionImpl thisClass; public: typedef thisClass CommandCollection; CommandCollectionImpl() { ZeroMemory(m_auCmd, sizeof m_auCmd); ZeroMemory(m_aCmdType, sizeof m_aCmdType); } UINT32 m_auCmd[t_items]; BYTE m_aCmdType[t_items]; // Operations HRESULT SetItemCommand(UINT uItem, UINT32 uCommandID, bool bUpdate = false) { ATLASSERT(uItem < t_items); if (uCommandID == m_auCmd[uItem]) return S_OK; TCtrl::WndRibbon& ribbon = static_cast(this)->GetWndRibbon(); m_auCmd[uItem] = uCommandID; if (uCommandID != 0) ribbon.UIAddRibbonElement(uCommandID); return bUpdate ? InvalidateItems() : S_OK; } HRESULT SetItemCommandType(UINT uItem, UI_COMMANDTYPE type, bool bUpdate = false) { ATLASSERT(uItem < t_items); m_aCmdType[uItem] = (BYTE)type; return bUpdate ? InvalidateItems() : S_OK; } // Implementation HRESULT DoGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value) { ATLASSERT(uItem < t_items); TCtrl::WndRibbon& ribbon = static_cast(this)->GetWndRibbon(); HRESULT hr = E_FAIL; switch (k_(key)) { case k_CommandId: if (m_auCmd[uItem] == 0) SetItemCommand(uItem, ribbon.OnRibbonQueryItemCommand(TCtrl::GetID(), uItem)); hr = SetPropertyVal(key, m_auCmd[uItem], value); break; case k_CommandType: if (m_aCmdType[uItem] == UI_COMMANDTYPE_UNKNOWN) SetItemCommandType(uItem, ribbon.OnRibbonQueryItemCommandType(TCtrl::GetID(), uItem)); hr = SetPropertyVal(key, UINT32(m_aCmdType[uItem]), value); break; case k_CategoryId: default: hr = Collection::DoGetItem(uItem, key, value); break; } return hr; } HRESULT Select(UINT /*uItem*/, bool /*bUpdate*/ = false) { ATLASSERT(FALSE); return S_OK; } }; // SimpleCollectionImpl: collection class for ribbon simple collection controls // template class SimpleCollectionImpl : public CollectionImplBase, t_size> { typedef SimpleCollectionImpl thisClass; public: typedef CollectionImplBase CollectionBase; typedef thisClass SimpleCollection; // Implementation HRESULT OnGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value) { ATLASSERT(uItem < t_size); TCtrl::WndRibbon& ribbon = static_cast(this)->GetWndRibbon(); HRESULT hr = E_NOTIMPL; switch (k_(key)) { case k_ItemImage: if (HBITMAP hbm = ribbon.DefRibbonQueryItemImage(TCtrl::GetID(), uItem)) hr = SetPropertyVal(key, GetImage(hbm, UI_OWNERSHIP_TRANSFER), value); break; case k_Label: if (LPCWSTR sText = ribbon.DefRibbonQueryItemText(TCtrl::GetID(), uItem)) hr = SetPropertyVal(key, (LPCWSTR)sText, value); break; case k_CommandType: hr = SetPropertyVal(key, t_CommandType, value); break; case k_CommandId: hr = SetPropertyVal(key, ribbon.DefRibbonQueryItemCommand(TCtrl::GetID(), uItem), value); break; case k_CategoryId: hr = SetPropertyVal(key, UI_COLLECTION_INVALIDINDEX, value); break; default: ATLASSERT(FALSE); break; } return hr; } }; /////////////////////////////////////////////////////////////////////////////// // Ribbon collection control classes // CollectionCtrlImpl: specializable class for ribbon collection controls // template class CollectionCtrlImpl : public CommandCtrlImpl, public TCollection { typedef CollectionCtrlImpl thisClass; public: typedef CommandCtrlImpl CommandCtrl; typedef TCollection Collection; // Implementation virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue) { ATLASSERT(nCmdID == GetID()); ATLASSERT(ppropvarNewValue); HRESULT hr = Collection::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue); if FAILED(hr) hr = CommandCtrl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue); return hr; } virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue, IUISimplePropertySet* /*pCommandExecutionProperties*/) { ATLASSERT (nCmdID == GetID()); nCmdID; // avoid level4 warning if (key == NULL) // gallery button pressed { GetWndRibbon().OnRibbonItemSelected(GetID(), UI_EXECUTIONVERB_EXECUTE, UI_COLLECTION_INVALIDINDEX); return S_OK; } ATLASSERT(k_(*key) == k_SelectedItem); ATLASSERT(ppropvarValue); HRESULT hr = S_OK; UINT32 uSel = 0xffff; hr = UIPropertyToUInt32(*key, *ppropvarValue, &uSel); if (SUCCEEDED(hr)) { if (GetWndRibbon().OnRibbonItemSelected(GetID(), verb, uSel)) TCollection::Select(uSel); } return hr; } }; // ToolbarGalleryCtrlImpl: base class for ribbon toolbar gallery controls // template class ToolbarGalleryCtrlImpl : public CollectionCtrlImpl, t_size>> { public: ToolbarGalleryCtrlImpl() { CResource tbres; ATLVERIFY(tbres.Load(RT_TOOLBAR, t_idTB)); _AtlToolBarData* pData = (_AtlToolBarData*)tbres.Lock(); ATLASSERT(pData); ATLASSERT(pData->wVersion == 1); WORD* pItems = pData->items(); INT j = 0; for (int i = 0; (i < pData->wItemCount) && (j < t_size); i++) { if (pItems[i] != 0) { m_aCmdType[j] = UI_COMMANDTYPE_ACTION; m_auCmd[j++] = pItems[i]; } } if (j < t_size) Resize(j); } HRESULT DoGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value) { ATLASSERT(uItem < m_size); ATLASSERT(m_auCmd[uItem]); HRESULT hr = E_FAIL; switch (k_(key)) { case k_CommandId: hr = SetPropertyVal(key, m_auCmd[uItem], value); break; case k_CommandType: hr = SetPropertyVal(key, UINT32(m_aCmdType[uItem]), value); break; case k_CategoryId: hr = SetPropertyVal(key, UI_COLLECTION_INVALIDINDEX, value); break; default: ATLASSERT(FALSE); break; } return hr; } }; // SimpleCollectionCtrlImpl: base class for simple gallery and listbox controls // template class SimpleCollectionCtrlImpl : public CommandCtrlImpl, public SimpleCollectionImpl, t_size, t_CommandType> { typedef SimpleCollectionCtrlImpl thisClass; public: typedef thisClass SimpleCollection; SimpleCollectionCtrlImpl() : m_uSelected(0) { } UINT m_uSelected; HRESULT Select(UINT uItem, bool bUpdate = false) { ATLASSERT((uItem < t_size) || (uItem == UI_COLLECTION_INVALIDINDEX)); m_uSelected = uItem; return bUpdate ? GetWndRibbon().SetProperty(GetID(), UI_PKEY_SelectedItem, uItem) : S_OK; } // Implementation virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue) { ATLASSERT(nCmdID == GetID()); ATLASSERT(ppropvarNewValue != NULL); HRESULT hr = S_OK; switch (k_(key)) { case k_ItemsSource: { ATL::CComQIPtr pIUICollection(ppropvarCurrentValue->punkVal); ATLASSERT(pIUICollection.p); hr = pIUICollection->Clear(); for (UINT i = 0; i < t_size; i++) { if FAILED(hr = pIUICollection->Add(m_apItems[i])) break; } ATLASSERT(SUCCEEDED(hr)); } break; case k_SelectedItem: hr = SetPropertyVal(UI_PKEY_SelectedItem, m_uSelected, ppropvarNewValue); break; default: hr = CommandCtrlImpl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue); break; } return hr; } virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue, IUISimplePropertySet* /*pCommandExecutionProperties*/) { ATLASSERT (nCmdID == GetID()); nCmdID; // avoid level 4 warning HRESULT hr = S_OK; if (key == NULL) // gallery button pressed { GetWndRibbon().OnRibbonItemSelected(GetID(), UI_EXECUTIONVERB_EXECUTE, UI_COLLECTION_INVALIDINDEX); return hr; } ATLASSERT(k_(*key) == k_SelectedItem); ATLASSERT(ppropvarValue); if SUCCEEDED(hr = UIPropertyToUInt32(*key, *ppropvarValue, &m_uSelected)) GetWndRibbon().OnRibbonItemSelected(GetID(), verb, m_uSelected); return hr; } }; // RecentItemsCtrlImpl // template class RecentItemsCtrlImpl : public CtrlImpl, public CollectionImplBase, TDocList::m_nMaxEntries_Max>, public TDocList { typedef RecentItemsCtrlImpl thisClass; public: typedef thisClass RecentItems; // Implementation HRESULT OnGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value) { ATLASSERT((INT)uItem < GetMaxEntries()); LPCWSTR sPath = m_arrDocs[uItem].szDocName; HRESULT hr = E_NOTIMPL; switch (k_(key)) { case k_Label: hr = SetPropertyVal(key, GetWndRibbon().OnRibbonQueryRecentItemName(sPath), value); break; case k_LabelDescription: hr = SetPropertyVal(key, sPath, value); break; default: ATLASSERT(FALSE); break; } return hr; } virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue) { ATLASSERT(nCmdID == GetID()); ATLASSERT(ppropvarNewValue); HRESULT hr = S_OK; switch (k_(key)) { case k_RecentItems: if (SAFEARRAY* psa = SafeArrayCreateVector(VT_UNKNOWN, 0, m_arrDocs.GetSize())) { const int iLastIndex = m_arrDocs.GetSize() - 1; for (LONG i = 0; i <= iLastIndex; i++) SafeArrayPutElement(psa, &i, m_apItems[iLastIndex - i]); // reverse order hr = SetPropertyVal(key, psa, ppropvarNewValue); SafeArrayDestroy(psa); } break; default: hr = CtrlImpl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue); break; } return hr; } virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue, IUISimplePropertySet* /*pCommandExecutionProperties*/) { ATLASSERT(nCmdID == GetID()); nCmdID; // avoid level 4 warning ATLASSERT(verb == UI_EXECUTIONVERB_EXECUTE); verb; // avoid level 4 warning ATLASSERT((key) && (k_(*key) == k_SelectedItem)); ATLASSERT(ppropvarValue); UINT32 uSel = 0xffff; HRESULT hr = UIPropertyToUInt32(*key, *ppropvarValue, &uSel); if SUCCEEDED(hr) { ATLASSERT(uSel < (UINT)GetMaxEntries()); GetWndRibbon().DefCommandExecute(ID_FILE_MRU_FIRST + uSel); } return hr; } }; /////////////////////////////////////////////////////////////////////////////// // Ribbon stand-alone control classes // FontCtrlImpl // template class FontCtrlImpl : public CtrlImpl { public: CharFormat m_cf; // Implementation virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue, IUISimplePropertySet* pCommandExecutionProperties) { ATLASSERT (nCmdID == GetID()); nCmdID; // avoid level 4 warning ATLASSERT ((key) && (k_(*key) == k_FontProperties)); key; // avoid level 4 warning HRESULT hr = E_INVALIDARG; switch (verb) { case UI_EXECUTIONVERB_PREVIEW: case UI_EXECUTIONVERB_EXECUTE: ATLASSERT(pCommandExecutionProperties); PROPVARIANT propvar; if (SUCCEEDED(hr = pCommandExecutionProperties->GetValue(UI_PKEY_FontProperties_ChangedProperties, &propvar))) m_cf << ATL::CComQIPtr(propvar.punkVal); break; case UI_EXECUTIONVERB_CANCELPREVIEW: ATLASSERT(ppropvarValue); ATL::CComPtr pStore; if (SUCCEEDED(hr = UIPropertyToInterface(UI_PKEY_FontProperties, *ppropvarValue, &pStore))) m_cf << pStore; break; } if (SUCCEEDED(hr)) GetWndRibbon().OnRibbonFontCtrlExecute(GetID(), verb, &m_cf); else ATLASSERT(FALSE); return hr; } virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue) { if ((k_(key) == k_FontProperties) && (GetWndRibbon().OnRibbonQueryFont(t_ID, m_cf))) { ATL::CComQIPtr pStore(ppropvarCurrentValue->punkVal); m_cf >> pStore; return SetPropertyVal(key, pStore.p, ppropvarNewValue); } else { return CtrlImpl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue); } } }; // ColorCtrlImpl // template class ColorCtrlImpl : public CommandCtrlImpl { public: ColorCtrlImpl() : m_colorType(UI_SWATCHCOLORTYPE_NOCOLOR), m_color(0x800080) /*MAGENTA*/ { } COLORREF m_color; UINT32 m_colorType; // value in UI_SWATCHCOLORTYPE Text m_sLabels[6]; // k_MoreColorsLabel to k_ThemeColorsCategoryLabel ATL::CSimpleArray m_aColors[2]; ATL::CSimpleArray m_aTooltips[2]; // Operations HRESULT SetColor(COLORREF color, bool bUpdate = false) { if (m_colorType != UI_SWATCHCOLORTYPE_RGB) SetColorType(UI_SWATCHCOLORTYPE_RGB, bUpdate); m_color = color; return bUpdate ? SetProperty(UI_PKEY_Color, color) : S_OK; } HRESULT SetColorType(UI_SWATCHCOLORTYPE type, bool bUpdate = false) { m_colorType = type; return bUpdate ? SetProperty(UI_PKEY_ColorType, type) : S_OK; } HRESULT SetColorLabel(REFPROPERTYKEY key, LPCWSTR sLabel, bool bUpdate = false) { ATLASSERT((k_(key) >= k_ThemeColorsCategoryLabel) && (k_(key) <= k_MoreColorsLabel)); m_sLabels[k_(key) - k_ThemeColorsCategoryLabel] = sLabel; return bUpdate ? SetProperty(key, sLabel) : S_OK; } HRESULT SetColorArray(REFPROPERTYKEY key, COLORREF* pColor, bool bUpdate = false) { ATLASSERT((k_(key) == k_ThemeColors) || (k_(key) == k_StandardColors)); const INT ic = k_(key) - k_ThemeColors; m_aColors[ic].RemoveAll(); while (*pColor != 0x800080) /*MAGENTA*/ m_aColors[ic].Add(*pColor++); if (bUpdate) { PROPVARIANT var; if SUCCEEDED(InitPropVariantFromUInt32Vector(m_aColors[ic].GetData(), m_aColors[ic].GetSize(), &var)) return SetProperty(key, var); else return E_INVALIDARG; } else { return S_OK; } } HRESULT SetColorTooltips(REFPROPERTYKEY key, LPCWSTR* ppsTT, bool bUpdate = false) { ATLASSERT((k_(key) == k_ThemeColorsTooltips) || (k_(key) == k_StandardColorsTooltips)); const INT ic = k_(key) - k_ThemeColorsTooltips; m_aTooltips[ic].RemoveAll(); while (*ppsTT) m_aTooltips[ic].Add(*ppsTT++); if (bUpdate) { PROPVARIANT var; if SUCCEEDED(InitPropVariantFromStringVector(m_aTooltips[ic].GetData(), m_aTooltips[ic].GetSize(), &var)) return SetProperty(key, var); else return E_INVALIDARG; } else { return S_OK; } } // Implementation virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue, IUISimplePropertySet* pCommandExecutionProperties) { ATLASSERT (nCmdID == GetID()); nCmdID; // avoid level 4 warning ATLASSERT (key && (k_(*key) == k_ColorType)); key; // avoid level 4 warning ATLASSERT (ppropvarValue); HRESULT hr = PropVariantToUInt32(*ppropvarValue, &m_colorType); ATLASSERT(SUCCEEDED(hr)); if (SUCCEEDED(hr) && (m_colorType == UI_SWATCHCOLORTYPE_RGB)) { ATLASSERT(pCommandExecutionProperties); PROPVARIANT var; if SUCCEEDED(hr = pCommandExecutionProperties->GetValue(UI_PKEY_Color, &var)) hr = PropVariantToUInt32(var, &m_color); } if SUCCEEDED(hr) GetWndRibbon().OnRibbonColorCtrlExecute(GetID(), verb, (UI_SWATCHCOLORTYPE)m_colorType/*uType*/, m_color); else ATLASSERT(FALSE); // something was wrong return hr; } virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue) { ATLASSERT (nCmdID == GetID()); HRESULT hr = E_NOTIMPL; switch (k_(key)) { case k_ColorType: hr = SetPropertyVal(key, m_colorType, ppropvarNewValue); break; case k_Color: if (m_color == 0x800080) /*MAGENTA*/ m_color = GetWndRibbon().OnRibbonQueryColor(GetID()); hr = SetPropertyVal(key, m_color, ppropvarNewValue); break; case k_ColorMode: break; case k_ThemeColorsCategoryLabel: case k_StandardColorsCategoryLabel: case k_RecentColorsCategoryLabel: case k_AutomaticColorLabel: case k_NoColorLabel: case k_MoreColorsLabel: { const UINT iLabel = k_(key) - k_ThemeColorsCategoryLabel; if (m_sLabels[iLabel].IsEmpty()) if (LPCWSTR psLabel = GetWndRibbon().OnRibbonQueryColorLabel(GetID(), key)) m_sLabels[iLabel] = psLabel; if (!m_sLabels[iLabel].IsEmpty()) hr = SetPropertyVal(key, (LPCWSTR)m_sLabels[iLabel], ppropvarNewValue); } break; case k_ThemeColors: case k_StandardColors: { const INT ic = k_(key) - k_ThemeColors; if (!m_aColors[ic].GetSize()) if (COLORREF* pColor = GetWndRibbon().OnRibbonQueryColorArray(GetID(), key)) SetColorArray(key, pColor); if (INT iMax = m_aColors[ic].GetSize()) hr = InitPropVariantFromUInt32Vector(m_aColors[ic].GetData(), iMax, ppropvarNewValue); } break; case k_ThemeColorsTooltips: case k_StandardColorsTooltips: { const INT ic = k_(key) - k_ThemeColorsTooltips; if (m_aTooltips[ic].GetSize() == 0) if (LPCWSTR* ppsTT = GetWndRibbon().OnRibbonQueryColorTooltips(GetID(), key)) SetColorTooltips(key, ppsTT); if (INT iMax = m_aTooltips[ic].GetSize()) hr = InitPropVariantFromStringVector(m_aTooltips[ic].GetData(), iMax, ppropvarNewValue); } break; default: hr = CommandCtrlImpl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue); break; } return hr; } }; // SpinnerCtrlImpl // template class SpinnerCtrlImpl : public CtrlImpl { public: SpinnerCtrlImpl() { m_Values[0] = m_Values[2] = m_Values[4] = 0; m_Values[1] = 100; m_Values[3] = 1; } V m_Values[5]; // k_DecimalValue = 201, k_MaxValue = 203, k_MinValue, k_Increment, k_DecimalPlaces Text m_FormatString; Text m_RepresentativeString; // Operations HRESULT SetDecimalPlaces(V vPlaces, bool bUpdate = false) { return SetValue(UI_PKEY_DecimalPlaces, vPlaces, bUpdate); } HRESULT SetMin(V vMin, bool bUpdate = false) { return SetValue(UI_PKEY_MinValue, vMin, bUpdate); } HRESULT SetMax(V vMax, bool bUpdate = false) { return SetValue(UI_PKEY_MaxValue, vMax, bUpdate); } HRESULT SetVal(V vVal, bool bUpdate = false) { return SetValue(UI_PKEY_DecimalValue, vVal, bUpdate); } HRESULT SetIncrement(V vIncrement, bool bUpdate = false) { return SetValue(UI_PKEY_Increment, vIncrement, bUpdate); } HRESULT SetFormatString(LPCWSTR sFormat, bool bUpdate = false) { return SetText(UI_PKEY_FormatString, sFormat, bUpdate); } HRESULT SetRepresentativeString(LPCWSTR sRepresentative, bool bUpdate = false) { return SetText(UI_PKEY_RepresentativeString, sRepresentative, bUpdate); } // Implementation HRESULT SetText(REFPROPERTYKEY key, LPCWSTR sText, bool bUpdate = false) { switch (k_(key)) { case k_FormatString: m_FormatString = sText; break; case k_RepresentativeString: m_RepresentativeString = sText; break; default: return CtrlImpl::SetText(key, sText, bUpdate); } return bUpdate ? GetWndRibbon().InvalidateProperty(GetID(), key) : S_OK; } HRESULT SetValue(REFPROPERTYKEY key, V val, bool bUpdate = false) { ATLASSERT((k_(key) <= k_DecimalPlaces) && (k_(key) >= k_DecimalValue)); const INT iVal = k_(key) == k_DecimalValue ? 0 : k_(key) - k_StringValue; m_Values[iVal] = val; if (bUpdate) { if(k_(key) == k_DecimalValue) { DECIMAL decVal; InitDecimal(val, &decVal); return SetProperty(key, &decVal); } else { return GetWndRibbon().InvalidateProperty(GetID(), key); } } else { return S_OK; } } HRESULT QueryValue(REFPROPERTYKEY key, LONG* plVal) { return GetWndRibbon().OnRibbonQuerySpinnerValue(GetID(), key, plVal) ? S_OK : S_FALSE; } HRESULT QueryValue(REFPROPERTYKEY key, DOUBLE* pdVal) { return GetWndRibbon().OnRibbonQueryFloatSpinnerValue(GetID(), key, pdVal) ? S_OK : S_FALSE; } HRESULT OnGetValue(REFPROPERTYKEY key, PROPVARIANT* ppv) { ATLASSERT((k_(key) <= k_DecimalPlaces) && (k_(key) >= k_DecimalValue)); const INT iVal = k_(key) == k_DecimalValue ? 0 : k_(key) - k_StringValue; QueryValue(key, m_Values + iVal); if (k_(key) == k_DecimalPlaces) { return SetPropertyVal(key, m_Values[iVal], ppv); } else { DECIMAL decVal; InitDecimal(m_Values[iVal], &decVal); return SetPropertyVal(key, &decVal, ppv); } } HRESULT OnGetText(REFPROPERTYKEY key, Text& sVal, PROPVARIANT* ppv) { if (LPCWSTR sNew = GetWndRibbon().OnRibbonQueryText(GetID(), key)) sVal = sNew; return SetPropertyVal(key, (LPCWSTR)sVal, ppv); } virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue, IUISimplePropertySet* /*pCommandExecutionProperties*/) { ATLASSERT (nCmdID == GetID()); nCmdID; // avoid level 4 warning ATLASSERT (key && (k_(*key) == k_DecimalValue)); key; // avoid level 4 warning ATLASSERT (verb == UI_EXECUTIONVERB_EXECUTE); verb; // avoid level 4 warning DECIMAL decVal; HRESULT hr = UIPropertyToDecimal(UI_PKEY_DecimalValue, *ppropvarValue, &decVal); hr = InitVal(m_Values[0], &decVal); GetWndRibbon().OnRibbonSpinnerCtrlExecute(GetID(), &m_Values[0]); return hr; } virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue) { ATLASSERT (nCmdID == GetID()); HRESULT hr = E_NOTIMPL; switch (k_(key)) { case k_DecimalPlaces: case k_DecimalValue: case k_Increment: case k_MaxValue: case k_MinValue: hr = OnGetValue(key, ppropvarNewValue); break; case k_FormatString: if (m_FormatString.IsEmpty()) return OnGetText(key, m_FormatString, ppropvarNewValue); break; case k_RepresentativeString: if (m_RepresentativeString.IsEmpty()) return OnGetText(key, m_RepresentativeString, ppropvarNewValue); break; default: hr = CtrlImpl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue); break; } return hr; } // decimal conversion helpers static HRESULT InitDecimal(LONG& val, DECIMAL* pDecimal) { return ::VarDecFromI4(val, pDecimal); } static HRESULT InitDecimal(DOUBLE& val, DECIMAL* pDecimal) { return ::VarDecFromR8(val, pDecimal); } static HRESULT InitVal(LONG& val, const DECIMAL* pDecimal) { return ::VarI4FromDec(pDecimal, &val); } static HRESULT InitVal(DOUBLE& val, const DECIMAL* pDecimal) { return ::VarR8FromDec(pDecimal, &val); } }; // CRibbonImpl Ribbon implementation class // template class CRibbonImpl : public CRibbonUpdateUI, public ICtrl, public IUIApplication, public IUICommandHandler { typedef CRibbonImpl thisClass; public: typedef thisClass Ribbon; typedef T WndRibbon; CRibbonImpl() : m_bRibbonUI(false), m_hgRibbonSettings(NULL) { #ifdef _DEBUG m_cRef = 1; #endif pWndRibbon = static_cast(this); HRESULT hr = ::CoInitialize(NULL); if(SUCCEEDED(hr)) if (RunTimeHelper::IsRibbonUIAvailable()) hr = m_pIUIFramework.CoCreateInstance(CLSID_UIRibbonFramework); else ATLTRACE2(atlTraceUI, 0, _T("Ribbon UI not available\n")); if FAILED(hr) ATLTRACE2(atlTraceUI, 0, _T("Ribbon construction failed\n")); ATLASSERT(SUCCEEDED(hr)); } ~CRibbonImpl() { ::GlobalFree(m_hgRibbonSettings); m_pIUIFramework.Release(); ::CoUninitialize(); } ICtrl& GetRibbonCtrl(UINT) { return static_cast(*this); } ATL::CComPtr m_pIUIFramework; HGLOBAL m_hgRibbonSettings; bool m_bRibbonUI; bool IsRibbonUI() { return m_bRibbonUI; } IUIFramework* GetIUIFrameworkPtr() { return m_pIUIFramework; } template I* GetRibbonViewPtr(UINT32 uID) { ATLASSERT(m_pIUIFramework); ATL::CComPtr pI; return m_pIUIFramework->GetView(uID, __uuidof(I), (void**) &pI) == S_OK ? pI : NULL; } IUIRibbon* GetRibbonPtr() { return GetRibbonViewPtr(0); } IUIContextualUI* GetMenuPtr(UINT32 uID) { ATLASSERT(uID); return GetRibbonViewPtr(uID); } UINT GetRibbonHeight() { ATLASSERT(IsRibbonUI()); UINT32 cy = 0; if (ATL::CComPtr pIUIRibbon = GetRibbonPtr()) pIUIRibbon->GetHeight(&cy); return cy; } HRESULT CreateRibbon(LPCWSTR sResName = L"APPLICATION_RIBBON") { T* pT = static_cast(this); ATLASSERT(GetIUIFrameworkPtr() && !IsRibbonUI()); ATLASSERT(pT->IsWindow()); HRESULT hr = m_pIUIFramework->Initialize(pT->m_hWnd, this); if (hr == S_OK) hr = m_pIUIFramework->LoadUI(ModuleHelper::GetResourceInstance(), sResName); return hr; } HRESULT DestroyRibbon() { T* pT = static_cast(this); ATLASSERT(GetIUIFrameworkPtr() && IsRibbonUI()); ATLASSERT(pT->IsWindow()); HRESULT hRes = m_pIUIFramework->Destroy(); if (!RunTimeHelper::IsWin7()) pT->SetWindowRgn(NULL, TRUE); // Vista Basic bug workaround return hRes; } // Ribbon persistency HRESULT operator >>(IStream* pIStream) { ATLASSERT(GetIUIFrameworkPtr()); ATLASSERT(pIStream); HRESULT hr = E_FAIL; if (ATL::CComPtr pIUIRibbon = GetRibbonPtr()) { const LARGE_INTEGER li0 = { 0 }; pIStream->Seek(li0, STREAM_SEEK_SET, NULL); hr = pIUIRibbon->SaveSettingsToStream(pIStream); pIStream->Commit(STGC_DEFAULT); } return hr; } HRESULT operator <<(IStream* pIStream) { ATLASSERT(GetIUIFrameworkPtr()); ATLASSERT(pIStream); HRESULT hr = E_FAIL; if (ATL::CComPtr pIUIRibbon = GetRibbonPtr()) { const LARGE_INTEGER li0 = { 0 }; pIStream->Seek(li0, STREAM_SEEK_SET, NULL); hr = pIUIRibbon->LoadSettingsFromStream(pIStream); } return hr; } void ResetRibbonSettings() { if (m_hgRibbonSettings != NULL) { ::GlobalFree(m_hgRibbonSettings); m_hgRibbonSettings = NULL; } } HRESULT SaveRibbonSettings() { ATLASSERT(GetIUIFrameworkPtr()); ATLASSERT(static_cast(this)->IsWindow()); HRESULT hr = E_FAIL; ATL::CComPtr pIStream; if SUCCEEDED(hr = ::CreateStreamOnHGlobal(m_hgRibbonSettings, FALSE, &pIStream)) hr = *this >> pIStream; if (SUCCEEDED(hr) && (m_hgRibbonSettings == NULL)) hr = ::GetHGlobalFromStream(pIStream, &m_hgRibbonSettings); if FAILED(hr) ResetRibbonSettings(); return hr; } HRESULT RestoreRibbonSettings() { ATLASSERT(GetIUIFrameworkPtr()); ATLASSERT(m_hgRibbonSettings); ATLASSERT(static_cast(this)->IsWindow()); HRESULT hr = E_FAIL; ATL::CComPtr pIStream; if SUCCEEDED(hr = ::CreateStreamOnHGlobal(m_hgRibbonSettings, FALSE, &pIStream)) hr = *this << pIStream; if FAILED(hr) ResetRibbonSettings(); return hr; } // QAT dock states UI_CONTROLDOCK GetQATDock() { ATLASSERT(GetIUIFrameworkPtr()); ATLASSERT(IsRibbonUI()); UINT32 uDock = 0; PROPVARIANT propvar; ATL::CComQIPtrpIPS(GetRibbonPtr()); if ((pIPS != NULL) && SUCCEEDED(pIPS->GetValue(UI_PKEY_QuickAccessToolbarDock, &propvar)) && SUCCEEDED(UIPropertyToUInt32(UI_PKEY_QuickAccessToolbarDock, propvar, &uDock))) return (UI_CONTROLDOCK)uDock; ATLASSERT(FALSE); // something was wrong return (UI_CONTROLDOCK)0; } bool SetQATDock(UI_CONTROLDOCK dockState) { ATLASSERT(GetIUIFrameworkPtr()); ATLASSERT(IsRibbonUI()); PROPVARIANT propvar; ATLVERIFY(SUCCEEDED(SetPropertyVal(UI_PKEY_QuickAccessToolbarDock, dockState, &propvar))); ATL::CComQIPtrpIPS(GetRibbonPtr()); if ((pIPS != NULL) && SUCCEEDED(pIPS->SetValue(UI_PKEY_QuickAccessToolbarDock, propvar))) { pIPS->Commit(); return true; } ATLASSERT(FALSE); // something was wrong return false; } // Ribbon display states bool GetRibbonDisplayState(REFPROPERTYKEY key) { ATLASSERT(GetIUIFrameworkPtr()); ATLASSERT(IsRibbonUI()); ATLASSERT((k_(key) == k_Viewable) || (k_(key) == k_Minimized)); PROPVARIANT propvar; ATL::CComQIPtrpIPS(GetRibbonPtr()); if ((pIPS != NULL) && SUCCEEDED(pIPS->GetValue(key, &propvar))) { BOOL bState = FALSE; if SUCCEEDED(UIPropertyToBoolean(key, propvar, &bState)) return (bState != FALSE); } ATLASSERT(FALSE); // something was wrong return false; } bool SetRibbonDisplayState(REFPROPERTYKEY key, bool bState = true) { ATLASSERT(GetIUIFrameworkPtr()); ATLASSERT(IsRibbonUI()); ATLASSERT((k_(key) == k_Viewable) || (k_(key) == k_Minimized)); PROPVARIANT propvar; ATLVERIFY(SUCCEEDED(SetPropertyVal(key, bState, &propvar))); ATL::CComQIPtrpIPS(GetRibbonPtr()); if ((pIPS != NULL) && SUCCEEDED(pIPS->SetValue(key, propvar))) { pIPS->Commit(); return true; } ATLASSERT(FALSE); // something was wrong return false; } bool IsRibbonMinimized() { return GetRibbonDisplayState(UI_PKEY_Minimized); } bool MinimizeRibbon(bool bMinimize = true) { return SetRibbonDisplayState(UI_PKEY_Minimized, bMinimize); } bool IsRibbonHidden() { return !GetRibbonDisplayState(UI_PKEY_Viewable); } bool HideRibbon(bool bHide = true) { return SetRibbonDisplayState(UI_PKEY_Viewable, !bHide); } // Ribbon colors UI_HSBCOLOR GetRibbonColor(REFPROPERTYKEY key) { ATLASSERT(GetIUIFrameworkPtr()); ATLASSERT(IsRibbonUI()); ATLASSERT((k_(key) >= k_GlobalBackgroundColor) && (k_(key) <= k_GlobalTextColor)); PROPVARIANT propvar; ATL::CComQIPtrpIPS(GetIUIFrameworkPtr()); if ((pIPS != NULL) && SUCCEEDED(pIPS->GetValue(key, &propvar))) { UINT32 color = 0; if SUCCEEDED(UIPropertyToUInt32(key, propvar, &color)) return color; } ATLASSERT(FALSE); // something was wrong return 0; } bool SetRibbonColor(REFPROPERTYKEY key, UI_HSBCOLOR color) { ATLASSERT(GetIUIFrameworkPtr()); ATLASSERT(IsRibbonUI()); ATLASSERT((k_(key) >= k_GlobalBackgroundColor) && (k_(key) <= k_GlobalTextColor)); PROPVARIANT propvar; ATLVERIFY(SUCCEEDED(SetPropertyVal(key, color, &propvar))); ATL::CComQIPtrpIPS(GetIUIFrameworkPtr()); if ((pIPS != NULL) && SUCCEEDED(pIPS->SetValue(key, propvar))) { pIPS->Commit(); return true; } ATLASSERT(FALSE); // something was wrong return false; } // Ribbon modes HRESULT SetRibbonModes(INT32 iModes) { ATLASSERT(IsRibbonUI()); return GetIUIFrameworkPtr()->SetModes(iModes); } // Ribbon contextual tab UI_CONTEXTAVAILABILITY GetRibbonContextAvail(UINT32 uID) { ATLASSERT(GetIUIFrameworkPtr()); PROPVARIANT propvar; if (IsRibbonUI() && SUCCEEDED(GetIUIFrameworkPtr()->GetUICommandProperty(uID, UI_PKEY_ContextAvailable, &propvar))) { UINT uav; if (SUCCEEDED(PropVariantToUInt32(propvar, &uav))) { CUpdateUIBase::UIEnable(uID, uav != UI_CONTEXTAVAILABILITY_NOTAVAILABLE); CUpdateUIBase::UISetCheck(uID, uav == UI_CONTEXTAVAILABILITY_ACTIVE); return (UI_CONTEXTAVAILABILITY)uav; } } return UI_CONTEXTAVAILABILITY_NOTAVAILABLE; } HRESULT SetRibbonContextAvail(UINT32 uID, UI_CONTEXTAVAILABILITY cav) { CUpdateUIBase::UIEnable(uID, cav != UI_CONTEXTAVAILABILITY_NOTAVAILABLE); CUpdateUIBase::UISetCheck(uID, cav == UI_CONTEXTAVAILABILITY_ACTIVE); return SetProperty((WORD)uID, UI_PKEY_ContextAvailable, UINT32(cav)); } // Ribbon context menu bool HasRibbonMenu(UINT32 uID) { ATL::CComPtr pI = GetMenuPtr(uID); return pI != NULL; } HRESULT TrackRibbonMenu(UINT32 uID, INT32 x, INT32 y) { ATLASSERT(HasRibbonMenu(uID)); return IsRibbonUI() ? ATL::CComPtr(GetMenuPtr(uID))->ShowAtLocation(x, y) : E_FAIL; } HRESULT TrackRibbonMenu(UINT32 uID, LPARAM lParam) { return TrackRibbonMenu(uID, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); } // Overrideables HBITMAP OnRibbonQueryImage(UINT nCmdID, REFPROPERTYKEY /*key*/) { return DefRibbonQueryImage(nCmdID); } LPCWSTR OnRibbonQueryText(UINT nCmdID, REFPROPERTYKEY key) { return DefRibbonQueryText(nCmdID, key); } bool OnRibbonQueryState(UINT nCmdID, REFPROPERTYKEY key) { return DefRibbonQueryState(nCmdID, key); } UI_CONTEXTAVAILABILITY OnRibbonQueryTabAvail(UINT nCmdID) { DWORD dwState = UIGetState(nCmdID); return ((dwState & UPDUI_DISABLED) == UPDUI_DISABLED) ? UI_CONTEXTAVAILABILITY_NOTAVAILABLE : (((dwState & UPDUI_CHECKED) == UPDUI_CHECKED) ? UI_CONTEXTAVAILABILITY_ACTIVE : UI_CONTEXTAVAILABILITY_AVAILABLE); } LPCWSTR OnRibbonQueryComboText(UINT32 /*uCtrlID*/) { return NULL; } LPCWSTR OnRibbonQueryCategoryText(UINT32 /*uCtrlID*/, UINT32 /*uCat*/) { return L"Category"; } UINT32 OnRibbonQueryItemCategory(UINT32 /*uCtrlID*/, UINT32 /*uItem*/) { return 0; } LPCWSTR OnRibbonQueryItemText(UINT32 uCtrlID, UINT32 uItem) { return DefRibbonQueryItemText(uCtrlID, uItem); } bool OnRibbonQuerySelectedItem(UINT32 /*uCtrlID*/, UINT32& /*uSel*/) { return false; } HBITMAP OnRibbonQueryItemImage(UINT32 uCtrlID, UINT32 uItem) { return DefRibbonQueryItemImage(uCtrlID, uItem); } UINT32 OnRibbonQueryItemCommand(UINT32 uCtrlID, UINT32 uItem) { return DefRibbonQueryItemCommand(uCtrlID, uItem); } UI_COMMANDTYPE OnRibbonQueryItemCommandType(UINT32 /*uCtrlID*/, UINT32 /*uItem*/) { return UI_COMMANDTYPE_ACTION; } LPCWSTR OnRibbonQueryRecentItemName(LPCWSTR sPath) { return ::PathFindFileName(sPath); } bool OnRibbonQueryFont(UINT /*nId*/, CHARFORMAT2& /*cf*/) { return false; } bool OnRibbonQuerySpinnerValue(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/, LONG* /*pVal*/) { return false; } bool OnRibbonQueryFloatSpinnerValue(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/, DOUBLE* /*pVal*/) { return false; } COLORREF OnRibbonQueryColor(UINT /*nCmdID*/) { return 0x800080; /*MAGENTA*/ } LPCWSTR OnRibbonQueryColorLabel(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/) { return NULL; } COLORREF* OnRibbonQueryColorArray(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/) { return NULL; } LPCWSTR* OnRibbonQueryColorTooltips(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/) { return NULL; } bool OnRibbonItemSelected(UINT32 uCtrlID, UI_EXECUTIONVERB verb, UINT32 uItem) { DefCommandExecute(MAKELONG(uCtrlID, verb), uItem); return true; } void OnRibbonColorCtrlExecute(UINT32 uCtrlID, UI_EXECUTIONVERB verb, UI_SWATCHCOLORTYPE uType, COLORREF color) { DefRibbonColorCtrlExecute(uCtrlID, verb, uType, color); } void OnRibbonFontCtrlExecute(UINT32 uCtrlID, UI_EXECUTIONVERB verb, CHARFORMAT2* pcf) { DefCommandExecute(MAKELONG(uCtrlID, verb), (LPARAM)pcf); } void OnRibbonSpinnerCtrlExecute(UINT32 uCtrlID, LONG* pVal) { DefCommandExecute(uCtrlID, *pVal); } void OnRibbonSpinnerCtrlExecute(UINT32 uCtrlID, DOUBLE* pVal) { DefCommandExecute(uCtrlID, (LPARAM)pVal); } void OnRibbonCommandExecute(UINT32 uCmdID) { DefCommandExecute(uCmdID); } // Default implementations HBITMAP DefRibbonQueryImage(UINT nCmdID) { return AtlLoadBitmapImage(nCmdID, LR_CREATEDIBSECTION); } bool DefRibbonQueryState(UINT nCmdID, REFPROPERTYKEY key) { DWORD dwState = UIGetState(nCmdID); bool bRet = false; switch (k_(key)) { case k_BooleanValue: bRet = (dwState & UPDUI_CHECKED) == UPDUI_CHECKED; break; case k_Enabled: bRet = (dwState & UPDUI_DISABLED) != UPDUI_DISABLED; break; default: ATLASSERT(FALSE); break; } return bRet; } LPCTSTR DefRibbonQueryText(UINT nCmdID, REFPROPERTYKEY key) { static WCHAR sText[RIBBONUI_MAX_TEXT] = { 0 }; if (k_(key) == k_Label) return UIGetText(nCmdID); if (AtlLoadString(nCmdID, sText, RIBBONUI_MAX_TEXT)) { PWCHAR pTitle = wcschr(sText, L'\n'); switch (k_(key)) { case k_Keytip: if (PWCHAR pAmp = wcschr(sText, L'&')) pTitle = pAmp; if (pTitle != NULL) *(pTitle + 2) = NULL; // fall through case k_TooltipTitle: return pTitle ? ++pTitle : NULL; case k_TooltipDescription: case k_LabelDescription: if (pTitle != NULL) *pTitle = NULL; return sText; } } return NULL; } LPCWSTR DefRibbonQueryItemText(UINT32 uCtrlID, UINT32 uItem) { return DefRibbonQueryText(uCtrlID + 1 + uItem, UI_PKEY_LabelDescription); } HBITMAP DefRibbonQueryItemImage(UINT32 uCtrlID, UINT32 uItem) { return DefRibbonQueryImage(uCtrlID + 1 + uItem); } UINT32 DefRibbonQueryItemCommand(UINT32 uCtrlID, UINT32 uItem) { return uCtrlID + 1 + uItem; } void DefRibbonColorCtrlExecute(UINT32 uCtrlID, UI_EXECUTIONVERB verb, UI_SWATCHCOLORTYPE uType, COLORREF color) { switch(uType) { case UI_SWATCHCOLORTYPE_RGB: break; case UI_SWATCHCOLORTYPE_AUTOMATIC: color = ::GetSysColor(COLOR_WINDOWTEXT); break; case UI_SWATCHCOLORTYPE_NOCOLOR: color = ::GetSysColor(COLOR_WINDOW); break; default: ATLASSERT(FALSE); break; } DefCommandExecute(MAKELONG(uCtrlID, verb), color); } void DefCommandExecute(UINT32 uCmd, LPARAM lParam = 0) { static_cast(this)->PostMessage(WM_COMMAND, uCmd, lParam); } // Elements setting helpers HRESULT InvalidateCtrl(UINT32 nID) { return IsRibbonUI() ? GetIUIFrameworkPtr()->InvalidateUICommand(nID, UI_INVALIDATIONS_ALLPROPERTIES, NULL) : E_FAIL; } HRESULT InvalidateProperty(UINT32 nID, REFPROPERTYKEY key, UI_INVALIDATIONS flags = UI_INVALIDATIONS_PROPERTY) { return IsRibbonUI() ? GetIUIFrameworkPtr()->InvalidateUICommand(nID, flags, &key) : E_FAIL; } template HRESULT SetProperty(WORD wID, REFPROPERTYKEY key, V val) { if (IsRibbonUI()) { PROPVARIANT var; if (SUCCEEDED(RibbonUI::SetPropertyVal(key, val, &var))) { return SetProperty(wID, key, var); } return E_INVALIDARG; } else { return E_FAIL; } } template <> HRESULT SetProperty(WORD nID, REFPROPERTYKEY key, PROPVARIANT var) { return IsRibbonUI() ? GetIUIFrameworkPtr()->SetUICommandProperty(nID, key, var) : E_FAIL; } // Interfaces // IUIApplication STDMETHODIMP OnViewChanged(UINT32, UI_VIEWTYPE, IUnknown*, UI_VIEWVERB verb, INT32) { switch (verb) { case UI_VIEWVERB_CREATE: m_bRibbonUI = true; if (m_hgRibbonSettings != NULL) RestoreRibbonSettings(); break; case UI_VIEWVERB_SIZE: static_cast(this)->UpdateLayout(FALSE); break; case UI_VIEWVERB_DESTROY: SaveRibbonSettings(); m_bRibbonUI = false; break; } return S_OK; } STDMETHODIMP OnCreateUICommand(UINT32 nCmdID, UI_COMMANDTYPE typeID, IUICommandHandler** ppCommandHandler) { UIAddRibbonElement(nCmdID); if (typeID == UI_COMMANDTYPE_CONTEXT) CUpdateUIBase::UIEnable(nCmdID, false); *ppCommandHandler = this; return S_OK; } STDMETHODIMP OnDestroyUICommand(UINT32 nCmdID, UI_COMMANDTYPE, IUICommandHandler*) { UIRemoveRibbonElement(nCmdID); return S_OK; } // IUICommandHandler STDMETHODIMP Execute(UINT nCmdID, UI_EXECUTIONVERB verb, const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue, IUISimplePropertySet* pCommandExecutionProperties) { T* pT =static_cast(this); return pT->GetRibbonCtrl(nCmdID).DoExecute(nCmdID, verb, key, ppropvarValue, pCommandExecutionProperties); } STDMETHODIMP UpdateProperty(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue) { T* pT =static_cast(this); return pT->GetRibbonCtrl(nCmdID).DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue); } #ifdef _DEBUG // IUnknown methods (heavyweight) STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) Release() { LONG cRef = InterlockedDecrement(&m_cRef); if (cRef == 0) // NoOp for breakpoint { cRef = 0; } return cRef; } STDMETHODIMP QueryInterface(REFIID iid, void** ppv) { if (ppv == NULL) { return E_POINTER; } else if ((iid == __uuidof(IUnknown)) || (iid == __uuidof(IUICommandHandler)) || (iid == __uuidof(IUIApplication))) { *ppv = this; AddRef(); return S_OK; } else { return E_NOINTERFACE; } } LONG m_cRef; #else // IUnknown methods (lightweight) STDMETHODIMP QueryInterface(REFIID iid, void** ppv) { if ((iid == __uuidof(IUnknown)) || (iid == __uuidof(IUICommandHandler)) || (iid == __uuidof(IUIApplication))) { *ppv = this; return S_OK; } return E_NOINTERFACE; } ULONG STDMETHODCALLTYPE AddRef() { return 1; } ULONG STDMETHODCALLTYPE Release() { return 1; } #endif // CRibbonImpl ICtrl implementation virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue, IUISimplePropertySet* /*pCommandExecutionProperties*/) { if (key != NULL) { if(k_(*key) != k_BooleanValue) { ATLTRACE2(atlTraceUI, 0, _T("Control ID %d is not handled\n"), nCmdID); return E_NOTIMPL; } BOOL bChecked = FALSE; ATLVERIFY(SUCCEEDED(PropVariantToBoolean(*ppropvarValue, &bChecked))); CUpdateUIBase::UISetCheck(nCmdID, bChecked); } ATLASSERT(verb == UI_EXECUTIONVERB_EXECUTE); verb; // avoid level 4 warning static_cast(this)->OnRibbonCommandExecute(nCmdID); return S_OK; } virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* /*ppropvarCurrentValue*/, PROPVARIANT* ppropvarNewValue) { T* pT = static_cast(this); HRESULT hr = E_NOTIMPL; switch (k_(key)) { case k_LargeImage: case k_LargeHighContrastImage: case k_SmallImage: case k_SmallHighContrastImage: if (HBITMAP hbm = pT->OnRibbonQueryImage(nCmdID, key)) hr = SetPropertyVal(key, GetImage(hbm, UI_OWNERSHIP_TRANSFER), ppropvarNewValue); break; case k_Label: case k_Keytip: case k_TooltipTitle: case k_TooltipDescription: case k_LabelDescription: if (LPCWSTR sText = pT->OnRibbonQueryText(nCmdID, key)) hr = SetPropertyVal(key, sText, ppropvarNewValue); break; case k_BooleanValue: case k_Enabled: hr = SetPropertyVal(key, pT->OnRibbonQueryState(nCmdID, key), ppropvarNewValue); break; case k_ContextAvailable: hr = SetPropertyVal(key, pT->OnRibbonQueryTabAvail(nCmdID), ppropvarNewValue); break; } return hr; } // CRibbonImpl::CRibbonXXXCtrl specialized classes //CRibbonComboCtrl template class CRibbonComboCtrl : public CollectionCtrlImpl, t_items, t_categories>> { public: CRibbonComboCtrl() { } }; // CRibbonItemGalleryCtrl template class CRibbonItemGalleryCtrl : public CollectionCtrlImpl, t_items, t_categories>> { public: CRibbonItemGalleryCtrl() { } }; // CRibbonCommandGalleryCtrl template class CRibbonCommandGalleryCtrl : public CollectionCtrlImpl, t_items, t_categories>> { public: CRibbonCommandGalleryCtrl() { } }; // CRibbonToolbarGalleryCtrl template class CRibbonToolbarGalleryCtrl : public ToolbarGalleryCtrlImpl { }; // CRibbonSimpleComboCtrl template class CRibbonSimpleComboCtrl : public SimpleCollectionCtrlImpl { }; // CRibbonSimpleGalleryCtrl template class CRibbonSimpleGalleryCtrl : public SimpleCollectionCtrlImpl { }; //CRibbonRecentItemsCtrl template class CRibbonRecentItemsCtrl : public RecentItemsCtrlImpl { public: CRibbonRecentItemsCtrl() { } }; // CRibbonColorCtrl template class CRibbonColorCtrl : public ColorCtrlImpl { public: CRibbonColorCtrl() { } }; //CRibbonFontCtrl template class CRibbonFontCtrl : public FontCtrlImpl { public: CRibbonFontCtrl() { } }; // CRibbonSpinnerCtrl template class CRibbonSpinnerCtrl : public SpinnerCtrlImpl { public: CRibbonSpinnerCtrl() { } }; // CRibbonFloatSpinnerCtrl template class CRibbonFloatSpinnerCtrl : public SpinnerCtrlImpl { public: CRibbonFloatSpinnerCtrl() { m_Values[4] = 1; // 1 decimal } }; // CRibbonCommandCtrl template class CRibbonCommandCtrl : public CommandCtrlImpl { public: CRibbonCommandCtrl() { } }; // Control classes access to T instance (re-initialized in constructor) static T* pWndRibbon; }; template __declspec(selectany) T* CRibbonImpl::pWndRibbon; // Control map element #pragma warning(push) #pragma warning(disable: 4510 610 4512) // missing default constructor, can't be instatiated, assignment operator could not be generated typedef struct { UINT uID; ICtrl& ctrl; } _ribbonCtrl; #pragma warning(pop) }; // namespace RibbonUI /////////////////////////////////////////////////////////////////////////////// // RibbonUI Control map // Control map macros #define BEGIN_RIBBON_CONTROL_MAP(theClass) \ WTL::RibbonUI::ICtrl& GetRibbonCtrl(UINT id) \ { \ WTL::RibbonUI::_ribbonCtrl _ctrls[] = \ { #define RIBBON_CONTROL(member) {member.GetID(), static_cast(member)}, #define END_RIBBON_CONTROL_MAP() \ {0, *this} \ }; \ int i = 0; \ for(; i < _countof(_ctrls) - 1; i++) \ if (_ctrls[i].uID == id) \ break; \ return _ctrls[i].ctrl; \ } // Control message map macros #define RIBBON_GALLERY_CONTROL_HANDLER(id, func) \ if(uMsg == WM_COMMAND && id == LOWORD(wParam)) \ { \ bHandled = TRUE; \ lResult = func((UI_EXECUTIONVERB)HIWORD(wParam), LOWORD(wParam), (UINT)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define RIBBON_COMBO_CONTROL_HANDLER(id, func) \ RIBBON_GALLERY_CONTROL_HANDLER(id, func) #define RIBBON_FONT_CONTROL_HANDLER(id, func) \ if(uMsg == WM_COMMAND && id == LOWORD(wParam)) \ { \ bHandled = TRUE; \ lResult = func((UI_EXECUTIONVERB)HIWORD(wParam), LOWORD(wParam), (CHARFORMAT2*)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define RIBBON_COLOR_CONTROL_HANDLER(id, func) \ if(uMsg == WM_COMMAND && id == LOWORD(wParam)) \ { \ bHandled = TRUE; \ lResult = func((UI_EXECUTIONVERB)HIWORD(wParam), LOWORD(wParam), (COLORREF)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define RIBBON_SPINNER_CONTROL_HANDLER(id, func) \ if(uMsg == WM_COMMAND && id == wParam) \ { \ bHandled = TRUE; \ lResult = func((WORD)wParam, (LONG)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define RIBBON_FLOATSPINNER_CONTROL_HANDLER(id, func) \ if(uMsg == WM_COMMAND && id == wParam) \ { \ bHandled = TRUE; \ lResult = func((WORD)wParam, (DOUBLE*)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } // Handler prototypes /* LRESULT OnRibbonGalleryCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled); LRESULT OnRibbonComboCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled); LRESULT OnRibbonFontCtrl(UI_EXECUTIONVERB verb, WORD wID, CHARFORMAT2* pcf, BOOL& bHandled); LRESULT OnRibbonColorCtrl(UI_EXECUTIONVERB verb, WORD wID, COLORREF color, BOOL& bHandled); LRESULT OnRibbonSpinnerCtrl(WORD wID, LONG lVal, BOOL& bHandled); LRESULT OnRibbonFloatSpinnerCtrl(WORD wID, DOUBLE* pdVal, BOOL& bHandled); */ /////////////////////////////////////////////////////////////////////////////// // Ribbon frame classes // CRibbonFrameWindowImplBase // template class ATL_NO_VTABLE CRibbonFrameWindowImplBase : public TFrameImpl, public RibbonUI::CRibbonImpl { typedef TFrameImpl baseFrame; bool m_bUseCommandBarBitmaps; bool m_bWin7Fix; public: // Construction CRibbonFrameWindowImplBase(bool bUseCommandBarBitmaps = true) : m_bUseCommandBarBitmaps(bUseCommandBarBitmaps), m_bWin7Fix(false) { __if_not_exists(T::m_CmdBar) { m_bUseCommandBarBitmaps = false; } } // Win7 Aero fix helpers void ResetFrame() { const MARGINS margins = { 0 }; ::DwmExtendFrameIntoClientArea(m_hWnd, &margins); } INT CalcWin7Fix() { ResetFrame(); RECT rc = { 0 }; ::AdjustWindowRectEx(&rc, T::GetWndStyle(0), GetMenu() != NULL, T::GetWndExStyle(0)); return -rc.top; } bool NeedWin7Fix() { BOOL bComp = FALSE; return m_bWin7Fix && RunTimeHelper::IsWin7() && SUCCEEDED(DwmIsCompositionEnabled(&bComp)) && bComp; } // Operations bool UseCommandBarBitmaps(bool bUse) { __if_exists(T::m_CmdBar) { return m_bUseCommandBarBitmaps = bUse; } __if_not_exists(T::m_CmdBar) { bUse; // avoid level 4 warning return false; } } bool ShowRibbonUI(bool bShow, INT32 imodes = UI_MAKEAPPMODE(0), LPCWSTR sResName = L"APPLICATION_RIBBON") { if (!RunTimeHelper::IsRibbonUIAvailable()) return false; ATLASSERT(GetIUIFrameworkPtr()); if (IsRibbonUI() == bShow) return bShow; bool bVisible = (IsWindowVisible() != FALSE); if(bVisible && !bShow) SetRedraw(FALSE); if (bShow && ::IsWindow(m_hWndToolBar)) { ::ShowWindow(m_hWndToolBar, SW_HIDE); UpdateLayout(); } m_bWin7Fix = !bShow; HRESULT hr = bShow ? CreateRibbon(sResName) : DestroyRibbon(); m_bWin7Fix = SUCCEEDED(hr) && !bShow; if (SUCCEEDED(hr)) { if(::IsWindow(m_hWndToolBar) && !bShow) { ::ShowWindow(m_hWndToolBar, SW_SHOWNA); UpdateLayout(); } else if (bShow) { PostMessage(WM_SIZE); SetRibbonModes(imodes); } } if(bVisible && !bShow) { SetRedraw(TRUE); RedrawWindow(NULL, NULL, RDW_FRAME | RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); } return SUCCEEDED(hr) ? bShow : !bShow; } // Overrideables HBITMAP OnRibbonQueryImage(UINT nCmdID, REFPROPERTYKEY key) { if ((key == UI_PKEY_SmallImage) && m_bUseCommandBarBitmaps) { if (HBITMAP hbm = GetCommandBarBitmap(nCmdID)) return (HBITMAP)::CopyImage(hbm, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); } return DefRibbonQueryImage(nCmdID); } BEGIN_MSG_MAP(CRibbonFrameWindowImplBase) if (!IsRibbonUI() && NeedWin7Fix()) { MESSAGE_HANDLER(WM_SIZING, OnSizing) MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_ACTIVATE, OnActivate) MESSAGE_HANDLER(WM_NCCALCSIZE, OnNCCalcSize) } CHAIN_MSG_MAP(CRibbonUpdateUI) CHAIN_MSG_MAP(baseFrame) END_MSG_MAP() // Message handlers for Win7 Aero LRESULT OnSizing(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { switch (wParam) { case WMSZ_TOP: case WMSZ_TOPLEFT: case WMSZ_TOPRIGHT: SetWindowPos(NULL, (LPRECT)lParam, SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED); break; default: DefWindowProc(); break; } return 1; // handled } LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { if (wParam != SIZE_MINIMIZED) SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); bHandled = FALSE; return 1; } LRESULT OnActivate(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { if(wParam != WA_INACTIVE) SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); bHandled = FALSE; return 1; } LRESULT OnNCCalcSize(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { ATLASSERT(!IsRibbonUI() && NeedWin7Fix()); LRESULT lRet = DefWindowProc(); if(wParam) { LPNCCALCSIZE_PARAMS pParams = (LPNCCALCSIZE_PARAMS)lParam; pParams->rgrc[0].top = pParams->rgrc[1].top + CalcWin7Fix(); } return lRet; } // Overrides void UpdateLayout(BOOL bResizeBars = TRUE) { RECT rect = { 0 }; GetClientRect(&rect); if (IsRibbonUI() && !IsRibbonHidden()) { rect.top += GetRibbonHeight(); } else if (!IsRibbonUI() && NeedWin7Fix()) { ResetFrame(); } // position bars and offset their dimensions UpdateBarsPosition(rect, bResizeBars); // resize client window if(m_hWndClient != NULL) ::SetWindowPos(m_hWndClient, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOACTIVATE); } // Implementation HBITMAP GetCommandBarBitmap(UINT nCmdID) { __if_exists (T::m_CmdBar) { ATLASSERT(RunTimeHelper::IsVista()); T* pT =static_cast(this); int nIndex = pT->m_CmdBar.m_arrCommand.Find((WORD&)nCmdID); return (nIndex == -1) ? NULL : pT->m_CmdBar.m_arrVistaBitmap[nIndex]; } __if_not_exists (T::m_CmdBar) { nCmdID; // avoid level 4 warning return NULL; } } }; // CRibbonFrameWindowImpl // template class ATL_NO_VTABLE CRibbonFrameWindowImpl : public CRibbonFrameWindowImplBase> { }; // CRibbonMDIFrameWindowImpl // template class ATL_NO_VTABLE CRibbonMDIFrameWindowImpl : public CRibbonFrameWindowImplBase> { }; /////////////////////////////////////////////////////////////////////////////// // CRibbonPersist helper for RibbonUI persistency class CRibbonPersist { public: CRibbonPersist(LPCWSTR sAppKey) { ATLASSERT(sAppKey && *sAppKey); m_Key.Create(HKEY_CURRENT_USER, sAppKey); ATLASSERT(m_Key.m_hKey); } CRegKeyEx m_Key; LONG Save(bool bRibbonUI, HGLOBAL hgSettings = NULL) { CRegKeyEx key; const DWORD dwUI = bRibbonUI; LONG lRet = key.Create(m_Key, L"Ribbon"); if(lRet != ERROR_SUCCESS) return lRet; lRet = key.SetDWORDValue(L"UI", dwUI); if(lRet != ERROR_SUCCESS) return lRet; if (hgSettings != NULL) { LPBYTE pVal = (LPBYTE)::GlobalLock(hgSettings); if (pVal != NULL) { lRet = key.SetBinaryValue(L"Settings", pVal, (ULONG)::GlobalSize(hgSettings)); ::GlobalUnlock(hgSettings); } else { lRet = GetLastError(); } } return lRet; } LONG Restore(bool& bRibbonUI, HGLOBAL& hgSettings) { ATLASSERT(hgSettings == NULL); CRegKeyEx key; LONG lRet = key.Open(m_Key, L"Ribbon"); if(lRet != ERROR_SUCCESS) return lRet; DWORD dwUI = 0xffff; lRet = key.QueryDWORDValue(L"UI", dwUI); if(lRet == ERROR_SUCCESS) bRibbonUI = dwUI == 1; else return lRet; ULONG ulSize = 0; lRet = key.QueryBinaryValue(L"Settings", NULL, &ulSize); if (lRet == ERROR_SUCCESS) { ATLASSERT(ulSize != 0); hgSettings = ::GlobalAlloc(GHND, ulSize); if (hgSettings != NULL) { LPBYTE pData = (LPBYTE)::GlobalLock(hgSettings); if (pData != NULL) { lRet = key.QueryBinaryValue(L"Settings", pData, &ulSize); } else { lRet = GetLastError(); ::GlobalFree(hgSettings); hgSettings = NULL; } } else { lRet = GetLastError(); } } return lRet; } LONG Delete() { return m_Key.DeleteSubKey(L"Ribbon"); } }; } // namespace WTL #endif // __ATLRIBBON_H__ ================================================ FILE: src/Setup/wtl90/atlscrl.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLSCRL_H__ #define __ATLSCRL_H__ #pragma once #ifndef __ATLAPP_H__ #error atlscrl.h requires atlapp.h to be included first #endif #ifndef __ATLWIN_H__ #error atlscrl.h requires atlwin.h to be included first #endif #if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE) #include #endif #ifndef GET_WHEEL_DELTA_WPARAM #define GET_WHEEL_DELTA_WPARAM(wParam) ((short)HIWORD(wParam)) #endif /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CScrollImpl // CScrollWindowImpl // CMapScrollImpl // CMapScrollWindowImpl // CFSBWindowT // CZoomScrollImpl // CZoomScrollWindowImpl // CScrollContainerImpl // CScrollContainer namespace WTL { /////////////////////////////////////////////////////////////////////////////// // CScrollImpl - Provides scrolling support to any window // Scroll extended styles #define SCRL_SCROLLCHILDREN 0x00000001 #define SCRL_ERASEBACKGROUND 0x00000002 #define SCRL_NOTHUMBTRACKING 0x00000004 #if (WINVER >= 0x0500) #define SCRL_SMOOTHSCROLL 0x00000008 #endif // (WINVER >= 0x0500) #define SCRL_DISABLENOSCROLLV 0x00000010 #define SCRL_DISABLENOSCROLLH 0x00000020 #define SCRL_DISABLENOSCROLL (SCRL_DISABLENOSCROLLV | SCRL_DISABLENOSCROLLH) template class CScrollImpl { public: enum { uSCROLL_FLAGS = SW_INVALIDATE }; POINT m_ptOffset; SIZE m_sizeAll; SIZE m_sizeLine; SIZE m_sizePage; SIZE m_sizeClient; int m_zDelta; // current wheel value int m_nWheelLines; // number of lines to scroll on wheel #if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE) // Note that this message must be forwarded from a top level window UINT m_uMsgMouseWheel; // MSH_MOUSEWHEEL #endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE) int m_zHDelta; // current horizontal wheel value int m_nHWheelChars; // number of chars to scroll on horizontal wheel UINT m_uScrollFlags; DWORD m_dwExtendedStyle; // scroll specific extended styles // Constructor CScrollImpl() : m_zDelta(0), m_nWheelLines(3), #if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE) m_uMsgMouseWheel(0U), #endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE) m_zHDelta(0), m_nHWheelChars(3), m_uScrollFlags(0U), m_dwExtendedStyle(0) { m_ptOffset.x = 0; m_ptOffset.y = 0; m_sizeAll.cx = 0; m_sizeAll.cy = 0; m_sizePage.cx = 0; m_sizePage.cy = 0; m_sizeLine.cx = 0; m_sizeLine.cy = 0; m_sizeClient.cx = 0; m_sizeClient.cy = 0; SetScrollExtendedStyle(SCRL_SCROLLCHILDREN | SCRL_ERASEBACKGROUND); } // Attributes & Operations DWORD GetScrollExtendedStyle() const { return m_dwExtendedStyle; } DWORD SetScrollExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0) { DWORD dwPrevStyle = m_dwExtendedStyle; if(dwMask == 0) m_dwExtendedStyle = dwExtendedStyle; else m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask); // cache scroll flags T* pT = static_cast(this); pT; // avoid level 4 warning m_uScrollFlags = pT->uSCROLL_FLAGS | (IsScrollingChildren() ? SW_SCROLLCHILDREN : 0) | (IsErasingBackground() ? SW_ERASE : 0); #if (WINVER >= 0x0500) && !defined(_WIN32_WCE) m_uScrollFlags |= (IsSmoothScroll() ? SW_SMOOTHSCROLL : 0); #endif // (WINVER >= 0x0500) && !defined(_WIN32_WCE) return dwPrevStyle; } // offset operations void SetScrollOffset(int x, int y, BOOL bRedraw = TRUE) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->AdjustScrollOffset(x, y); int dx = m_ptOffset.x - x; int dy = m_ptOffset.y - y; m_ptOffset.x = x; m_ptOffset.y = y; // block: set horizontal scroll bar { SCROLLINFO si = { sizeof(SCROLLINFO) }; si.fMask = SIF_POS; if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0) si.fMask |= SIF_DISABLENOSCROLL; si.nPos = m_ptOffset.x; pT->SetScrollInfo(SB_HORZ, &si, bRedraw); } // block: set vertical scroll bar { SCROLLINFO si = { sizeof(SCROLLINFO) }; si.fMask = SIF_POS; if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0) si.fMask |= SIF_DISABLENOSCROLL; si.nPos = m_ptOffset.y; pT->SetScrollInfo(SB_VERT, &si, bRedraw); } // Move all children if needed if(IsScrollingChildren() && (dx != 0 || dy != 0)) { for(HWND hWndChild = ::GetWindow(pT->m_hWnd, GW_CHILD); hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT)) { RECT rect = { 0 }; ::GetWindowRect(hWndChild, &rect); ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 1); ::SetWindowPos(hWndChild, NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); } } if(bRedraw) pT->Invalidate(); } void SetScrollOffset(POINT ptOffset, BOOL bRedraw = TRUE) { SetScrollOffset(ptOffset.x, ptOffset.y, bRedraw); } void GetScrollOffset(POINT& ptOffset) const { ptOffset = m_ptOffset; } // size operations void SetScrollSize(int cx, int cy, BOOL bRedraw = TRUE, bool bResetOffset = true) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); m_sizeAll.cx = cx; m_sizeAll.cy = cy; int x = 0; int y = 0; if(!bResetOffset) { x = m_ptOffset.x; y = m_ptOffset.y; pT->AdjustScrollOffset(x, y); } int dx = m_ptOffset.x - x; int dy = m_ptOffset.y - y; m_ptOffset.x = x; m_ptOffset.y = y; // block: set horizontal scroll bar { SCROLLINFO si = { sizeof(SCROLLINFO) }; si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0) si.fMask |= SIF_DISABLENOSCROLL; si.nMin = 0; si.nMax = m_sizeAll.cx - 1; si.nPage = m_sizeClient.cx; si.nPos = m_ptOffset.x; pT->SetScrollInfo(SB_HORZ, &si, bRedraw); } // block: set vertical scroll bar { SCROLLINFO si = { sizeof(SCROLLINFO) }; si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0) si.fMask |= SIF_DISABLENOSCROLL; si.nMin = 0; si.nMax = m_sizeAll.cy - 1; si.nPage = m_sizeClient.cy; si.nPos = m_ptOffset.y; pT->SetScrollInfo(SB_VERT, &si, bRedraw); } // Move all children if needed if(IsScrollingChildren() && (dx != 0 || dy != 0)) { for(HWND hWndChild = ::GetWindow(pT->m_hWnd, GW_CHILD); hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT)) { RECT rect = { 0 }; ::GetWindowRect(hWndChild, &rect); ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 1); ::SetWindowPos(hWndChild, NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); } } SetScrollLine(0, 0); SetScrollPage(0, 0); if(bRedraw) pT->Invalidate(); } void SetScrollSize(SIZE size, BOOL bRedraw = TRUE, bool bResetOffset = true) { SetScrollSize(size.cx, size.cy, bRedraw, bResetOffset); } void GetScrollSize(SIZE& sizeWnd) const { sizeWnd = m_sizeAll; } // line operations void SetScrollLine(int cxLine, int cyLine) { ATLASSERT(cxLine >= 0 && cyLine >= 0); ATLASSERT(m_sizeAll.cx != 0 && m_sizeAll.cy != 0); m_sizeLine.cx = T::CalcLineOrPage(cxLine, m_sizeAll.cx, 100); m_sizeLine.cy = T::CalcLineOrPage(cyLine, m_sizeAll.cy, 100); } void SetScrollLine(SIZE sizeLine) { SetScrollLine(sizeLine.cx, sizeLine.cy); } void GetScrollLine(SIZE& sizeLine) const { sizeLine = m_sizeLine; } // page operations void SetScrollPage(int cxPage, int cyPage) { ATLASSERT(cxPage >= 0 && cyPage >= 0); ATLASSERT(m_sizeAll.cx != 0 && m_sizeAll.cy != 0); m_sizePage.cx = T::CalcLineOrPage(cxPage, m_sizeAll.cx, 10); m_sizePage.cy = T::CalcLineOrPage(cyPage, m_sizeAll.cy, 10); } void SetScrollPage(SIZE sizePage) { SetScrollPage(sizePage.cx, sizePage.cy); } void GetScrollPage(SIZE& sizePage) const { sizePage = m_sizePage; } // commands void ScrollLineDown() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_VERT, SB_LINEDOWN, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy); } void ScrollLineUp() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_VERT, SB_LINEUP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy); } void ScrollPageDown() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_VERT, SB_PAGEDOWN, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy); } void ScrollPageUp() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_VERT, SB_PAGEUP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy); } void ScrollTop() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_VERT, SB_TOP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy); } void ScrollBottom() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_VERT, SB_BOTTOM, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy); } void ScrollLineRight() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_HORZ, SB_LINEDOWN, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx); } void ScrollLineLeft() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_HORZ, SB_LINEUP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx); } void ScrollPageRight() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_HORZ, SB_PAGEDOWN, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx); } void ScrollPageLeft() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_HORZ, SB_PAGEUP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx); } void ScrollAllLeft() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_HORZ, SB_TOP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx); } void ScrollAllRight() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_HORZ, SB_BOTTOM, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx); } // scroll to make point/view/window visible void ScrollToView(POINT pt) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); RECT rect = { pt.x, pt.y, pt.x, pt.y }; pT->ScrollToView(rect); } void ScrollToView(RECT& rect) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); RECT rcClient = { 0 }; pT->GetClientRect(&rcClient); int x = m_ptOffset.x; if(rect.left < m_ptOffset.x) x = rect.left; else if(rect.right > (m_ptOffset.x + rcClient.right)) x = rect.right - rcClient.right; int y = m_ptOffset.y; if(rect.top < m_ptOffset.y) y = rect.top; else if(rect.bottom > (m_ptOffset.y + rcClient.bottom)) y = rect.bottom - rcClient.bottom; SetScrollOffset(x, y); } void ScrollToView(HWND hWnd) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); RECT rect = { 0 }; ::GetWindowRect(hWnd, &rect); ::OffsetRect(&rect, m_ptOffset.x, m_ptOffset.y); ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 2); ScrollToView(rect); } BEGIN_MSG_MAP(CScrollImpl) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_VSCROLL, OnVScroll) MESSAGE_HANDLER(WM_HSCROLL, OnHScroll) MESSAGE_HANDLER(WM_MOUSEWHEEL, OnMouseWheel) #if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE) MESSAGE_HANDLER(m_uMsgMouseWheel, OnMouseWheel) #endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE) MESSAGE_HANDLER(WM_MOUSEHWHEEL, OnMouseHWheel) MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange) MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_PAINT, OnPaint) #ifndef _WIN32_WCE MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint) #endif // !_WIN32_WCE // standard scroll commands ALT_MSG_MAP(1) COMMAND_ID_HANDLER(ID_SCROLL_UP, OnScrollUp) COMMAND_ID_HANDLER(ID_SCROLL_DOWN, OnScrollDown) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, OnScrollPageUp) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, OnScrollPageDown) COMMAND_ID_HANDLER(ID_SCROLL_TOP, OnScrollTop) COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, OnScrollBottom) COMMAND_ID_HANDLER(ID_SCROLL_LEFT, OnScrollLeft) COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, OnScrollRight) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, OnScrollPageLeft) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, OnScrollPageRight) COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, OnScrollAllLeft) COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, OnScrollAllRight) END_MSG_MAP() LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); pT->GetSystemSettings(); bHandled = FALSE; return 1; } LRESULT OnVScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_VERT, (int)(short)LOWORD(wParam), (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy); return 0; } LRESULT OnHScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoScroll(SB_HORZ, (int)(short)LOWORD(wParam), (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx); return 0; } LRESULT OnMouseWheel(UINT uMsg, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) || defined(_WIN32_WCE) uMsg; int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam); #else int zDelta = (uMsg == WM_MOUSEWHEEL) ? (int)GET_WHEEL_DELTA_WPARAM(wParam) : (int)wParam; #endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) || defined(_WIN32_WCE)) int nScrollCode = (m_nWheelLines == WHEEL_PAGESCROLL) ? ((zDelta > 0) ? SB_PAGEUP : SB_PAGEDOWN) : ((zDelta > 0) ? SB_LINEUP : SB_LINEDOWN); m_zDelta += zDelta; // cumulative int zTotal = (m_nWheelLines == WHEEL_PAGESCROLL) ? abs(m_zDelta) : abs(m_zDelta) * m_nWheelLines; if(m_sizeAll.cy > m_sizeClient.cy) { for(int i = 0; i < zTotal; i += WHEEL_DELTA) { pT->DoScroll(SB_VERT, nScrollCode, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy); pT->UpdateWindow(); } } else if(m_sizeAll.cx > m_sizeClient.cx) // can't scroll vertically, scroll horizontally { for(int i = 0; i < zTotal; i += WHEEL_DELTA) { pT->DoScroll(SB_HORZ, nScrollCode, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx); pT->UpdateWindow(); } } m_zDelta %= WHEEL_DELTA; return 0; } LRESULT OnMouseHWheel(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam); int nScrollCode = (m_nHWheelChars == WHEEL_PAGESCROLL) ? ((zDelta > 0) ? SB_PAGERIGHT : SB_PAGELEFT) : ((zDelta > 0) ? SB_LINERIGHT : SB_LINELEFT); m_zHDelta += zDelta; // cumulative int zTotal = (m_nHWheelChars == WHEEL_PAGESCROLL) ? abs(m_zHDelta) : abs(m_zHDelta) * m_nHWheelChars; if(m_sizeAll.cx > m_sizeClient.cx) { for(int i = 0; i < zTotal; i += WHEEL_DELTA) { pT->DoScroll(SB_HORZ, nScrollCode, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx); pT->UpdateWindow(); } } m_zHDelta %= WHEEL_DELTA; return 0; } LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { GetSystemSettings(); return 0; } LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); pT->DoSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); bHandled = FALSE; return 1; } LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); if(wParam != NULL) { CDCHandle dc = (HDC)wParam; POINT ptViewportOrg = { 0, 0 }; dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y, &ptViewportOrg); pT->DoPaint(dc); dc.SetViewportOrg(ptViewportOrg); } else { CPaintDC dc(pT->m_hWnd); dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y); pT->DoPaint(dc.m_hDC); } return 0; } // scrolling handlers LRESULT OnScrollUp(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ScrollLineUp(); return 0; } LRESULT OnScrollDown(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ScrollLineDown(); return 0; } LRESULT OnScrollPageUp(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ScrollPageUp(); return 0; } LRESULT OnScrollPageDown(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ScrollPageDown(); return 0; } LRESULT OnScrollTop(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ScrollTop(); return 0; } LRESULT OnScrollBottom(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ScrollBottom(); return 0; } LRESULT OnScrollLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ScrollLineLeft(); return 0; } LRESULT OnScrollRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ScrollLineRight(); return 0; } LRESULT OnScrollPageLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ScrollPageLeft(); return 0; } LRESULT OnScrollPageRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ScrollPageRight(); return 0; } LRESULT OnScrollAllLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ScrollAllLeft(); return 0; } LRESULT OnScrollAllRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ScrollAllRight(); return 0; } // Overrideables void DoPaint(CDCHandle /*dc*/) { // must be implemented in a derived class ATLASSERT(FALSE); } // Implementation void DoSize(int cx, int cy) { m_sizeClient.cx = cx; m_sizeClient.cy = cy; T* pT = static_cast(this); // block: set horizontal scroll bar { SCROLLINFO si = { sizeof(SCROLLINFO) }; si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; si.nMin = 0; si.nMax = m_sizeAll.cx - 1; if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0) si.fMask |= SIF_DISABLENOSCROLL; si.nPage = m_sizeClient.cx; si.nPos = m_ptOffset.x; pT->SetScrollInfo(SB_HORZ, &si, TRUE); } // block: set vertical scroll bar { SCROLLINFO si = { sizeof(SCROLLINFO) }; si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; si.nMin = 0; si.nMax = m_sizeAll.cy - 1; if((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0) si.fMask |= SIF_DISABLENOSCROLL; si.nPage = m_sizeClient.cy; si.nPos = m_ptOffset.y; pT->SetScrollInfo(SB_VERT, &si, TRUE); } int x = m_ptOffset.x; int y = m_ptOffset.y; if(pT->AdjustScrollOffset(x, y)) { // Children will be moved in SetScrollOffset, if needed pT->ScrollWindowEx(m_ptOffset.x - x, m_ptOffset.y - y, (m_uScrollFlags & ~SCRL_SCROLLCHILDREN)); SetScrollOffset(x, y, FALSE); } } void DoScroll(int nType, int nScrollCode, int& cxyOffset, int cxySizeAll, int cxySizePage, int cxySizeLine) { T* pT = static_cast(this); RECT rect = { 0 }; pT->GetClientRect(&rect); int cxyClient = (nType == SB_VERT) ? rect.bottom : rect.right; int cxyMax = cxySizeAll - cxyClient; if(cxyMax < 0) // can't scroll, client area is bigger return; bool bUpdate = true; int cxyScroll = 0; switch(nScrollCode) { case SB_TOP: // top or all left cxyScroll = cxyOffset; cxyOffset = 0; break; case SB_BOTTOM: // bottom or all right cxyScroll = cxyOffset - cxyMax; cxyOffset = cxyMax; break; case SB_LINEUP: // line up or line left if(cxyOffset >= cxySizeLine) { cxyScroll = cxySizeLine; cxyOffset -= cxySizeLine; } else { cxyScroll = cxyOffset; cxyOffset = 0; } break; case SB_LINEDOWN: // line down or line right if(cxyOffset < cxyMax - cxySizeLine) { cxyScroll = -cxySizeLine; cxyOffset += cxySizeLine; } else { cxyScroll = cxyOffset - cxyMax; cxyOffset = cxyMax; } break; case SB_PAGEUP: // page up or page left if(cxyOffset >= cxySizePage) { cxyScroll = cxySizePage; cxyOffset -= cxySizePage; } else { cxyScroll = cxyOffset; cxyOffset = 0; } break; case SB_PAGEDOWN: // page down or page right if(cxyOffset < cxyMax - cxySizePage) { cxyScroll = -cxySizePage; cxyOffset += cxySizePage; } else { cxyScroll = cxyOffset - cxyMax; cxyOffset = cxyMax; } break; case SB_THUMBTRACK: if(IsNoThumbTracking()) break; // else fall through case SB_THUMBPOSITION: { SCROLLINFO si = { sizeof(SCROLLINFO), SIF_TRACKPOS }; if(pT->GetScrollInfo(nType, &si)) { cxyScroll = cxyOffset - si.nTrackPos; cxyOffset = si.nTrackPos; } } break; case SB_ENDSCROLL: default: bUpdate = false; break; } if(bUpdate && cxyScroll != 0) { pT->SetScrollPos(nType, cxyOffset, TRUE); if(nType == SB_VERT) pT->ScrollWindowEx(0, cxyScroll, m_uScrollFlags); else pT->ScrollWindowEx(cxyScroll, 0, m_uScrollFlags); } } static int CalcLineOrPage(int nVal, int nMax, int nDiv) { if(nVal == 0) { nVal = nMax / nDiv; if(nVal < 1) nVal = 1; } else if(nVal > nMax) { nVal = nMax; } return nVal; } bool AdjustScrollOffset(int& x, int& y) { int xOld = x; int yOld = y; int cxMax = m_sizeAll.cx - m_sizeClient.cx; if(x > cxMax) x = (cxMax >= 0) ? cxMax : 0; else if(x < 0) x = 0; int cyMax = m_sizeAll.cy - m_sizeClient.cy; if(y > cyMax) y = (cyMax >= 0) ? cyMax : 0; else if(y < 0) y = 0; return (x != xOld || y != yOld); } void GetSystemSettings() { #ifndef _WIN32_WCE #ifndef SPI_GETWHEELSCROLLLINES const UINT SPI_GETWHEELSCROLLLINES = 104; #endif // !SPI_GETWHEELSCROLLLINES ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &m_nWheelLines, 0); #ifndef SPI_GETWHEELSCROLLCHARS const UINT SPI_GETWHEELSCROLLCHARS = 0x006C; #endif // !SPI_GETWHEELSCROLLCHARS ::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &m_nHWheelChars, 0); #if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) if(m_uMsgMouseWheel != 0) m_uMsgMouseWheel = ::RegisterWindowMessage(MSH_MOUSEWHEEL); HWND hWndWheel = FindWindow(MSH_WHEELMODULE_CLASS, MSH_WHEELMODULE_TITLE); if(::IsWindow(hWndWheel)) { UINT uMsgScrollLines = ::RegisterWindowMessage(MSH_SCROLL_LINES); if(uMsgScrollLines != 0) m_nWheelLines = (int)::SendMessage(hWndWheel, uMsgScrollLines, 0, 0L); } #endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) #endif // !_WIN32_WCE } bool IsScrollingChildren() const { return (m_dwExtendedStyle & SCRL_SCROLLCHILDREN) != 0; } bool IsErasingBackground() const { return (m_dwExtendedStyle & SCRL_ERASEBACKGROUND) != 0; } bool IsNoThumbTracking() const { return (m_dwExtendedStyle & SCRL_NOTHUMBTRACKING) != 0; } #if (WINVER >= 0x0500) bool IsSmoothScroll() const { return (m_dwExtendedStyle & SCRL_SMOOTHSCROLL) != 0; } #endif // (WINVER >= 0x0500) }; /////////////////////////////////////////////////////////////////////////////// // CScrollWindowImpl - Implements a scrollable window template class ATL_NO_VTABLE CScrollWindowImpl : public ATL::CWindowImpl, public CScrollImpl< T > { public: BOOL SubclassWindow(HWND hWnd) { #if (_MSC_VER >= 1300) BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); #else // !(_MSC_VER >= 1300) / typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass; BOOL bRet = _baseClass::SubclassWindow(hWnd); #endif // !(_MSC_VER >= 1300) if(bRet != FALSE) { T* pT = static_cast(this); pT->GetSystemSettings(); RECT rect = { 0 }; GetClientRect(&rect); pT->DoSize(rect.right, rect.bottom); } return bRet; } BEGIN_MSG_MAP(CScrollWindowImpl) MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll) MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll) MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel) #if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE) MESSAGE_HANDLER(m_uMsgMouseWheel, CScrollImpl< T >::OnMouseWheel) #endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && !defined(_WIN32_WCE) MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel) MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange) MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize) MESSAGE_HANDLER(WM_PAINT, CScrollImpl< T >::OnPaint) #ifndef _WIN32_WCE MESSAGE_HANDLER(WM_PRINTCLIENT, CScrollImpl< T >::OnPaint) #endif // !_WIN32_WCE ALT_MSG_MAP(1) COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp) COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown) COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop) COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom) COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft) COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight) COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft) COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight) END_MSG_MAP() }; /////////////////////////////////////////////////////////////////////////////// // CMapScrollImpl - Provides mapping and scrolling support to any window #ifndef _WIN32_WCE template class CMapScrollImpl : public CScrollImpl< T > { public: int m_nMapMode; RECT m_rectLogAll; SIZE m_sizeLogLine; SIZE m_sizeLogPage; // Constructor CMapScrollImpl() : m_nMapMode(MM_TEXT) { ::SetRectEmpty(&m_rectLogAll); m_sizeLogPage.cx = 0; m_sizeLogPage.cy = 0; m_sizeLogLine.cx = 0; m_sizeLogLine.cy = 0; } // Attributes & Operations // mapping mode operations void SetScrollMapMode(int nMapMode) { ATLASSERT(nMapMode >= MM_MIN && nMapMode <= MM_MAX_FIXEDSCALE); m_nMapMode = nMapMode; } int GetScrollMapMode() const { ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE); return m_nMapMode; } // offset operations void SetScrollOffset(int x, int y, BOOL bRedraw = TRUE) { ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE); POINT ptOff = { x, y }; // block: convert logical to device units { CWindowDC dc(NULL); dc.SetMapMode(m_nMapMode); dc.LPtoDP(&ptOff); } CScrollImpl< T >::SetScrollOffset(ptOff, bRedraw); } void SetScrollOffset(POINT ptOffset, BOOL bRedraw = TRUE) { SetScrollOffset(ptOffset.x, ptOffset.y, bRedraw); } void GetScrollOffset(POINT& ptOffset) const { ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE); ptOffset = m_ptOffset; // block: convert device to logical units { CWindowDC dc(NULL); dc.SetMapMode(m_nMapMode); dc.DPtoLP(&ptOffset); } } // size operations void SetScrollSize(int xMin, int yMin, int xMax, int yMax, BOOL bRedraw = TRUE, bool bResetOffset = true) { ATLASSERT(xMax > xMin && yMax > yMin); ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE); ::SetRect(&m_rectLogAll, xMin, yMin, xMax, yMax); SIZE sizeAll = { 0 }; sizeAll.cx = xMax - xMin + 1; sizeAll.cy = yMax - yMin + 1; // block: convert logical to device units { CWindowDC dc(NULL); dc.SetMapMode(m_nMapMode); dc.LPtoDP(&sizeAll); } CScrollImpl< T >::SetScrollSize(sizeAll, bRedraw, bResetOffset); SetScrollLine(0, 0); SetScrollPage(0, 0); } void SetScrollSize(RECT& rcScroll, BOOL bRedraw = TRUE, bool bResetOffset = true) { SetScrollSize(rcScroll.left, rcScroll.top, rcScroll.right, rcScroll.bottom, bRedraw, bResetOffset); } void SetScrollSize(int cx, int cy, BOOL bRedraw = TRUE, bool bResetOffset = true) { SetScrollSize(0, 0, cx, cy, bRedraw, bResetOffset); } void SetScrollSize(SIZE size, BOOL bRedraw = TRUE, bool bResetOffset = true) { SetScrollSize(0, 0, size.cx, size.cy, bRedraw, bResetOffset); } void GetScrollSize(RECT& rcScroll) const { ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE); rcScroll = m_rectLogAll; } // line operations void SetScrollLine(int cxLine, int cyLine) { ATLASSERT(cxLine >= 0 && cyLine >= 0); ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE); m_sizeLogLine.cx = cxLine; m_sizeLogLine.cy = cyLine; SIZE sizeLine = m_sizeLogLine; // block: convert logical to device units { CWindowDC dc(NULL); dc.SetMapMode(m_nMapMode); dc.LPtoDP(&sizeLine); } CScrollImpl< T >::SetScrollLine(sizeLine); } void SetScrollLine(SIZE sizeLine) { SetScrollLine(sizeLine.cx, sizeLine.cy); } void GetScrollLine(SIZE& sizeLine) const { ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE); sizeLine = m_sizeLogLine; } // page operations void SetScrollPage(int cxPage, int cyPage) { ATLASSERT(cxPage >= 0 && cyPage >= 0); ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE); m_sizeLogPage.cx = cxPage; m_sizeLogPage.cy = cyPage; SIZE sizePage = m_sizeLogPage; // block: convert logical to device units { CWindowDC dc(NULL); dc.SetMapMode(m_nMapMode); dc.LPtoDP(&sizePage); } CScrollImpl< T >::SetScrollPage(sizePage); } void SetScrollPage(SIZE sizePage) { SetScrollPage(sizePage.cx, sizePage.cy); } void GetScrollPage(SIZE& sizePage) const { ATLASSERT(m_nMapMode >= MM_MIN && m_nMapMode <= MM_MAX_FIXEDSCALE); sizePage = m_sizeLogPage; } BEGIN_MSG_MAP(CMapScrollImpl) MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll) MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll) MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel) #if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) MESSAGE_HANDLER(m_uMsgMouseWheel, CScrollImpl< T >::OnMouseWheel) #endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel) MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange) MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize) MESSAGE_HANDLER(WM_PAINT, OnPaint) MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint) ALT_MSG_MAP(1) COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp) COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown) COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop) COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom) COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft) COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight) COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft) COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight) END_MSG_MAP() LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); if(wParam != NULL) { CDCHandle dc = (HDC)wParam; int nMapModeSav = dc.GetMapMode(); dc.SetMapMode(m_nMapMode); POINT ptViewportOrg = { 0, 0 }; if(m_nMapMode == MM_TEXT) dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y, &ptViewportOrg); else dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y + m_sizeAll.cy, &ptViewportOrg); POINT ptWindowOrg = { 0, 0 }; dc.SetWindowOrg(m_rectLogAll.left, m_rectLogAll.top, &ptWindowOrg); pT->DoPaint(dc); dc.SetMapMode(nMapModeSav); dc.SetViewportOrg(ptViewportOrg); dc.SetWindowOrg(ptWindowOrg); } else { CPaintDC dc(pT->m_hWnd); dc.SetMapMode(m_nMapMode); if(m_nMapMode == MM_TEXT) dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y); else dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y + m_sizeAll.cy); dc.SetWindowOrg(m_rectLogAll.left, m_rectLogAll.top); pT->DoPaint(dc.m_hDC); } return 0; } }; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CMapScrollWindowImpl - Implements scrolling window with mapping #ifndef _WIN32_WCE template class ATL_NO_VTABLE CMapScrollWindowImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CMapScrollImpl< T > { public: BOOL SubclassWindow(HWND hWnd) { #if (_MSC_VER >= 1300) BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); #else // !(_MSC_VER >= 1300) / typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass; BOOL bRet = _baseClass::SubclassWindow(hWnd); #endif // !(_MSC_VER >= 1300) if(bRet != FALSE) { T* pT = static_cast(this); pT->GetSystemSettings(); RECT rect = { 0 }; GetClientRect(&rect); pT->DoSize(rect.right, rect.bottom); } return bRet; } BEGIN_MSG_MAP(CMapScrollWindowImpl) MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll) MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll) MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel) #if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) MESSAGE_HANDLER(m_uMsgMouseWheel, CScrollImpl< T >::OnMouseWheel) #endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel) MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange) MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize) MESSAGE_HANDLER(WM_PAINT, CMapScrollImpl< T >::OnPaint) MESSAGE_HANDLER(WM_PRINTCLIENT, CMapScrollImpl< T >::OnPaint) ALT_MSG_MAP(1) COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp) COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown) COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop) COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom) COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft) COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight) COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft) COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight) END_MSG_MAP() }; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CFSBWindow - Use as a base instead of CWindow to get flat scroll bar support #if defined(__ATLCTRLS_H__) && (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) template class CFSBWindowT : public TBase, public CFlatScrollBarImpl > { public: // Constructors CFSBWindowT(HWND hWnd = NULL) : TBase(hWnd) { } CFSBWindowT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } // CWindow overrides that use flat scroll bar API // (only those methods that are used by scroll window classes) int SetScrollPos(int nBar, int nPos, BOOL bRedraw = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return FlatSB_SetScrollPos(nBar, nPos, bRedraw); } BOOL GetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo) { ATLASSERT(::IsWindow(m_hWnd)); return FlatSB_GetScrollInfo(nBar, lpScrollInfo); } BOOL SetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); return FlatSB_SetScrollInfo(nBar, lpScrollInfo, bRedraw); } }; typedef CFSBWindowT CFSBWindow; #endif // defined(__ATLCTRLS_H__) && (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE) /////////////////////////////////////////////////////////////////////////////// // CZoomScrollImpl - Provides zooming and scrolling support to any window #ifndef _WIN32_WCE // The zoom modes that can be set with the SetZoomMode method enum { ZOOMMODE_OFF, ZOOMMODE_IN, // If left mouse button is clicked or dragged, zoom in on point clicked or rectangle dragged. ZOOMMODE_OUT // If left mouse button clicked, zoom out on point clicked. }; // Notification to parent that zoom scale changed as a result of user mouse action. #define ZSN_ZOOMCHANGED (NM_FIRST - 50) template class CZoomScrollImpl : public CScrollImpl< T > { public: enum { m_cxyMinZoomRect = 12 }; // min rect size to zoom in on rect. struct _ChildPlacement { HWND hWnd; int x; int y; int cx; int cy; bool operator ==(const _ChildPlacement& cp) const { return (memcmp(this, &cp, sizeof(_ChildPlacement)) == 0); } }; // Data members SIZE m_sizeLogAll; SIZE m_sizeLogLine; SIZE m_sizeLogPage; float m_fZoomScale; float m_fZoomScaleMin; float m_fZoomScaleMax; float m_fZoomDelta; // Used in ZOOMMODE_IN and ZOOMMODE_OUT on left-button click. int m_nZoomMode; RECT m_rcTrack; bool m_bTracking; bool m_bZoomChildren; ATL::CSimpleArray<_ChildPlacement> m_arrChildren; // Constructor CZoomScrollImpl(): m_fZoomScale(1.0f), m_fZoomScaleMin(0.1f), m_fZoomScaleMax(100.0f), m_fZoomDelta(0.5f), m_nZoomMode(ZOOMMODE_OFF), m_bTracking(false), m_bZoomChildren(false) { m_sizeLogAll.cx = 0; m_sizeLogAll.cy = 0; m_sizeLogPage.cx = 0; m_sizeLogPage.cy = 0; m_sizeLogLine.cx = 0; m_sizeLogLine.cy = 0; ::SetRectEmpty(&m_rcTrack); } // Attributes & Operations // size operations void SetScrollSize(int cxLog, int cyLog, BOOL bRedraw = TRUE, bool bResetOffset = true) { ATLASSERT(cxLog >= 0 && cyLog >= 0); // Set up the defaults if((cxLog == 0) && (cyLog == 0)) { cxLog = 1; cyLog = 1; } m_sizeLogAll.cx = cxLog; m_sizeLogAll.cy = cyLog; SIZE sizeAll = { 0 }; sizeAll.cx = (int)((float)m_sizeLogAll.cx * m_fZoomScale); sizeAll.cy = (int)((float)m_sizeLogAll.cy * m_fZoomScale); CScrollImpl< T >::SetScrollSize(sizeAll, bRedraw, bResetOffset); } void SetScrollSize(SIZE sizeLog, BOOL bRedraw = TRUE, bool bResetOffset = true) { SetScrollSize(sizeLog.cx, sizeLog.cy, bRedraw, bResetOffset); } void GetScrollSize(SIZE& sizeLog) const { sizeLog = m_sizeLogAll; } // line operations void SetScrollLine(int cxLogLine, int cyLogLine) { ATLASSERT(cxLogLine >= 0 && cyLogLine >= 0); m_sizeLogLine.cx = cxLogLine; m_sizeLogLine.cy = cyLogLine; SIZE sizeLine = { 0 }; sizeLine.cx = (int)((float)m_sizeLogLine.cx * m_fZoomScale); sizeLine.cy = (int)((float)m_sizeLogLine.cy * m_fZoomScale); CScrollImpl< T >::SetScrollLine(sizeLine); } void SetScrollLine(SIZE sizeLogLine) { SetScrollLine(sizeLogLine.cx, sizeLogLine.cy); } void GetScrollLine(SIZE& sizeLogLine) const { sizeLogLine = m_sizeLogLine; } // page operations void SetScrollPage(int cxLogPage, int cyLogPage) { ATLASSERT((cxLogPage >= 0) && (cyLogPage >= 0)); m_sizeLogPage.cx = cxLogPage; m_sizeLogPage.cy = cyLogPage; SIZE sizePage = { 0 }; sizePage.cx = (int)((float)m_sizeLogPage.cx * m_fZoomScale); sizePage.cy = (int)((float)m_sizeLogPage.cy * m_fZoomScale); CScrollImpl< T >::SetScrollPage(sizePage); } void SetScrollPage(SIZE sizeLogPage) { SetScrollPage(sizeLogPage.cx, sizeLogPage.cy); } void GetScrollPage(SIZE& sizeLogPage) const { sizeLogPage = m_sizeLogPage; } void SetZoomScale(float fZoomScale) { ATLASSERT(fZoomScale > 0.0f); if(fZoomScale <= 0.0f) return; m_fZoomScale = fZoomScale; if(m_fZoomScale < m_fZoomScaleMin) m_fZoomScale = m_fZoomScaleMin; else if(m_fZoomScale > m_fZoomScaleMax) m_fZoomScale = m_fZoomScaleMax; } float GetZoomScale() const { return m_fZoomScale; } void SetZoomScaleMin(float fZoomScaleMin) { ATLASSERT(fZoomScaleMin > 0.0f); ATLASSERT(fZoomScaleMin <= m_fZoomScaleMax); m_fZoomScaleMin = fZoomScaleMin; } float GetZoomScaleMin() const { return m_fZoomScaleMin; } void SetZoomScaleMax(float fZoomScaleMax) { ATLASSERT(fZoomScaleMax > 0.0f); ATLASSERT(m_fZoomScaleMin <= fZoomScaleMax); m_fZoomScaleMax = fZoomScaleMax; } float GetZoomScaleMax() const { return m_fZoomScaleMax; } void SetZoomDelta(float fZoomDelta) { ATLASSERT(fZoomDelta >= 0.0f); if(fZoomDelta >= 0.0f) m_fZoomDelta = fZoomDelta; } float GetZoomDelta() const { return m_fZoomDelta; } void SetZoomMode(int nZoomMode) { m_nZoomMode = nZoomMode; } int GetZoomMode() const { return m_nZoomMode; } void SetZoomChildren(bool bEnable = true) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); m_bZoomChildren = bEnable; m_arrChildren.RemoveAll(); if(m_bZoomChildren) { for(HWND hWndChild = ::GetWindow(pT->m_hWnd, GW_CHILD); hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT)) { RECT rect = { 0 }; ::GetWindowRect(hWndChild, &rect); ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 2); _ChildPlacement cp = { 0 }; cp.hWnd = hWndChild; cp.x = rect.left; cp.y = rect.top; cp.cx = rect.right - rect.left; cp.cy = rect.bottom - rect.top; m_arrChildren.Add(cp); } } } bool GetZoomChildren() const { return m_bZoomChildren; } void Zoom(int x, int y, float fZoomScale) { if(fZoomScale <= 0.0f) return; if(fZoomScale < m_fZoomScaleMin) fZoomScale = m_fZoomScaleMin; else if(fZoomScale > m_fZoomScaleMax) fZoomScale = m_fZoomScaleMax; T* pT = static_cast(this); POINT pt = { x, y }; if(!pT->PtInDevRect(pt)) return; pT->ViewDPtoLP(&pt); pT->Zoom(fZoomScale, false); pT->CenterOnLogicalPoint(pt); } void Zoom(POINT pt, float fZoomScale) { T* pT = static_cast(this); pT->Zoom(pt.x, pt.y, fZoomScale); } void Zoom(RECT& rc) { T* pT = static_cast(this); RECT rcZoom = rc; pT->NormalizeRect(rcZoom); SIZE size = { rcZoom.right - rcZoom.left, rcZoom.bottom - rcZoom.top }; POINT pt = { rcZoom.left + size.cx / 2, rcZoom.top + size.cy / 2 }; if(size.cx < m_cxyMinZoomRect || size.cy < m_cxyMinZoomRect) { pT->Zoom(pt, m_fZoomScale + m_fZoomDelta); return; } ATLASSERT((size.cx > 0) && (size.cy > 0)); float fScaleH = (float)(m_sizeClient.cx + 1) / (float)size.cx; float fScaleV = (float)(m_sizeClient.cy + 1) / (float)size.cy; float fZoomScale = __min(fScaleH, fScaleV) * m_fZoomScale; pT->Zoom(pt, fZoomScale); } void Zoom(float fZoomScale, bool bCenter = true) { if(fZoomScale <= 0.0f) return; if(fZoomScale < m_fZoomScaleMin) fZoomScale = m_fZoomScaleMin; else if(fZoomScale > m_fZoomScaleMax) fZoomScale = m_fZoomScaleMax; T* pT = static_cast(this); POINT pt = { 0 }; if(bCenter) { RECT rcClient = { 0 }; ::GetClientRect(pT->m_hWnd, &rcClient); pt.x = rcClient.right / 2; pt.y = rcClient.bottom / 2; pT->ViewDPtoLP(&pt); } // Modify the Viewport extent SIZE sizeAll = { 0 }; sizeAll.cx = (int)((float)m_sizeLogAll.cx * fZoomScale); sizeAll.cy = (int)((float)m_sizeLogAll.cy * fZoomScale); // Update scroll bars and window CScrollImpl< T >::SetScrollSize(sizeAll); // Zoom all children if needed if(m_bZoomChildren && (m_fZoomScale != fZoomScale)) { for(int i = 0; i < m_arrChildren.GetSize(); i++) { ATLASSERT(::IsWindow(m_arrChildren[i].hWnd)); ::SetWindowPos(m_arrChildren[i].hWnd, NULL, (int)((float)m_arrChildren[i].x * fZoomScale + 0.5f), (int)((float)m_arrChildren[i].y * fZoomScale + 0.5f), (int)((float)m_arrChildren[i].cx * fZoomScale + 0.5f), (int)((float)m_arrChildren[i].cy * fZoomScale + 0.5f), SWP_NOZORDER | SWP_NOACTIVATE); } } // Set new zoom scale m_fZoomScale = fZoomScale; if(bCenter) pT->CenterOnLogicalPoint(pt); } // Helper functions void PrepareDC(CDCHandle dc) { ATLASSERT(m_sizeAll.cx >= 0 && m_sizeAll.cy >= 0); dc.SetMapMode(MM_ANISOTROPIC); dc.SetWindowExt(m_sizeLogAll); dc.SetViewportExt(m_sizeAll); dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y); } void ViewDPtoLP(LPPOINT lpPoints, int nCount = 1) { ATLASSERT(lpPoints); T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); CWindowDC dc(pT->m_hWnd); pT->PrepareDC(dc.m_hDC); dc.DPtoLP(lpPoints, nCount); } void ViewLPtoDP(LPPOINT lpPoints, int nCount = 1) { ATLASSERT(lpPoints); T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); CWindowDC dc(pT->m_hWnd); pT->PrepareDC(dc.m_hDC); dc.LPtoDP(lpPoints, nCount); } void ClientToDevice(POINT &pt) { pt.x += m_ptOffset.x; pt.y += m_ptOffset.y; } void DeviceToClient(POINT &pt) { pt.x -= m_ptOffset.x; pt.y -= m_ptOffset.y; } void CenterOnPoint(POINT pt) { T* pT = static_cast(this); RECT rect = { 0 }; pT->GetClientRect(&rect); int xOfs = pt.x - (rect.right / 2) + m_ptOffset.x; if(xOfs < 0) { xOfs = 0; } else { int xMax = __max((int)(m_sizeAll.cx - rect.right), 0); if(xOfs > xMax) xOfs = xMax; } int yOfs = pt.y - (rect.bottom / 2) + m_ptOffset.y; if(yOfs < 0) { yOfs = 0; } else { int yMax = __max((int)(m_sizeAll.cy - rect.bottom), 0); if(yOfs > yMax) yOfs = yMax; } CScrollImpl< T >::SetScrollOffset(xOfs, yOfs); } void CenterOnLogicalPoint(POINT ptLog) { T* pT = static_cast(this); pT->ViewLPtoDP(&ptLog); pT->DeviceToClient(ptLog); pT->CenterOnPoint(ptLog); } BOOL PtInDevRect(POINT pt) { RECT rc = { 0, 0, m_sizeAll.cx, m_sizeAll.cy }; ::OffsetRect(&rc, -m_ptOffset.x, -m_ptOffset.y); return ::PtInRect(&rc, pt); } void NormalizeRect(RECT& rc) { if(rc.left > rc.right) { int r = rc.right; rc.right = rc.left; rc.left = r; } if(rc.top > rc.bottom) { int b = rc.bottom; rc.bottom = rc.top; rc.top = b; } } void DrawTrackRect() { T* pT = static_cast(this); const SIZE sizeLines = { 2, 2 }; RECT rc = m_rcTrack; pT->NormalizeRect(rc); if(!::IsRectEmpty(&rc)) { ::MapWindowPoints(pT->m_hWnd, NULL, (LPPOINT)&rc, 2); CWindowDC dc(NULL); dc.DrawDragRect(&rc, sizeLines, NULL, sizeLines); } } void NotifyParentZoomChanged() { T* pT = static_cast(this); int nId = pT->GetDlgCtrlID(); NMHDR nmhdr = { pT->m_hWnd, nId, ZSN_ZOOMCHANGED }; ::SendMessage(pT->GetParent(), WM_NOTIFY, (WPARAM)nId, (LPARAM)&nmhdr); } BEGIN_MSG_MAP(CZoomScrollImpl) MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor) MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll) MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll) MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel) #if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) MESSAGE_HANDLER(m_uMsgMouseWheel, CScrollImpl< T >::OnMouseWheel) #endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel) MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange) MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize) MESSAGE_HANDLER(WM_PAINT, OnPaint) MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint) MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown) MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp) MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged) ALT_MSG_MAP(1) COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp) COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown) COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop) COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom) COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft) COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight) COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft) COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight) END_MSG_MAP() LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); ATLASSERT((m_sizeLogAll.cx >= 0) && (m_sizeLogAll.cy >= 0)); ATLASSERT((m_sizeAll.cx >= 0) && (m_sizeAll.cy >= 0)); if(wParam != NULL) { CDCHandle dc = (HDC)wParam; int nMapModeSav = dc.GetMapMode(); dc.SetMapMode(MM_ANISOTROPIC); SIZE szWindowExt = { 0, 0 }; dc.SetWindowExt(m_sizeLogAll, &szWindowExt); SIZE szViewportExt = { 0, 0 }; dc.SetViewportExt(m_sizeAll, &szViewportExt); POINT ptViewportOrg = { 0, 0 }; dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y, &ptViewportOrg); pT->DoPaint(dc); dc.SetMapMode(nMapModeSav); dc.SetWindowExt(szWindowExt); dc.SetViewportExt(szViewportExt); dc.SetViewportOrg(ptViewportOrg); } else { CPaintDC dc(pT->m_hWnd); pT->PrepareDC(dc.m_hDC); pT->DoPaint(dc.m_hDC); } return 0; } LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { if(m_nZoomMode == ZOOMMODE_IN && !m_bTracking) { T* pT = static_cast(this); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; if(pT->PtInDevRect(pt)) { pT->SetCapture(); m_bTracking = true; ::SetRect(&m_rcTrack, pt.x, pt.y, pt.x, pt.y); } } bHandled = FALSE; return 0; } LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { if(m_bTracking) { T* pT = static_cast(this); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; if(pT->PtInDevRect(pt)) { pT->DrawTrackRect(); m_rcTrack.right = pt.x; m_rcTrack.bottom = pt.y; pT->DrawTrackRect(); } } bHandled = FALSE; return 0; } LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { ::ReleaseCapture(); if(m_nZoomMode == ZOOMMODE_OUT) { T* pT = static_cast(this); pT->Zoom(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), m_fZoomScale - m_fZoomDelta); pT->NotifyParentZoomChanged(); } bHandled = FALSE; return 0; } LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(m_bTracking) { m_bTracking = false; T* pT = static_cast(this); pT->DrawTrackRect(); pT->Zoom(m_rcTrack); pT->NotifyParentZoomChanged(); ::SetRectEmpty(&m_rcTrack); } bHandled = FALSE; return 0; } LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if(LOWORD(lParam) == HTCLIENT && m_nZoomMode != ZOOMMODE_OFF) { T* pT = static_cast(this); if((HWND)wParam == pT->m_hWnd) { DWORD dwPos = ::GetMessagePos(); POINT pt = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) }; pT->ScreenToClient(&pt); if(pT->PtInDevRect(pt)) { ::SetCursor(::LoadCursor(NULL, IDC_CROSS)); return 1; } } } bHandled = FALSE; return 0; } }; /////////////////////////////////////////////////////////////////////////////// // CZoomScrollWindowImpl - Implements scrolling window with zooming template class ATL_NO_VTABLE CZoomScrollWindowImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CZoomScrollImpl< T > { public: BOOL SubclassWindow(HWND hWnd) { #if (_MSC_VER >= 1300) BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); #else // !(_MSC_VER >= 1300) / typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass; BOOL bRet = _baseClass::SubclassWindow(hWnd); #endif // !(_MSC_VER >= 1300) if(bRet != FALSE) { T* pT = static_cast(this); pT->GetSystemSettings(); RECT rect = { 0 }; GetClientRect(&rect); pT->DoSize(rect.right, rect.bottom); } return bRet; } BEGIN_MSG_MAP(CZoomScrollWindowImpl) MESSAGE_HANDLER(WM_SETCURSOR, CZoomScrollImpl< T >::OnSetCursor) MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll) MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll) MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel) #if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) MESSAGE_HANDLER(m_uMsgMouseWheel, CScrollImpl< T >::OnMouseWheel) #endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScrollImpl< T >::OnMouseHWheel) MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange) MESSAGE_HANDLER(WM_SIZE, CScrollImpl< T >::OnSize) MESSAGE_HANDLER(WM_PAINT, CZoomScrollImpl< T >::OnPaint) MESSAGE_HANDLER(WM_PRINTCLIENT, CZoomScrollImpl< T >::OnPaint) MESSAGE_HANDLER(WM_LBUTTONDOWN, CZoomScrollImpl< T >::OnLButtonDown) MESSAGE_HANDLER(WM_MOUSEMOVE, CZoomScrollImpl< T >::OnMouseMove) MESSAGE_HANDLER(WM_LBUTTONUP, CZoomScrollImpl< T >::OnLButtonUp) MESSAGE_HANDLER(WM_CAPTURECHANGED, CZoomScrollImpl< T >::OnCaptureChanged) ALT_MSG_MAP(1) COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp) COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown) COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop) COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom) COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft) COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft) COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight) COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft) COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight) END_MSG_MAP() }; #endif // !_WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // CScrollContainer template class ATL_NO_VTABLE CScrollContainerImpl : public CScrollWindowImpl< T, TBase, TWinTraits > { public: DECLARE_WND_CLASS_EX(NULL, 0, -1) typedef CScrollWindowImpl< T, TBase, TWinTraits > _baseClass; // Data members ATL::CWindow m_wndClient; bool m_bAutoSizeClient; bool m_bDrawEdgeIfEmpty; // Constructor CScrollContainerImpl() : m_bAutoSizeClient(true), m_bDrawEdgeIfEmpty(false) { // Set CScrollWindowImpl extended style SetScrollExtendedStyle(SCRL_SCROLLCHILDREN); } // Attributes HWND GetClient() const { return m_wndClient; } HWND SetClient(HWND hWndClient, bool bClientSizeAsMin = true) { ATLASSERT(::IsWindow(m_hWnd)); HWND hWndOldClient = m_wndClient; m_wndClient = hWndClient; SetRedraw(FALSE); SetScrollSize(1, 1, FALSE); if(m_wndClient.m_hWnd != NULL) { m_wndClient.SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE); if(bClientSizeAsMin) { RECT rect = { 0 }; m_wndClient.GetWindowRect(&rect); if((rect.right - rect.left) > 0 && (rect.bottom - rect.top) > 0) SetScrollSize(rect.right - rect.left, rect.bottom - rect.top, FALSE); } T* pT = static_cast(this); pT->UpdateLayout(); } SetRedraw(TRUE); RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); return hWndOldClient; } // Message map and handlers BEGIN_MSG_MAP(CScrollContainerImpl) MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) CHAIN_MSG_MAP(_baseClass) FORWARD_NOTIFICATIONS() ALT_MSG_MAP(1) CHAIN_MSG_MAP_ALT(_baseClass, 1) END_MSG_MAP() LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { if(m_wndClient.m_hWnd != NULL) m_wndClient.SetFocus(); return 0; } LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return 1; // no background needed } // Overrides for CScrollWindowImpl void DoSize(int cx, int cy) { _baseClass::DoSize(cx, cy); T* pT = static_cast(this); pT->UpdateLayout(); } void DoPaint(CDCHandle dc) { if(!m_bAutoSizeClient || m_wndClient.m_hWnd == NULL) { T* pT = static_cast(this); RECT rect = { 0 }; pT->GetContainerRect(rect); if(m_bDrawEdgeIfEmpty && m_wndClient.m_hWnd == NULL) dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); dc.FillRect(&rect, COLOR_APPWORKSPACE); } } void ScrollToView(POINT pt) { CScrollWindowImpl< T, TBase, TWinTraits >::ScrollToView(pt); } void ScrollToView(RECT& rect) { CScrollWindowImpl< T, TBase, TWinTraits >::ScrollToView(rect); } void ScrollToView(HWND hWnd) // client window coordinates { T* pT = static_cast(this); pT; // avoid level 4 warning ATLASSERT(::IsWindow(pT->m_hWnd)); ATLASSERT(m_wndClient.IsWindow()); RECT rect = { 0 }; ::GetWindowRect(hWnd, &rect); ::MapWindowPoints(NULL, m_wndClient.m_hWnd, (LPPOINT)&rect, 2); ScrollToView(rect); } // Implementation - overrideable methods void UpdateLayout() { ATLASSERT(::IsWindow(m_hWnd)); if(m_bAutoSizeClient && m_wndClient.m_hWnd != NULL) { T* pT = static_cast(this); RECT rect = { 0 }; pT->GetContainerRect(rect); m_wndClient.SetWindowPos(NULL, &rect, SWP_NOZORDER | SWP_NOMOVE); } else { Invalidate(); } } void GetContainerRect(RECT& rect) { GetClientRect(&rect); if(rect.right < m_sizeAll.cx) rect.right = m_sizeAll.cx; if(rect.bottom < m_sizeAll.cy) rect.bottom = m_sizeAll.cy; } }; class CScrollContainer : public CScrollContainerImpl { public: DECLARE_WND_CLASS_EX(_T("WTL_ScrollContainer"), 0, -1) }; }; // namespace WTL #endif // __ATLSCRL_H__ ================================================ FILE: src/Setup/wtl90/atlsplit.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLSPLIT_H__ #define __ATLSPLIT_H__ #pragma once #ifndef __ATLAPP_H__ #error atlsplit.h requires atlapp.h to be included first #endif #ifndef __ATLWIN_H__ #error atlsplit.h requires atlwin.h to be included first #endif /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CSplitterImpl // CSplitterWindowImpl // CSplitterWindowT - CSplitterWindow, CHorSplitterWindow namespace WTL { /////////////////////////////////////////////////////////////////////////////// // CSplitterImpl - Provides splitter support to any window // Splitter panes constants #define SPLIT_PANE_LEFT 0 #define SPLIT_PANE_RIGHT 1 #define SPLIT_PANE_TOP SPLIT_PANE_LEFT #define SPLIT_PANE_BOTTOM SPLIT_PANE_RIGHT #define SPLIT_PANE_NONE -1 // Splitter extended styles #define SPLIT_PROPORTIONAL 0x00000001 #define SPLIT_NONINTERACTIVE 0x00000002 #define SPLIT_RIGHTALIGNED 0x00000004 #define SPLIT_BOTTOMALIGNED SPLIT_RIGHTALIGNED #define SPLIT_GRADIENTBAR 0x00000008 #define SPLIT_FIXEDBARSIZE 0x00000010 // Note: SPLIT_PROPORTIONAL and SPLIT_RIGHTALIGNED/SPLIT_BOTTOMALIGNED are // mutually exclusive. If both are set, splitter defaults to SPLIT_PROPORTIONAL. // SPLIT_GRADIENTBAR doesn't wotk with _ATL_NO_MSIMG template class CSplitterImpl { public: enum { m_nPanesCount = 2, m_nPropMax = 10000, m_cxyStep = 10 }; bool m_bVertical; HWND m_hWndPane[m_nPanesCount]; RECT m_rcSplitter; int m_xySplitterPos; // splitter bar position int m_xySplitterPosNew; // internal - new position while moving HWND m_hWndFocusSave; int m_nDefActivePane; int m_cxySplitBar; // splitter bar width/height HCURSOR m_hCursor; int m_cxyMin; // minimum pane size int m_cxyBarEdge; // splitter bar edge bool m_bFullDrag; int m_cxyDragOffset; // internal int m_nProportionalPos; bool m_bUpdateProportionalPos; DWORD m_dwExtendedStyle; // splitter specific extended styles int m_nSinglePane; // single pane mode int m_xySplitterDefPos; // default position bool m_bProportionalDefPos; // porportinal def pos // Constructor CSplitterImpl(bool bVertical = true) : m_bVertical(bVertical), m_xySplitterPos(-1), m_xySplitterPosNew(-1), m_hWndFocusSave(NULL), m_nDefActivePane(SPLIT_PANE_NONE), m_cxySplitBar(4), m_hCursor(NULL), m_cxyMin(0), m_cxyBarEdge(0), m_bFullDrag(true), m_cxyDragOffset(0), m_nProportionalPos(0), m_bUpdateProportionalPos(true), m_dwExtendedStyle(SPLIT_PROPORTIONAL), m_nSinglePane(SPLIT_PANE_NONE), m_xySplitterDefPos(-1), m_bProportionalDefPos(false) { m_hWndPane[SPLIT_PANE_LEFT] = NULL; m_hWndPane[SPLIT_PANE_RIGHT] = NULL; ::SetRectEmpty(&m_rcSplitter); } // Attributes void SetSplitterRect(LPRECT lpRect = NULL, bool bUpdate = true) { if(lpRect == NULL) { T* pT = static_cast(this); pT->GetClientRect(&m_rcSplitter); } else { m_rcSplitter = *lpRect; } if(IsProportional()) UpdateProportionalPos(); else if(IsRightAligned()) UpdateRightAlignPos(); if(bUpdate) UpdateSplitterLayout(); } void GetSplitterRect(LPRECT lpRect) const { ATLASSERT(lpRect != NULL); *lpRect = m_rcSplitter; } bool SetSplitterPos(int xyPos = -1, bool bUpdate = true) { if(xyPos == -1) // -1 == default position { if(m_bProportionalDefPos) { ATLASSERT((m_xySplitterDefPos >= 0) && (m_xySplitterDefPos <= m_nPropMax)); if(m_bVertical) xyPos = ::MulDiv(m_xySplitterDefPos, m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge, m_nPropMax); else xyPos = ::MulDiv(m_xySplitterDefPos, m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge, m_nPropMax); } else if(m_xySplitterDefPos != -1) { xyPos = m_xySplitterDefPos; } else // not set, use middle position { if(m_bVertical) xyPos = (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) / 2; else xyPos = (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge) / 2; } } // Adjust if out of valid range int cxyMax = 0; if(m_bVertical) cxyMax = m_rcSplitter.right - m_rcSplitter.left; else cxyMax = m_rcSplitter.bottom - m_rcSplitter.top; if(xyPos < m_cxyMin + m_cxyBarEdge) xyPos = m_cxyMin; else if(xyPos > (cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin)) xyPos = cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin; // Set new position and update if requested bool bRet = (m_xySplitterPos != xyPos); m_xySplitterPos = xyPos; if(m_bUpdateProportionalPos) { if(IsProportional()) StoreProportionalPos(); else if(IsRightAligned()) StoreRightAlignPos(); } else { m_bUpdateProportionalPos = true; } if(bUpdate && bRet) UpdateSplitterLayout(); return bRet; } void SetSplitterPosPct(int nPct, bool bUpdate = true) { ATLASSERT((nPct >= 0) && (nPct <= 100)); m_nProportionalPos = ::MulDiv(nPct, m_nPropMax, 100); UpdateProportionalPos(); if(bUpdate) UpdateSplitterLayout(); } int GetSplitterPos() const { return m_xySplitterPos; } bool SetSinglePaneMode(int nPane = SPLIT_PANE_NONE) { ATLASSERT((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT) || (nPane == SPLIT_PANE_NONE)); if(!((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT) || (nPane == SPLIT_PANE_NONE))) return false; if(nPane != SPLIT_PANE_NONE) { if(::IsWindowVisible(m_hWndPane[nPane]) == FALSE) ::ShowWindow(m_hWndPane[nPane], SW_SHOW); int nOtherPane = (nPane == SPLIT_PANE_LEFT) ? SPLIT_PANE_RIGHT : SPLIT_PANE_LEFT; ::ShowWindow(m_hWndPane[nOtherPane], SW_HIDE); if(m_nDefActivePane != nPane) m_nDefActivePane = nPane; } else if(m_nSinglePane != SPLIT_PANE_NONE) { int nOtherPane = (m_nSinglePane == SPLIT_PANE_LEFT) ? SPLIT_PANE_RIGHT : SPLIT_PANE_LEFT; ::ShowWindow(m_hWndPane[nOtherPane], SW_SHOW); } m_nSinglePane = nPane; UpdateSplitterLayout(); return true; } int GetSinglePaneMode() const { return m_nSinglePane; } DWORD GetSplitterExtendedStyle() const { return m_dwExtendedStyle; } DWORD SetSplitterExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0) { DWORD dwPrevStyle = m_dwExtendedStyle; if(dwMask == 0) m_dwExtendedStyle = dwExtendedStyle; else m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask); #ifdef _DEBUG if(IsProportional() && IsRightAligned()) ATLTRACE2(atlTraceUI, 0, _T("CSplitterImpl::SetSplitterExtendedStyle - SPLIT_PROPORTIONAL and SPLIT_RIGHTALIGNED are mutually exclusive, defaulting to SPLIT_PROPORTIONAL.\n")); #endif // _DEBUG return dwPrevStyle; } void SetSplitterDefaultPos(int xyPos = -1) { m_xySplitterDefPos = xyPos; m_bProportionalDefPos = false; } void SetSplitterDefaultPosPct(int nPct) { ATLASSERT((nPct >= 0) && (nPct <= 100)); m_xySplitterDefPos = ::MulDiv(nPct, m_nPropMax, 100); m_bProportionalDefPos = true; } // Splitter operations void SetSplitterPanes(HWND hWndLeftTop, HWND hWndRightBottom, bool bUpdate = true) { m_hWndPane[SPLIT_PANE_LEFT] = hWndLeftTop; m_hWndPane[SPLIT_PANE_RIGHT] = hWndRightBottom; ATLASSERT((m_hWndPane[SPLIT_PANE_LEFT] == NULL) || (m_hWndPane[SPLIT_PANE_RIGHT] == NULL) || (m_hWndPane[SPLIT_PANE_LEFT] != m_hWndPane[SPLIT_PANE_RIGHT])); if(bUpdate) UpdateSplitterLayout(); } bool SetSplitterPane(int nPane, HWND hWnd, bool bUpdate = true) { ATLASSERT((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT)); if((nPane != SPLIT_PANE_LEFT) && (nPane != SPLIT_PANE_RIGHT)) return false; m_hWndPane[nPane] = hWnd; ATLASSERT((m_hWndPane[SPLIT_PANE_LEFT] == NULL) || (m_hWndPane[SPLIT_PANE_RIGHT] == NULL) || (m_hWndPane[SPLIT_PANE_LEFT] != m_hWndPane[SPLIT_PANE_RIGHT])); if(bUpdate) UpdateSplitterLayout(); return true; } HWND GetSplitterPane(int nPane) const { ATLASSERT((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT)); if((nPane != SPLIT_PANE_LEFT) && (nPane != SPLIT_PANE_RIGHT)) return false; return m_hWndPane[nPane]; } bool SetActivePane(int nPane) { ATLASSERT((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT)); if((nPane != SPLIT_PANE_LEFT) && (nPane != SPLIT_PANE_RIGHT)) return false; if((m_nSinglePane != SPLIT_PANE_NONE) && (nPane != m_nSinglePane)) return false; ::SetFocus(m_hWndPane[nPane]); m_nDefActivePane = nPane; return true; } int GetActivePane() const { int nRet = SPLIT_PANE_NONE; HWND hWndFocus = ::GetFocus(); if(hWndFocus != NULL) { for(int nPane = 0; nPane < m_nPanesCount; nPane++) { if((hWndFocus == m_hWndPane[nPane]) || (::IsChild(m_hWndPane[nPane], hWndFocus) != FALSE)) { nRet = nPane; break; } } } return nRet; } bool ActivateNextPane(bool bNext = true) { int nPane = m_nSinglePane; if(nPane == SPLIT_PANE_NONE) { switch(GetActivePane()) { case SPLIT_PANE_LEFT: nPane = SPLIT_PANE_RIGHT; break; case SPLIT_PANE_RIGHT: nPane = SPLIT_PANE_LEFT; break; default: nPane = bNext ? SPLIT_PANE_LEFT : SPLIT_PANE_RIGHT; break; } } return SetActivePane(nPane); } bool SetDefaultActivePane(int nPane) { ATLASSERT((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT)); if((nPane != SPLIT_PANE_LEFT) && (nPane != SPLIT_PANE_RIGHT)) return false; m_nDefActivePane = nPane; return true; } bool SetDefaultActivePane(HWND hWnd) { for(int nPane = 0; nPane < m_nPanesCount; nPane++) { if(hWnd == m_hWndPane[nPane]) { m_nDefActivePane = nPane; return true; } } return false; // not found } int GetDefaultActivePane() const { return m_nDefActivePane; } void DrawSplitter(CDCHandle dc) { ATLASSERT(dc.m_hDC != NULL); if((m_nSinglePane == SPLIT_PANE_NONE) && (m_xySplitterPos == -1)) return; T* pT = static_cast(this); if(m_nSinglePane == SPLIT_PANE_NONE) { pT->DrawSplitterBar(dc); for(int nPane = 0; nPane < m_nPanesCount; nPane++) { if(m_hWndPane[nPane] == NULL) pT->DrawSplitterPane(dc, nPane); } } else { if(m_hWndPane[m_nSinglePane] == NULL) pT->DrawSplitterPane(dc, m_nSinglePane); } } // call to initiate moving splitter bar with keyboard void MoveSplitterBar() { T* pT = static_cast(this); int x = 0; int y = 0; if(m_bVertical) { x = m_xySplitterPos + (m_cxySplitBar / 2) + m_cxyBarEdge; y = (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge) / 2; } else { x = (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) / 2; y = m_xySplitterPos + (m_cxySplitBar / 2) + m_cxyBarEdge; } POINT pt = { x, y }; pT->ClientToScreen(&pt); ::SetCursorPos(pt.x, pt.y); m_xySplitterPosNew = m_xySplitterPos; pT->SetCapture(); m_hWndFocusSave = pT->SetFocus(); ::SetCursor(m_hCursor); if(!m_bFullDrag) DrawGhostBar(); if(m_bVertical) m_cxyDragOffset = x - m_rcSplitter.left - m_xySplitterPos; else m_cxyDragOffset = y - m_rcSplitter.top - m_xySplitterPos; } void SetOrientation(bool bVertical, bool bUpdate = true) { if(m_bVertical != bVertical) { m_bVertical = bVertical; m_hCursor = ::LoadCursor(NULL, m_bVertical ? IDC_SIZEWE : IDC_SIZENS); T* pT = static_cast(this); pT->GetSystemSettings(false); if(m_bVertical) m_xySplitterPos = ::MulDiv(m_xySplitterPos, m_rcSplitter.right - m_rcSplitter.left, m_rcSplitter.bottom - m_rcSplitter.top); else m_xySplitterPos = ::MulDiv(m_xySplitterPos, m_rcSplitter.bottom - m_rcSplitter.top, m_rcSplitter.right - m_rcSplitter.left); } if(bUpdate) UpdateSplitterLayout(); } // Overrideables void DrawSplitterBar(CDCHandle dc) { RECT rect = { 0 }; if(GetSplitterBarRect(&rect)) { dc.FillRect(&rect, COLOR_3DFACE); #if (!defined(_WIN32_WCE) && !defined(_ATL_NO_MSIMG)) || (_WIN32_WCE >= 420) if((m_dwExtendedStyle & SPLIT_GRADIENTBAR) != 0) { RECT rect2 = rect; if(m_bVertical) rect2.left = (rect.left + rect.right) / 2 - 1; else rect2.top = (rect.top + rect.bottom) / 2 - 1; dc.GradientFillRect(rect2, ::GetSysColor(COLOR_3DFACE), ::GetSysColor(COLOR_3DSHADOW), m_bVertical); } #endif // (!defined(_WIN32_WCE) && !defined(_ATL_NO_MSIMG)) || (_WIN32_WCE >= 420) // draw 3D edge if needed T* pT = static_cast(this); if((pT->GetExStyle() & WS_EX_CLIENTEDGE) != 0) dc.DrawEdge(&rect, EDGE_RAISED, m_bVertical ? (BF_LEFT | BF_RIGHT) : (BF_TOP | BF_BOTTOM)); } } // called only if pane is empty void DrawSplitterPane(CDCHandle dc, int nPane) { RECT rect = { 0 }; if(GetSplitterPaneRect(nPane, &rect)) { T* pT = static_cast(this); if((pT->GetExStyle() & WS_EX_CLIENTEDGE) == 0) dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); dc.FillRect(&rect, COLOR_APPWORKSPACE); } } // Message map and handlers BEGIN_MSG_MAP(CSplitterImpl) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_PAINT, OnPaint) #ifndef _WIN32_WCE MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint) #endif // !_WIN32_WCE if(IsInteractive()) { MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor) MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown) MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp) MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDoubleClick) MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged) MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown) } MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) #ifndef _WIN32_WCE MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate) #endif // !_WIN32_WCE MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange) END_MSG_MAP() LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); pT->Init(); bHandled = FALSE; return 1; } LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); // try setting position if not set if((m_nSinglePane == SPLIT_PANE_NONE) && (m_xySplitterPos == -1)) pT->SetSplitterPos(); // do painting if(wParam != NULL) { pT->DrawSplitter((HDC)wParam); } else { CPaintDC dc(pT->m_hWnd); pT->DrawSplitter(dc.m_hDC); } return 0; } LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { T* pT = static_cast(this); if(((HWND)wParam == pT->m_hWnd) && (LOWORD(lParam) == HTCLIENT)) { DWORD dwPos = ::GetMessagePos(); POINT ptPos = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) }; pT->ScreenToClient(&ptPos); if(IsOverSplitterBar(ptPos.x, ptPos.y)) return 1; } bHandled = FALSE; return 0; } LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { T* pT = static_cast(this); int xPos = GET_X_LPARAM(lParam); int yPos = GET_Y_LPARAM(lParam); if(::GetCapture() == pT->m_hWnd) { int xyNewSplitPos = 0; if(m_bVertical) xyNewSplitPos = xPos - m_rcSplitter.left - m_cxyDragOffset; else xyNewSplitPos = yPos - m_rcSplitter.top - m_cxyDragOffset; if(xyNewSplitPos == -1) // avoid -1, that means default position xyNewSplitPos = -2; if(m_xySplitterPos != xyNewSplitPos) { if(m_bFullDrag) { if(pT->SetSplitterPos(xyNewSplitPos, true)) pT->UpdateWindow(); } else { DrawGhostBar(); pT->SetSplitterPos(xyNewSplitPos, false); DrawGhostBar(); } } } else // not dragging, just set cursor { if(IsOverSplitterBar(xPos, yPos)) ::SetCursor(m_hCursor); bHandled = FALSE; } return 0; } LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { T* pT = static_cast(this); int xPos = GET_X_LPARAM(lParam); int yPos = GET_Y_LPARAM(lParam); if((::GetCapture() != pT->m_hWnd) && IsOverSplitterBar(xPos, yPos)) { m_xySplitterPosNew = m_xySplitterPos; pT->SetCapture(); m_hWndFocusSave = pT->SetFocus(); ::SetCursor(m_hCursor); if(!m_bFullDrag) DrawGhostBar(); if(m_bVertical) m_cxyDragOffset = xPos - m_rcSplitter.left - m_xySplitterPos; else m_cxyDragOffset = yPos - m_rcSplitter.top - m_xySplitterPos; } else if((::GetCapture() == pT->m_hWnd) && !IsOverSplitterBar(xPos, yPos)) { ::ReleaseCapture(); } bHandled = FALSE; return 1; } LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); if(::GetCapture() == pT->m_hWnd) { m_xySplitterPosNew = m_xySplitterPos; ::ReleaseCapture(); } bHandled = FALSE; return 1; } LRESULT OnLButtonDoubleClick(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->SetSplitterPos(); // default return 0; } LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { if(!m_bFullDrag) DrawGhostBar(); if(!m_bFullDrag || (m_xySplitterPos != m_xySplitterPosNew)) { m_xySplitterPos = m_xySplitterPosNew; UpdateSplitterLayout(); T* pT = static_cast(this); pT->UpdateWindow(); } if(m_hWndFocusSave != NULL) ::SetFocus(m_hWndFocusSave); return 0; } LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); if(::GetCapture() == pT->m_hWnd) { switch(wParam) { case VK_RETURN: m_xySplitterPosNew = m_xySplitterPos; case VK_ESCAPE: ::ReleaseCapture(); break; case VK_LEFT: case VK_RIGHT: if(m_bVertical) { POINT pt = { 0, 0 }; ::GetCursorPos(&pt); int xyPos = m_xySplitterPos + ((wParam == VK_LEFT) ? -pT->m_cxyStep : pT->m_cxyStep); int cxyMax = m_rcSplitter.right - m_rcSplitter.left; if(xyPos < (m_cxyMin + m_cxyBarEdge)) xyPos = m_cxyMin; else if(xyPos > (cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin)) xyPos = cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin; pt.x += xyPos - m_xySplitterPos; ::SetCursorPos(pt.x, pt.y); } break; case VK_UP: case VK_DOWN: if(!m_bVertical) { POINT pt = { 0, 0 }; ::GetCursorPos(&pt); int xyPos = m_xySplitterPos + ((wParam == VK_UP) ? -pT->m_cxyStep : pT->m_cxyStep); int cxyMax = m_rcSplitter.bottom - m_rcSplitter.top; if(xyPos < (m_cxyMin + m_cxyBarEdge)) xyPos = m_cxyMin; else if(xyPos > (cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin)) xyPos = cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin; pt.y += xyPos - m_xySplitterPos; ::SetCursorPos(pt.x, pt.y); } break; default: break; } } else { bHandled = FALSE; } return 0; } LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM, BOOL& bHandled) { T* pT = static_cast(this); if(::GetCapture() != pT->m_hWnd) { if(m_nSinglePane == SPLIT_PANE_NONE) { if((m_nDefActivePane == SPLIT_PANE_LEFT) || (m_nDefActivePane == SPLIT_PANE_RIGHT)) ::SetFocus(m_hWndPane[m_nDefActivePane]); } else { ::SetFocus(m_hWndPane[m_nSinglePane]); } } bHandled = FALSE; return 1; } #ifndef _WIN32_WCE LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { T* pT = static_cast(this); LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam); if((lRet == MA_ACTIVATE) || (lRet == MA_ACTIVATEANDEAT)) { DWORD dwPos = ::GetMessagePos(); POINT pt = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) }; pT->ScreenToClient(&pt); RECT rcPane = { 0 }; for(int nPane = 0; nPane < m_nPanesCount; nPane++) { if(GetSplitterPaneRect(nPane, &rcPane) && (::PtInRect(&rcPane, pt) != FALSE)) { m_nDefActivePane = nPane; break; } } } return lRet; } #endif // !_WIN32_WCE LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->GetSystemSettings(true); return 0; } // Implementation - internal helpers void Init() { m_hCursor = ::LoadCursor(NULL, m_bVertical ? IDC_SIZEWE : IDC_SIZENS); T* pT = static_cast(this); pT->GetSystemSettings(false); } void UpdateSplitterLayout() { if((m_nSinglePane == SPLIT_PANE_NONE) && (m_xySplitterPos == -1)) return; T* pT = static_cast(this); RECT rect = { 0 }; if(m_nSinglePane == SPLIT_PANE_NONE) { if(GetSplitterBarRect(&rect)) pT->InvalidateRect(&rect); for(int nPane = 0; nPane < m_nPanesCount; nPane++) { if(GetSplitterPaneRect(nPane, &rect)) { if(m_hWndPane[nPane] != NULL) ::SetWindowPos(m_hWndPane[nPane], NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER); else pT->InvalidateRect(&rect); } } } else { if(GetSplitterPaneRect(m_nSinglePane, &rect)) { if(m_hWndPane[m_nSinglePane] != NULL) ::SetWindowPos(m_hWndPane[m_nSinglePane], NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER); else pT->InvalidateRect(&rect); } } } bool GetSplitterBarRect(LPRECT lpRect) const { ATLASSERT(lpRect != NULL); if((m_nSinglePane != SPLIT_PANE_NONE) || (m_xySplitterPos == -1)) return false; if(m_bVertical) { lpRect->left = m_rcSplitter.left + m_xySplitterPos; lpRect->top = m_rcSplitter.top; lpRect->right = m_rcSplitter.left + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge; lpRect->bottom = m_rcSplitter.bottom; } else { lpRect->left = m_rcSplitter.left; lpRect->top = m_rcSplitter.top + m_xySplitterPos; lpRect->right = m_rcSplitter.right; lpRect->bottom = m_rcSplitter.top + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge; } return true; } bool GetSplitterPaneRect(int nPane, LPRECT lpRect) const { ATLASSERT((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT)); ATLASSERT(lpRect != NULL); bool bRet = true; if(m_nSinglePane != SPLIT_PANE_NONE) { if(nPane == m_nSinglePane) *lpRect = m_rcSplitter; else bRet = false; } else if(nPane == SPLIT_PANE_LEFT) { if(m_bVertical) { lpRect->left = m_rcSplitter.left; lpRect->top = m_rcSplitter.top; lpRect->right = m_rcSplitter.left + m_xySplitterPos; lpRect->bottom = m_rcSplitter.bottom; } else { lpRect->left = m_rcSplitter.left; lpRect->top = m_rcSplitter.top; lpRect->right = m_rcSplitter.right; lpRect->bottom = m_rcSplitter.top + m_xySplitterPos; } } else if(nPane == SPLIT_PANE_RIGHT) { if(m_bVertical) { lpRect->left = m_rcSplitter.left + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge; lpRect->top = m_rcSplitter.top; lpRect->right = m_rcSplitter.right; lpRect->bottom = m_rcSplitter.bottom; } else { lpRect->left = m_rcSplitter.left; lpRect->top = m_rcSplitter.top + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge; lpRect->right = m_rcSplitter.right; lpRect->bottom = m_rcSplitter.bottom; } } else { bRet = false; } return bRet; } bool IsOverSplitterRect(int x, int y) const { // -1 == don't check return ((x == -1 || (x >= m_rcSplitter.left && x <= m_rcSplitter.right)) && (y == -1 || (y >= m_rcSplitter.top && y <= m_rcSplitter.bottom))); } bool IsOverSplitterBar(int x, int y) const { if(m_nSinglePane != SPLIT_PANE_NONE) return false; if((m_xySplitterPos == -1) || !IsOverSplitterRect(x, y)) return false; int xy = m_bVertical ? x : y; int xyOff = m_bVertical ? m_rcSplitter.left : m_rcSplitter.top; return ((xy >= (xyOff + m_xySplitterPos)) && (xy < xyOff + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge)); } void DrawGhostBar() { RECT rect = { 0 }; if(GetSplitterBarRect(&rect)) { // convert client to window coordinates T* pT = static_cast(this); RECT rcWnd = { 0 }; pT->GetWindowRect(&rcWnd); ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rcWnd, 2); ::OffsetRect(&rect, -rcWnd.left, -rcWnd.top); // invert the brush pattern (looks just like frame window sizing) CWindowDC dc(pT->m_hWnd); CBrush brush = CDCHandle::GetHalftoneBrush(); if(brush.m_hBrush != NULL) { CBrushHandle brushOld = dc.SelectBrush(brush); dc.PatBlt(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, PATINVERT); dc.SelectBrush(brushOld); } } } void GetSystemSettings(bool bUpdate) { if((m_dwExtendedStyle & SPLIT_FIXEDBARSIZE) == 0) { #ifndef _WIN32_WCE m_cxySplitBar = ::GetSystemMetrics(m_bVertical ? SM_CXSIZEFRAME : SM_CYSIZEFRAME); #else // CE specific m_cxySplitBar = 2 * ::GetSystemMetrics(m_bVertical ? SM_CXEDGE : SM_CYEDGE); #endif // _WIN32_WCE } T* pT = static_cast(this); if((pT->GetExStyle() & WS_EX_CLIENTEDGE) != 0) { m_cxyBarEdge = 2 * ::GetSystemMetrics(m_bVertical ? SM_CXEDGE : SM_CYEDGE); m_cxyMin = 0; } else { m_cxyBarEdge = 0; m_cxyMin = 2 * ::GetSystemMetrics(m_bVertical ? SM_CXEDGE : SM_CYEDGE); } #ifndef _WIN32_WCE ::SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &m_bFullDrag, 0); #endif // !_WIN32_WCE if(bUpdate) UpdateSplitterLayout(); } bool IsProportional() const { return ((m_dwExtendedStyle & SPLIT_PROPORTIONAL) != 0); } void StoreProportionalPos() { int cxyTotal = m_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge); if(cxyTotal > 0) m_nProportionalPos = ::MulDiv(m_xySplitterPos, m_nPropMax, cxyTotal); else m_nProportionalPos = 0; ATLTRACE2(atlTraceUI, 0, _T("CSplitterImpl::StoreProportionalPos - %i\n"), m_nProportionalPos); } void UpdateProportionalPos() { int cxyTotal = m_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge); if(cxyTotal > 0) { int xyNewPos = ::MulDiv(m_nProportionalPos, cxyTotal, m_nPropMax); m_bUpdateProportionalPos = false; T* pT = static_cast(this); pT->SetSplitterPos(xyNewPos, false); } } bool IsRightAligned() const { return ((m_dwExtendedStyle & SPLIT_RIGHTALIGNED) != 0); } void StoreRightAlignPos() { int cxyTotal = m_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge); if(cxyTotal > 0) m_nProportionalPos = cxyTotal - m_xySplitterPos; else m_nProportionalPos = 0; ATLTRACE2(atlTraceUI, 0, _T("CSplitterImpl::StoreRightAlignPos - %i\n"), m_nProportionalPos); } void UpdateRightAlignPos() { int cxyTotal = m_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge); if(cxyTotal > 0) { m_bUpdateProportionalPos = false; T* pT = static_cast(this); pT->SetSplitterPos(cxyTotal - m_nProportionalPos, false); } } bool IsInteractive() const { return ((m_dwExtendedStyle & SPLIT_NONINTERACTIVE) == 0); } }; /////////////////////////////////////////////////////////////////////////////// // CSplitterWindowImpl - Implements a splitter window template class ATL_NO_VTABLE CSplitterWindowImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CSplitterImpl< T > { public: DECLARE_WND_CLASS_EX(NULL, CS_DBLCLKS, COLOR_WINDOW) CSplitterWindowImpl(bool bVertical = true) : CSplitterImpl< T >(bVertical) { } BOOL SubclassWindow(HWND hWnd) { #if (_MSC_VER >= 1300) BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); #else // !(_MSC_VER >= 1300) typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass; BOOL bRet = _baseClass::SubclassWindow(hWnd); #endif // !(_MSC_VER >= 1300) if(bRet != FALSE) { T* pT = static_cast(this); pT->Init(); SetSplitterRect(); } return bRet; } BEGIN_MSG_MAP(CSplitterWindowImpl) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) MESSAGE_HANDLER(WM_SIZE, OnSize) CHAIN_MSG_MAP(CSplitterImpl< T >) FORWARD_NOTIFICATIONS() END_MSG_MAP() LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { // handled, no background painting needed return 1; } LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { if(wParam != SIZE_MINIMIZED) SetSplitterRect(); bHandled = FALSE; return 1; } }; /////////////////////////////////////////////////////////////////////////////// // CSplitterWindow/CHorSplitterWindow - Implements splitter windows to be used as is template class CSplitterWindowT : public CSplitterWindowImpl > { public: DECLARE_WND_CLASS_EX(_T("WTL_SplitterWindow"), CS_DBLCLKS, COLOR_WINDOW) CSplitterWindowT() : CSplitterWindowImpl >(t_bVertical) { } }; typedef CSplitterWindowT CSplitterWindow; typedef CSplitterWindowT CHorSplitterWindow; }; // namespace WTL #endif // __ATLSPLIT_H__ ================================================ FILE: src/Setup/wtl90/atltheme.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLTHEME_H__ #define __ATLTHEME_H__ #pragma once #ifdef _WIN32_WCE #error atltheme.h is not supported on Windows CE #endif #ifndef __ATLAPP_H__ #error atltheme.h requires atlapp.h to be included first #endif #ifndef __ATLWIN_H__ #error atltheme.h requires atlwin.h to be included first #endif #if (_WIN32_WINNT < 0x0501) #error atltheme.h requires _WIN32_WINNT >= 0x0501 #endif // (_WIN32_WINNT < 0x0501) #if defined(_WTL_USE_VSSYM32) || (defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN)) #include #else #ifndef TMSCHEMA_H #include #endif #endif #ifndef _UXTHEME_H_ #include #endif #pragma comment(lib, "uxtheme.lib") // Note: To create an application that also runs on older versions of Windows, // use delay load of uxtheme.dll and ensure that no calls to the Theme API are // made if theming is not supported. It is enough to check if m_hTheme is NULL. // Example: // if(m_hTheme != NULL) // { // DrawThemeBackground(dc, BP_PUSHBUTTON, PBS_NORMAL, &rect, NULL); // DrawThemeText(dc, BP_PUSHBUTTON, PBS_NORMAL, L"Button", -1, DT_SINGLELINE | DT_CENTER | DT_VCENTER, 0, &rect); // } // else // { // dc.DrawFrameControl(&rect, DFC_BUTTON, DFCS_BUTTONPUSH); // dc.DrawText(_T("Button"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); // } // // Delay load is NOT AUTOMATIC for VC++ 7, you have to link to delayimp.lib, // and add uxtheme.dll in the Linker.Input.Delay Loaded DLLs section of the // project properties. #if (_MSC_VER < 1300) && !defined(_WTL_NO_THEME_DELAYLOAD) #pragma comment(lib, "delayimp.lib") #pragma comment(linker, "/delayload:uxtheme.dll") #endif // (_MSC_VER < 1300) && !defined(_WTL_NO_THEME_DELAYLOAD) // Hack: Signatures in uxtheme.h changed - the only way to check which variant of uxtheme.h // is included is to check for presence of new defines MAX_THEMECOLOR and MAX_THEMESIZE // Note: In WinSDK 7.0 (and higher) they are defined with #if (_WIN32_WINNT >= 0x0600), // so you have to compile with _WTL_NEW_UXTHEME defined for _WIN32_WINNT < 0x0600 #ifndef _WTL_NEW_UXTHEME #if defined(MAX_THEMECOLOR) && defined(MAX_THEMESIZE) #define _WTL_NEW_UXTHEME #endif // defined(MAX_THEMECOLOR) && defined(MAX_THEMESIZE) #endif // _WTL_NEW_UXTHEME /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CTheme // CThemeImpl // // CBufferedPaint // CBufferedPaintImpl // CBufferedPaintWindowImpl // CBufferedAnimation // CBufferedAnimationImpl // CBufferedAnimationWindowImpl // // Global functions: // AtlDrawThemeClientEdge() namespace WTL { /////////////////////////////////////////////////////////////////////////////// // CTheme - wrapper for theme handle class CTheme { public: // Data members HTHEME m_hTheme; static int m_nIsThemingSupported; // Constructor CTheme(HTHEME hTheme = NULL) : m_hTheme(hTheme) { IsThemingSupported(); } // Operators and helpers bool IsThemeNull() const { return (m_hTheme == NULL); } CTheme& operator =(HTHEME hTheme) { m_hTheme = hTheme; return *this; } operator HTHEME() const { return m_hTheme; } void Attach(HTHEME hTheme) { m_hTheme = hTheme; } HTHEME Detach() { HTHEME hTheme = m_hTheme; m_hTheme = NULL; return hTheme; } // Theme support helper static bool IsThemingSupported() { if(m_nIsThemingSupported == -1) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CTheme::IsThemingSupported.\n")); ATLASSERT(FALSE); return false; } if(m_nIsThemingSupported == -1) { HMODULE hThemeDLL = ::LoadLibrary(_T("uxtheme.dll")); m_nIsThemingSupported = (hThemeDLL != NULL) ? 1 : 0; if(hThemeDLL != NULL) ::FreeLibrary(hThemeDLL); } lock.Unlock(); } ATLASSERT(m_nIsThemingSupported != -1); return (m_nIsThemingSupported == 1); } // Operations and theme properties HTHEME OpenThemeData(HWND hWnd, LPCWSTR pszClassList) { if(!IsThemingSupported()) return NULL; ATLASSERT(m_hTheme == NULL); m_hTheme = ::OpenThemeData(hWnd, pszClassList); return m_hTheme; } HRESULT CloseThemeData() { HRESULT hRet = S_FALSE; if(m_hTheme != NULL) { hRet = ::CloseThemeData(m_hTheme); if(SUCCEEDED(hRet)) m_hTheme = NULL; } return hRet; } HRESULT DrawThemeBackground(HDC hDC, int nPartID, int nStateID, LPCRECT pRect, LPCRECT pClipRect = NULL) { ATLASSERT(m_hTheme != NULL); return ::DrawThemeBackground(m_hTheme, hDC, nPartID, nStateID, pRect, pClipRect); } // Missing in original uxtheme.h #ifdef DTBG_CLIPRECT HRESULT DrawThemeBackgroundEx(HDC hDC, int nPartID, int nStateID, LPCRECT pRect, const DTBGOPTS* pOptions = NULL) { ATLASSERT(m_hTheme != NULL); return ::DrawThemeBackgroundEx(m_hTheme, hDC, nPartID, nStateID, pRect, pOptions); } #endif // DTBG_CLIPRECT HRESULT DrawThemeText(HDC hDC, int nPartID, int nStateID, LPCWSTR pszText, int nCharCount, DWORD dwTextFlags, DWORD dwTextFlags2, LPCRECT pRect) { ATLASSERT(m_hTheme != NULL); return ::DrawThemeText(m_hTheme, hDC, nPartID, nStateID, pszText, nCharCount, dwTextFlags, dwTextFlags2, pRect); } HRESULT GetThemeBackgroundContentRect(HDC hDC, int nPartID, int nStateID, LPCRECT pBoundingRect, LPRECT pContentRect) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeBackgroundContentRect(m_hTheme, hDC, nPartID, nStateID, pBoundingRect, pContentRect); } HRESULT GetThemeBackgroundExtent(HDC hDC, int nPartID, int nStateID, LPCRECT pContentRect, LPRECT pExtentRect) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeBackgroundExtent(m_hTheme, hDC, nPartID, nStateID, pContentRect, pExtentRect); } HRESULT GetThemePartSize(HDC hDC, int nPartID, int nStateID, LPCRECT pRect, enum THEMESIZE eSize, LPSIZE pSize) const { ATLASSERT(m_hTheme != NULL); #ifdef _WTL_NEW_UXTHEME return ::GetThemePartSize(m_hTheme, hDC, nPartID, nStateID, pRect, eSize, pSize); #else // !_WTL_NEW_UXTHEME // Note: The cast to LPRECT is because uxtheme.h incorrectly uses it instead of LPCRECT return ::GetThemePartSize(m_hTheme, hDC, nPartID, nStateID, (LPRECT)pRect, eSize, pSize); #endif // !_WTL_NEW_UXTHEME } HRESULT GetThemeTextExtent(HDC hDC, int nPartID, int nStateID, LPCWSTR pszText, int nCharCount, DWORD dwTextFlags, LPCRECT pBoundingRect, LPRECT pExtentRect) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeTextExtent(m_hTheme, hDC, nPartID, nStateID, pszText, nCharCount, dwTextFlags, pBoundingRect, pExtentRect); } HRESULT GetThemeTextMetrics(HDC hDC, int nPartID, int nStateID, PTEXTMETRICW pTextMetric) const { ATLASSERT(m_hTheme != NULL); #ifdef _WTL_NEW_UXTHEME return ::GetThemeTextMetrics(m_hTheme, hDC, nPartID, nStateID, pTextMetric); #else // !_WTL_NEW_UXTHEME // Note: The cast to PTEXTMETRIC is because uxtheme.h incorrectly uses it instead of PTEXTMETRICW return ::GetThemeTextMetrics(m_hTheme, hDC, nPartID, nStateID, (PTEXTMETRIC)pTextMetric); #endif // !_WTL_NEW_UXTHEME } HRESULT GetThemeBackgroundRegion(HDC hDC, int nPartID, int nStateID, LPCRECT pRect, HRGN* pRegion) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeBackgroundRegion(m_hTheme, hDC, nPartID, nStateID, pRect, pRegion); } HRESULT HitTestThemeBackground(HDC hDC, int nPartID, int nStateID, DWORD dwOptions, LPCRECT pRect, HRGN hrgn, POINT ptTest, WORD* pwHitTestCode) const { ATLASSERT(m_hTheme != NULL); return ::HitTestThemeBackground(m_hTheme, hDC, nPartID, nStateID, dwOptions, pRect, hrgn, ptTest, pwHitTestCode); } HRESULT DrawThemeEdge(HDC hDC, int nPartID, int nStateID, LPCRECT pDestRect, UINT uEdge, UINT uFlags, LPRECT pContentRect = NULL) { ATLASSERT(m_hTheme != NULL); return ::DrawThemeEdge(m_hTheme, hDC, nPartID, nStateID, pDestRect, uEdge, uFlags, pContentRect); } HRESULT DrawThemeIcon(HDC hDC, int nPartID, int nStateID, LPCRECT pRect, HIMAGELIST himl, int nImageIndex) { ATLASSERT(m_hTheme != NULL); return ::DrawThemeIcon(m_hTheme, hDC, nPartID, nStateID, pRect, himl, nImageIndex); } BOOL IsThemePartDefined(int nPartID, int nStateID) const { ATLASSERT(m_hTheme != NULL); return ::IsThemePartDefined(m_hTheme, nPartID, nStateID); } BOOL IsThemeBackgroundPartiallyTransparent(int nPartID, int nStateID) const { ATLASSERT(m_hTheme != NULL); return ::IsThemeBackgroundPartiallyTransparent(m_hTheme, nPartID, nStateID); } HRESULT GetThemeColor(int nPartID, int nStateID, int nPropID, COLORREF* pColor) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeColor(m_hTheme, nPartID, nStateID, nPropID, pColor); } HRESULT GetThemeMetric(HDC hDC, int nPartID, int nStateID, int nPropID, int* pnVal) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeMetric(m_hTheme, hDC, nPartID, nStateID, nPropID, pnVal); } HRESULT GetThemeString(int nPartID, int nStateID, int nPropID, LPWSTR pszBuff, int cchMaxBuffChars) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeString(m_hTheme, nPartID, nStateID, nPropID, pszBuff, cchMaxBuffChars); } HRESULT GetThemeBool(int nPartID, int nStateID, int nPropID, BOOL* pfVal) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeBool(m_hTheme, nPartID, nStateID, nPropID, pfVal); } HRESULT GetThemeInt(int nPartID, int nStateID, int nPropID, int* pnVal) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeInt(m_hTheme, nPartID, nStateID, nPropID, pnVal); } HRESULT GetThemeEnumValue(int nPartID, int nStateID, int nPropID, int* pnVal) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeEnumValue(m_hTheme, nPartID, nStateID, nPropID, pnVal); } HRESULT GetThemePosition(int nPartID, int nStateID, int nPropID, LPPOINT pPoint) const { ATLASSERT(m_hTheme != NULL); return ::GetThemePosition(m_hTheme, nPartID, nStateID, nPropID, pPoint); } // deprecated HRESULT GetThemeFont(int nPartID, HDC hDC, int nStateID, int nPropID, LOGFONTW* pFont) const { ATLASSERT(m_hTheme != NULL); #ifdef _WTL_NEW_UXTHEME return ::GetThemeFont(m_hTheme, hDC, nPartID, nStateID, nPropID, pFont); #else // !_WTL_NEW_UXTHEME // Note: The cast to LOGFONT* is because uxtheme.h incorrectly uses it instead of LOGFONTW* return ::GetThemeFont(m_hTheme, hDC, nPartID, nStateID, nPropID, (LOGFONT*)pFont); #endif // !_WTL_NEW_UXTHEME } HRESULT GetThemeFont(HDC hDC, int nPartID, int nStateID, int nPropID, LOGFONTW* pFont) const { ATLASSERT(m_hTheme != NULL); #ifdef _WTL_NEW_UXTHEME return ::GetThemeFont(m_hTheme, hDC, nPartID, nStateID, nPropID, pFont); #else // !_WTL_NEW_UXTHEME // Note: The cast to LOGFONT* is because uxtheme.h incorrectly uses it instead of LOGFONTW* return ::GetThemeFont(m_hTheme, hDC, nPartID, nStateID, nPropID, (LOGFONT*)pFont); #endif // !_WTL_NEW_UXTHEME } HRESULT GetThemeRect(int nPartID, int nStateID, int nPropID, LPRECT pRect) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeRect(m_hTheme, nPartID, nStateID, nPropID, pRect); } HRESULT GetThemeMargins(HDC hDC, int nPartID, int nStateID, int nPropID, LPRECT pRect, PMARGINS pMargins) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeMargins(m_hTheme, hDC, nPartID, nStateID, nPropID, pRect, pMargins); } HRESULT GetThemeIntList(int nPartID, int nStateID, int nPropID, INTLIST* pIntList) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeIntList(m_hTheme, nPartID, nStateID, nPropID, pIntList); } HRESULT GetThemePropertyOrigin(int nPartID, int nStateID, int nPropID, enum PROPERTYORIGIN* pOrigin) const { ATLASSERT(m_hTheme != NULL); return ::GetThemePropertyOrigin(m_hTheme, nPartID, nStateID, nPropID, pOrigin); } HRESULT GetThemeFilename(int nPartID, int nStateID, int nPropID, LPWSTR pszThemeFileName, int cchMaxBuffChars) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeFilename(m_hTheme, nPartID, nStateID, nPropID, pszThemeFileName, cchMaxBuffChars); } COLORREF GetThemeSysColor(int nColorID) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeSysColor(m_hTheme, nColorID); } HBRUSH GetThemeSysColorBrush(int nColorID) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeSysColorBrush(m_hTheme, nColorID); } int GetThemeSysSize(int nSizeID) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeSysSize(m_hTheme, nSizeID); } BOOL GetThemeSysBool(int nBoolID) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeSysBool(m_hTheme, nBoolID); } HRESULT GetThemeSysFont(int nFontID, LOGFONTW* plf) const { ATLASSERT(m_hTheme != NULL); #ifdef _WTL_NEW_UXTHEME return ::GetThemeSysFont(m_hTheme, nFontID, plf); #else // !_WTL_NEW_UXTHEME // Note: The cast to LOGFONT* is because uxtheme.h incorrectly uses it instead of LOGFONTW* return ::GetThemeSysFont(m_hTheme, nFontID, (LOGFONT*)plf); #endif // !_WTL_NEW_UXTHEME } HRESULT GetThemeSysString(int nStringID, LPWSTR pszStringBuff, int cchMaxStringChars) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeSysString(m_hTheme, nStringID, pszStringBuff, cchMaxStringChars); } HRESULT GetThemeSysInt(int nIntID, int* pnValue) const { ATLASSERT(m_hTheme != NULL); return ::GetThemeSysInt(m_hTheme, nIntID, pnValue); } #ifdef _WTL_NEW_UXTHEME HTHEME OpenThemeDataEx(HWND hWnd, LPCWSTR pszClassList, DWORD dwFlags) { if(!IsThemingSupported()) return NULL; ATLASSERT(m_hTheme == NULL); m_hTheme = ::OpenThemeDataEx(hWnd, pszClassList, dwFlags); return m_hTheme; } #if (_WIN32_WINNT >= 0x0600) HRESULT DrawThemeTextEx(HDC hDC, int nPartID, int nStateID, LPCWSTR pszText, int cchText, DWORD dwTextFlags, LPRECT lpRect, const DTTOPTS* pOptions) { ATLASSERT(m_hTheme != NULL); return ::DrawThemeTextEx(m_hTheme, hDC, nPartID, nStateID, pszText, cchText, dwTextFlags, lpRect, pOptions); } HRESULT GetThemeTransitionDuration(int nPartID, int nFromStateID, int nToStateID, int nPropID, DWORD& dwDuration) { ATLASSERT(m_hTheme != NULL); return ::GetThemeTransitionDuration(m_hTheme, nPartID, nFromStateID, nToStateID, nPropID, &dwDuration); } #endif // (_WIN32_WINNT >= 0x0600) #endif // _WTL_NEW_UXTHEME #if (_WIN32_WINNT >= 0x0600) HRESULT GetThemeBitmap(int nPartID, int nStateID, int nPropID, ULONG uFlags, HBITMAP& hBitmap) { ATLASSERT(m_hTheme != NULL); return ::GetThemeBitmap(m_hTheme, nPartID, nStateID, nPropID, uFlags, &hBitmap); } HRESULT GetThemeStream(int nPartID, int nStateID, int nPropID, VOID** ppvStream, DWORD* pcbStream, HINSTANCE hInstance) { ATLASSERT(m_hTheme != NULL); return ::GetThemeStream(m_hTheme, nPartID, nStateID, nPropID, ppvStream, pcbStream, hInstance); } #endif // (_WIN32_WINNT >= 0x0600) #if (_WIN32_WINNT >= 0x0602) HRESULT GetThemeAnimationProperty(int iStoryboardId, int iTargetId, TA_PROPERTY eProperty, VOID* pvProperty, DWORD cbSize, DWORD* pcbSizeOut) { ATLASSERT(m_hTheme != NULL); return ::GetThemeAnimationProperty(m_hTheme, iStoryboardId, iTargetId, eProperty, pvProperty, cbSize, pcbSizeOut); } HRESULT GetThemeAnimationTransform(int iStoryboardId, int iTargetId, DWORD dwTransformIndex, TA_TRANSFORM* pTransform, DWORD cbSize, DWORD* pcbSizeOut) { ATLASSERT(m_hTheme != NULL); return ::GetThemeAnimationTransform(m_hTheme, iStoryboardId, iTargetId, dwTransformIndex, pTransform, cbSize, pcbSizeOut); } HRESULT GetThemeTimingFunction(int iTimingFunctionId, TA_TIMINGFUNCTION* pTimingFunction, DWORD cbSize, DWORD* pcbSizeOut) { ATLASSERT(m_hTheme != NULL); return ::GetThemeTimingFunction(m_hTheme, iTimingFunctionId, pTimingFunction, cbSize, pcbSizeOut); } #endif // (_WIN32_WINNT >= 0x0602) }; __declspec(selectany) int CTheme::m_nIsThemingSupported = -1; /////////////////////////////////////////////////////////////////////////////// // CThemeImpl - theme support implementation // Derive from this class to implement window with theme support. // Example: // class CMyThemeWindow : public CWindowImpl, public CThemeImpl // { // ... // BEGIN_MSG_MAP(CMyThemeWindow) // CHAIN_MSG_MAP(CThemeImpl) // ... // END_MSG_MAP() // ... // }; // // If you set theme class list, the class will automaticaly open/close/reopen theme data. // Helper for drawing theme client edge inline bool AtlDrawThemeClientEdge(HTHEME hTheme, HWND hWnd, HRGN hRgnUpdate = NULL, HBRUSH hBrush = NULL, int nPartID = 0, int nStateID = 0) { ATLASSERT(hTheme != NULL); ATLASSERT(::IsWindow(hWnd)); CWindowDC dc(hWnd); if(dc.IsNull()) return false; // Get border size int cxBorder = ::GetSystemMetrics(SM_CXBORDER); int cyBorder = ::GetSystemMetrics(SM_CYBORDER); if(SUCCEEDED(::GetThemeInt(hTheme, nPartID, nStateID, TMT_SIZINGBORDERWIDTH, &cxBorder))) cyBorder = cxBorder; RECT rect = { 0 }; ::GetWindowRect(hWnd, &rect); // Remove the client edge from the update region int cxEdge = ::GetSystemMetrics(SM_CXEDGE); int cyEdge = ::GetSystemMetrics(SM_CYEDGE); ::InflateRect(&rect, -cxEdge, -cyEdge); CRgn rgn; rgn.CreateRectRgnIndirect(&rect); if(rgn.IsNull()) return false; if(hRgnUpdate != NULL) rgn.CombineRgn(hRgnUpdate, rgn, RGN_AND); ::OffsetRect(&rect, -rect.left, -rect.top); ::OffsetRect(&rect, cxEdge, cyEdge); dc.ExcludeClipRect(&rect); ::InflateRect(&rect, cxEdge, cyEdge); ::DrawThemeBackground(hTheme, dc, nPartID, nStateID, &rect, NULL); // Use background brush too, since theme border might not cover everything if((cxBorder < cxEdge) && (cyBorder < cyEdge)) { if(hBrush == NULL) // need conditional code because types don't match in winuser.h #ifdef _WIN64 hBrush = (HBRUSH)::GetClassLongPtr(hWnd, GCLP_HBRBACKGROUND); #else hBrush = (HBRUSH)UlongToPtr(::GetClassLongPtr(hWnd, GCLP_HBRBACKGROUND)); #endif ::InflateRect(&rect, cxBorder - cxEdge, cyBorder - cyEdge); dc.FillRect(&rect, hBrush); } ::DefWindowProc(hWnd, WM_NCPAINT, (WPARAM)rgn.m_hRgn, 0L); return true; } // Theme extended styles #define THEME_EX_3DCLIENTEDGE 0x00000001 #define THEME_EX_THEMECLIENTEDGE 0x00000002 template class CThemeImpl : public TBase { public: // Data members LPWSTR m_lpstrThemeClassList; DWORD m_dwExtendedStyle; // theme specific extended styles // Constructor & destructor CThemeImpl() : m_lpstrThemeClassList(NULL), m_dwExtendedStyle(0) { } ~CThemeImpl() { delete [] m_lpstrThemeClassList; } // Attributes bool SetThemeClassList(LPCWSTR lpstrThemeClassList) { if(m_lpstrThemeClassList != NULL) { delete [] m_lpstrThemeClassList; m_lpstrThemeClassList = NULL; } if(lpstrThemeClassList == NULL) return true; int cchLen = lstrlenW(lpstrThemeClassList) + 1; ATLTRY(m_lpstrThemeClassList = new WCHAR[cchLen]); if(m_lpstrThemeClassList == NULL) return false; SecureHelper::strcpyW_x(m_lpstrThemeClassList, cchLen, lpstrThemeClassList); return true; } bool GetThemeClassList(LPWSTR lpstrThemeClassList, int cchListBuffer) const { int cchLen = lstrlenW(m_lpstrThemeClassList) + 1; if(cchListBuffer < cchLen) return false; SecureHelper::strcpyW_x(lpstrThemeClassList, cchListBuffer, m_lpstrThemeClassList); return true; } LPCWSTR GetThemeClassList() const { return m_lpstrThemeClassList; } DWORD SetThemeExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0) { DWORD dwPrevStyle = m_dwExtendedStyle; if(dwMask == 0) m_dwExtendedStyle = dwExtendedStyle; else m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask); return dwPrevStyle; } DWORD GetThemeExtendedStyle() const { return m_dwExtendedStyle; } // Operations HTHEME OpenThemeData() { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); ATLASSERT(m_lpstrThemeClassList != NULL); if(m_lpstrThemeClassList == NULL) return NULL; CloseThemeData(); return TBase::OpenThemeData(pT->m_hWnd, m_lpstrThemeClassList); } HTHEME OpenThemeData(LPCWSTR pszClassList) { if(!SetThemeClassList(pszClassList)) return NULL; return OpenThemeData(); } HRESULT SetWindowTheme(LPCWSTR pszSubAppName, LPCWSTR pszSubIDList) { if(!IsThemingSupported()) return S_FALSE; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::SetWindowTheme(pT->m_hWnd, pszSubAppName, pszSubIDList); } HTHEME GetWindowTheme() const { if(!IsThemingSupported()) return NULL; const T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::GetWindowTheme(pT->m_hWnd); } HRESULT EnableThemeDialogTexture(DWORD dwFlags) { if(!IsThemingSupported()) return S_FALSE; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::EnableThemeDialogTexture(pT->m_hWnd, dwFlags); } BOOL IsThemeDialogTextureEnabled() const { if(!IsThemingSupported()) return FALSE; const T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::IsThemeDialogTextureEnabled(pT->m_hWnd); } HRESULT DrawThemeParentBackground(HDC hDC, const RECT* pRect = NULL) { if(!IsThemingSupported()) return S_FALSE; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); #ifdef _WTL_NEW_UXTHEME return ::DrawThemeParentBackground(pT->m_hWnd, hDC, pRect); #else return ::DrawThemeParentBackground(pT->m_hWnd, hDC, (RECT*)pRect); #endif } #if defined(_WTL_NEW_UXTHEME) && (_WIN32_WINNT >= 0x0600) HRESULT SetWindowThemeAttribute(WINDOWTHEMEATTRIBUTETYPE type, PVOID pvAttribute, DWORD cbAttribute) { if(!IsThemingSupported()) return S_FALSE; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::SetWindowThemeAttribute(pT->m_hWnd, type, pvAttribute, cbAttribute); } HRESULT SetWindowThemeNonClientAttributes(DWORD dwAttributes, DWORD dwMask) { if(!IsThemingSupported()) return S_FALSE; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); WTA_OPTIONS opt = { dwAttributes, dwMask }; return ::SetWindowThemeAttribute(pT->m_hWnd, WTA_NONCLIENT, (PVOID)&opt, sizeof(opt)); } HRESULT DrawThemeParentBackgroundEx(HDC hDC, DWORD dwFlags, const RECT* lpRect = NULL) { if(!IsThemingSupported()) return S_FALSE; T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); return ::DrawThemeParentBackgroundEx(pT->m_hWnd, hDC, dwFlags, lpRect); } #endif // defined(_WTL_NEW_UXTHEME) && (_WIN32_WINNT >= 0x0600) // Message map and handlers // Note: If you handle any of these messages in your derived class, // it is better to put CHAIN_MSG_MAP at the start of your message map. BEGIN_MSG_MAP(CThemeImpl) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged) MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint) END_MSG_MAP() LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(m_lpstrThemeClassList != NULL) OpenThemeData(); bHandled = FALSE; return 1; } LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { CloseThemeData(); bHandled = FALSE; return 1; } LRESULT OnThemeChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { CloseThemeData(); if(m_lpstrThemeClassList != NULL) OpenThemeData(); bHandled = FALSE; return 1; } LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); LRESULT lRet = 0; bHandled = FALSE; if(IsThemingSupported() && ((pT->GetExStyle() & WS_EX_CLIENTEDGE) != 0)) { if((m_dwExtendedStyle & THEME_EX_3DCLIENTEDGE) != 0) { lRet = ::DefWindowProc(pT->m_hWnd, uMsg, wParam, lParam); bHandled = TRUE; } else if((m_hTheme != NULL) && ((m_dwExtendedStyle & THEME_EX_THEMECLIENTEDGE) != 0)) { HRGN hRgn = (wParam != 1) ? (HRGN)wParam : NULL; if(pT->DrawThemeClientEdge(hRgn)) bHandled = TRUE; } } return lRet; } // Drawing helper bool DrawThemeClientEdge(HRGN hRgnUpdate) { T* pT = static_cast(this); return AtlDrawThemeClientEdge(m_hTheme, pT->m_hWnd, hRgnUpdate, NULL, 0, 0); } }; /////////////////////////////////////////////////////////////////////////////// // Buffered Paint and Animation #if defined(_WTL_NEW_UXTHEME) && (_WIN32_WINNT >= 0x0600) /////////////////////////////////////////////////////////////////////////////// // CBufferedPaintBase - Buffered Paint support for othe classes class CBufferedPaintBase { public: static int m_nIsBufferedPaintSupported; CBufferedPaintBase() { if(IsBufferedPaintSupported()) ATLVERIFY(SUCCEEDED(::BufferedPaintInit())); } ~CBufferedPaintBase() { if(IsBufferedPaintSupported()) ATLVERIFY(SUCCEEDED(::BufferedPaintUnInit())); } static bool IsBufferedPaintSupported() { if(m_nIsBufferedPaintSupported == -1) { CStaticDataInitCriticalSectionLock lock; if(FAILED(lock.Lock())) { ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CBufferedPaintBase::IsBufferedPaintSupported.\n")); ATLASSERT(FALSE); return false; } if(m_nIsBufferedPaintSupported == -1) m_nIsBufferedPaintSupported = RunTimeHelper::IsVista() ? 1 : 0; lock.Unlock(); } ATLASSERT(m_nIsBufferedPaintSupported != -1); return (m_nIsBufferedPaintSupported == 1); } }; __declspec(selectany) int CBufferedPaintBase::m_nIsBufferedPaintSupported = -1; /////////////////////////////////////////////////////////////////////////////// // CBufferedPaint - support for buffered paint functions class CBufferedPaint { public: HPAINTBUFFER m_hPaintBuffer; CBufferedPaint() : m_hPaintBuffer(NULL) { } ~CBufferedPaint() { ATLVERIFY(SUCCEEDED(End())); } bool IsNull() const { return (m_hPaintBuffer == NULL); } HPAINTBUFFER Begin(HDC hdcTarget, const RECT* prcTarget, BP_BUFFERFORMAT dwFormat, BP_PAINTPARAMS* pPaintParams, HDC* phdcPaint) { ATLASSERT(m_hPaintBuffer == NULL); m_hPaintBuffer = ::BeginBufferedPaint(hdcTarget, prcTarget, dwFormat, pPaintParams, phdcPaint); return m_hPaintBuffer; } HRESULT End(BOOL bUpdate = TRUE) { HRESULT hRet = S_FALSE; if(m_hPaintBuffer != NULL) { hRet = ::EndBufferedPaint(m_hPaintBuffer, bUpdate); m_hPaintBuffer = NULL; } return hRet; } HRESULT GetTargetRect(LPRECT pRect) const { ATLASSERT(m_hPaintBuffer != NULL); return ::GetBufferedPaintTargetRect(m_hPaintBuffer, pRect); } HDC GetTargetDC() const { ATLASSERT(m_hPaintBuffer != NULL); return ::GetBufferedPaintTargetDC(m_hPaintBuffer); } HDC GetPaintDC() const { ATLASSERT(m_hPaintBuffer != NULL); return ::GetBufferedPaintDC(m_hPaintBuffer); } HRESULT GetBits(RGBQUAD** ppbBuffer, int* pcxRow) const { ATLASSERT(m_hPaintBuffer != NULL); return ::GetBufferedPaintBits(m_hPaintBuffer, ppbBuffer, pcxRow); } HRESULT Clear(const RECT* pRect = NULL) { ATLASSERT(m_hPaintBuffer != NULL); return ::BufferedPaintClear(m_hPaintBuffer, pRect); } HRESULT SetAlpha(BYTE alpha, const RECT* pRect = NULL) { ATLASSERT(m_hPaintBuffer != NULL); return ::BufferedPaintSetAlpha(m_hPaintBuffer, pRect, alpha); } HRESULT MakeOpaque(const RECT* pRect = NULL) { ATLASSERT(m_hPaintBuffer != NULL); return ::BufferedPaintSetAlpha(m_hPaintBuffer, pRect, 255); } }; /////////////////////////////////////////////////////////////////////////////// // CBufferedPaintImpl - provides buffered paint for any window template class ATL_NO_VTABLE CBufferedPaintImpl : public CBufferedPaintBase { public: CBufferedPaint m_BufferedPaint; BP_BUFFERFORMAT m_dwFormat; BP_PAINTPARAMS m_PaintParams; CBufferedPaintImpl() : m_dwFormat(BPBF_TOPDOWNDIB) { memset(&m_PaintParams, 0, sizeof(BP_PAINTPARAMS)); m_PaintParams.cbSize = sizeof(BP_PAINTPARAMS); } // Message map and handlers BEGIN_MSG_MAP(CBufferedPaintImpl) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) MESSAGE_HANDLER(WM_PAINT, OnPaint) MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint) END_MSG_MAP() LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return 1; // no background needed } LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); if(wParam != NULL) { RECT rect = { 0 }; pT->GetClientRect(&rect); pT->DoPaint((HDC)wParam, rect); } else { CPaintDC dc(pT->m_hWnd); pT->DoBufferedPaint(dc.m_hDC, dc.m_ps.rcPaint); } return 0; } // Overrideables void DoBufferedPaint(CDCHandle dc, RECT& rect) { HDC hDCPaint = NULL; if(IsBufferedPaintSupported()) m_BufferedPaint.Begin(dc, &rect, m_dwFormat, &m_PaintParams, &hDCPaint); T* pT = static_cast(this); if(hDCPaint != NULL) pT->DoPaint(hDCPaint, rect); else pT->DoPaint(dc.m_hDC, rect); if(IsBufferedPaintSupported()) m_BufferedPaint.End(); } void DoPaint(CDCHandle /*dc*/, RECT& /*rect*/) { // must be implemented in a derived class ATLASSERT(FALSE); } }; /////////////////////////////////////////////////////////////////////////////// // CBufferedPaintWindowImpl - implements a window that uses buffered paint template class ATL_NO_VTABLE CBufferedPaintWindowImpl : public ATL::CWindowImpl, public CBufferedPaintImpl< T > { public: BEGIN_MSG_MAP(CBufferedPaintWindowImpl) CHAIN_MSG_MAP(CBufferedPaintImpl< T >) END_MSG_MAP() }; /////////////////////////////////////////////////////////////////////////////// // CBufferedAnimation - support for buffered animation class CBufferedAnimation { public: HANIMATIONBUFFER m_hAnimationBuffer; CBufferedAnimation() : m_hAnimationBuffer(NULL) { } ~CBufferedAnimation() { ATLVERIFY(SUCCEEDED(End())); } bool IsNull() const { return (m_hAnimationBuffer == NULL); } HANIMATIONBUFFER Begin(HWND hWnd, HDC hDCTarget, const RECT* pRectTarget, BP_BUFFERFORMAT dwFormat, BP_PAINTPARAMS* pPaintParams, BP_ANIMATIONPARAMS* pAnimationParams, HDC* phdcFrom, HDC* phdcTo) { ATLASSERT(m_hAnimationBuffer == NULL); m_hAnimationBuffer = ::BeginBufferedAnimation(hWnd, hDCTarget, pRectTarget, dwFormat, pPaintParams, pAnimationParams, phdcFrom, phdcTo); return m_hAnimationBuffer; } HRESULT End(BOOL bUpdate = TRUE) { HRESULT hRet = S_FALSE; if(m_hAnimationBuffer != NULL) { hRet = ::EndBufferedAnimation(m_hAnimationBuffer, bUpdate); m_hAnimationBuffer = NULL; } return hRet; } static bool IsRendering(HWND hWnd, HDC hDC) { return (::BufferedPaintRenderAnimation(hWnd, hDC) != FALSE); } static HRESULT StopAllAnimations(HWND hWnd) { return ::BufferedPaintStopAllAnimations(hWnd); } }; /////////////////////////////////////////////////////////////////////////////// // CBufferedAnimationImpl - provides buffered animation support for any window // Note: You can either use m_State and m_NewState to store the state information // for the animation change, or map your state to those data members. DoPaint() // should only rely on the state information that is passed to it. template class ATL_NO_VTABLE CBufferedAnimationImpl : public CBufferedPaintBase { public: BP_BUFFERFORMAT m_dwFormat; BP_PAINTPARAMS m_PaintParams; BP_ANIMATIONPARAMS m_AnimationParams; TState m_State; TState m_NewState; CBufferedAnimationImpl(TState InitialState) : m_dwFormat(BPBF_TOPDOWNDIB) { memset(&m_PaintParams, 0, sizeof(BP_PAINTPARAMS)); m_PaintParams.cbSize = sizeof(BP_PAINTPARAMS); memset(&m_AnimationParams, 0, sizeof(BP_ANIMATIONPARAMS)); m_AnimationParams.cbSize = sizeof(BP_ANIMATIONPARAMS); m_AnimationParams.style = BPAS_LINEAR; m_AnimationParams.dwDuration = 500; T* pT = static_cast(this); pT->SetState(InitialState); pT->SetNewState(InitialState); } DWORD GetDuration() const { return m_AnimationParams.dwDuration; } void SetDuration(DWORD dwDuration) { m_AnimationParams.dwDuration = dwDuration; } void DoAnimation(TState NewState, const RECT* pRect = NULL) { T* pT = static_cast(this); pT->SetNewState(NewState); pT->InvalidateRect(pRect, FALSE); pT->UpdateWindow(); pT->SetState(NewState); } // Message map and handlers BEGIN_MSG_MAP(CBufferedAnimationImpl) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) MESSAGE_HANDLER(WM_PAINT, OnPaint) MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint) END_MSG_MAP() LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return 1; // no background needed } LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); if(wParam != NULL) { RECT rect = { 0 }; pT->GetClientRect(&rect); pT->DoPaint((HDC)wParam, rect, m_NewState); } else { CPaintDC dc(pT->m_hWnd); pT->DoAnimationPaint(dc.m_hDC, dc.m_ps.rcPaint); } return 0; } // Overrideables void SetState(TState State) { m_State = State; } void SetNewState(TState State) { m_NewState = State; } bool AreStatesEqual() const { return (m_State == m_NewState); } void DoAnimationPaint(CDCHandle dc, RECT& rect) { T* pT = static_cast(this); if(IsBufferedPaintSupported() && CBufferedAnimation::IsRendering(pT->m_hWnd, dc)) return; DWORD dwDurationSave = m_AnimationParams.dwDuration; if(pT->AreStatesEqual()) m_AnimationParams.dwDuration = 0; HDC hdcFrom = NULL, hdcTo = NULL; CBufferedAnimation ba; if(IsBufferedPaintSupported()) ba.Begin(pT->m_hWnd, dc, &rect, m_dwFormat, &m_PaintParams, &m_AnimationParams, &hdcFrom, &hdcTo); if(!ba.IsNull()) { if(hdcFrom != NULL) pT->DoPaint(hdcFrom, rect, m_State); if (hdcTo != NULL) pT->DoPaint(hdcTo, rect, m_NewState); } else { pT->DoPaint(dc.m_hDC, rect, m_NewState); } m_AnimationParams.dwDuration = dwDurationSave; } void DoPaint(CDCHandle /*dc*/, RECT& /*rect*/, TState /*State*/) { // must be implemented in a derived class ATLASSERT(FALSE); } }; /////////////////////////////////////////////////////////////////////////////// // CBufferedAnimationWindowImpl - implements a window that uses buffered animation template class ATL_NO_VTABLE CBufferedAnimationWindowImpl : public ATL::CWindowImpl, public CBufferedAnimationImpl< T, TState > { public: CBufferedAnimationWindowImpl(TState InitialState) : CBufferedAnimationImpl< T, TState >(InitialState) { } typedef CBufferedAnimationImpl< T, TState > _baseBufferedAnimation; BEGIN_MSG_MAP(CBufferedAnimationWindowImpl) CHAIN_MSG_MAP(_baseBufferedAnimation) END_MSG_MAP() }; #endif // defined(_WTL_NEW_UXTHEME) && (_WIN32_WINNT >= 0x0600) }; // namespace WTL #endif // __ATLTHEME_H__ ================================================ FILE: src/Setup/wtl90/atluser.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLUSER_H__ #define __ATLUSER_H__ #pragma once #ifndef __ATLAPP_H__ #error atluser.h requires atlapp.h to be included first #endif /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CMenuItemInfo // CMenuT // CAcceleratorT // CIconT // CCursorT // CResource // // Global functions: // AtlMessageBox() // // AtlLoadAccelerators() // AtlLoadMenu() // AtlLoadBitmap() // AtlLoadSysBitmap() // AtlLoadCursor() // AtlLoadSysCursor() // AtlLoadIcon() // AtlLoadSysIcon() // AtlLoadBitmapImage() // AtlLoadCursorImage() // AtlLoadIconImage() // AtlLoadSysBitmapImage() // AtlLoadSysCursorImage() // AtlLoadSysIconImage() // AtlLoadString() namespace WTL { /////////////////////////////////////////////////////////////////////////////// // AtlMessageBox - accepts both memory and resource based strings inline int AtlMessageBox(HWND hWndOwner, ATL::_U_STRINGorID message, ATL::_U_STRINGorID title = (LPCTSTR)NULL, UINT uType = MB_OK | MB_ICONINFORMATION) { ATLASSERT(hWndOwner == NULL || ::IsWindow(hWndOwner)); LPTSTR lpstrMessage = NULL; if(IS_INTRESOURCE(message.m_lpstr)) { for(int nLen = 256; ; nLen *= 2) { ATLTRY(lpstrMessage = new TCHAR[nLen]); if(lpstrMessage == NULL) { ATLASSERT(FALSE); return 0; } int nRes = ::LoadString(ModuleHelper::GetResourceInstance(), LOWORD(message.m_lpstr), lpstrMessage, nLen); if(nRes < nLen - 1) break; delete [] lpstrMessage; lpstrMessage = NULL; } message.m_lpstr = lpstrMessage; } LPTSTR lpstrTitle = NULL; if(IS_INTRESOURCE(title.m_lpstr) && LOWORD(title.m_lpstr) != 0) { for(int nLen = 256; ; nLen *= 2) { ATLTRY(lpstrTitle = new TCHAR[nLen]); if(lpstrTitle == NULL) { ATLASSERT(FALSE); return 0; } int nRes = ::LoadString(ModuleHelper::GetResourceInstance(), LOWORD(title.m_lpstr), lpstrTitle, nLen); if(nRes < nLen - 1) break; delete [] lpstrTitle; lpstrTitle = NULL; } title.m_lpstr = lpstrTitle; } int nRet = ::MessageBox(hWndOwner, message.m_lpstr, title.m_lpstr, uType); delete [] lpstrMessage; delete [] lpstrTitle; return nRet; } /////////////////////////////////////////////////////////////////////////////// // CMenu #if (WINVER >= 0x0500) #ifndef MII_SIZEOF_STRUCT #define MII_SIZEOF_STRUCT(structname, member) (((int)((LPBYTE)(&((structname*)0)->member) - ((LPBYTE)((structname*)0)))) + sizeof(((structname*)0)->member)) #endif #define MENUITEMINFO_SIZE_VERSION_400A MII_SIZEOF_STRUCT(MENUITEMINFOA, cch) #define MENUITEMINFO_SIZE_VERSION_400W MII_SIZEOF_STRUCT(MENUITEMINFOW, cch) #ifdef UNICODE #define MENUITEMINFO_SIZE_VERSION_400 MENUITEMINFO_SIZE_VERSION_400W #else #define MENUITEMINFO_SIZE_VERSION_400 MENUITEMINFO_SIZE_VERSION_400A #endif // !UNICODE #endif // (WINVER >= 0x0500) class CMenuItemInfo : public MENUITEMINFO { public: CMenuItemInfo() { memset(this, 0, sizeof(MENUITEMINFO)); cbSize = sizeof(MENUITEMINFO); #if (WINVER >= 0x0500) // adjust struct size if running on older version of Windows if(AtlIsOldWindows()) { ATLASSERT(cbSize > MENUITEMINFO_SIZE_VERSION_400); // must be cbSize = MENUITEMINFO_SIZE_VERSION_400; } #endif // (WINVER >= 0x0500) } }; // forward declarations template class CMenuT; typedef CMenuT CMenuHandle; typedef CMenuT CMenu; template class CMenuT { public: // Data members HMENU m_hMenu; // Constructor/destructor/operators CMenuT(HMENU hMenu = NULL) : m_hMenu(hMenu) { } ~CMenuT() { if(t_bManaged && m_hMenu != NULL) DestroyMenu(); } CMenuT& operator =(HMENU hMenu) { Attach(hMenu); return *this; } void Attach(HMENU hMenuNew) { ATLASSERT(::IsMenu(hMenuNew)); if(t_bManaged && m_hMenu != NULL && m_hMenu != hMenuNew) ::DestroyMenu(m_hMenu); m_hMenu = hMenuNew; } HMENU Detach() { HMENU hMenu = m_hMenu; m_hMenu = NULL; return hMenu; } operator HMENU() const { return m_hMenu; } bool IsNull() const { return (m_hMenu == NULL); } BOOL IsMenu() const { return ::IsMenu(m_hMenu); } // Create/destroy methods BOOL CreateMenu() { ATLASSERT(m_hMenu == NULL); m_hMenu = ::CreateMenu(); return (m_hMenu != NULL) ? TRUE : FALSE; } BOOL CreatePopupMenu() { ATLASSERT(m_hMenu == NULL); m_hMenu = ::CreatePopupMenu(); return (m_hMenu != NULL) ? TRUE : FALSE; } BOOL LoadMenu(ATL::_U_STRINGorID menu) { ATLASSERT(m_hMenu == NULL); m_hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), menu.m_lpstr); return (m_hMenu != NULL) ? TRUE : FALSE; } #ifndef _WIN32_WCE BOOL LoadMenuIndirect(const void* lpMenuTemplate) { ATLASSERT(m_hMenu == NULL); m_hMenu = ::LoadMenuIndirect(lpMenuTemplate); return (m_hMenu != NULL) ? TRUE : FALSE; } #endif // !_WIN32_WCE BOOL DestroyMenu() { if (m_hMenu == NULL) return FALSE; BOOL bRet = ::DestroyMenu(m_hMenu); if(bRet) m_hMenu = NULL; return bRet; } // Menu Operations BOOL DeleteMenu(UINT nPosition, UINT nFlags) { ATLASSERT(::IsMenu(m_hMenu)); return ::DeleteMenu(m_hMenu, nPosition, nFlags); } BOOL TrackPopupMenu(UINT nFlags, int x, int y, HWND hWnd, LPCRECT lpRect = NULL) { ATLASSERT(::IsMenu(m_hMenu)); #ifndef _WIN32_WCE #if (WINVER >= 0x0500) x = _FixTrackMenuPopupX(x, y); #endif // !(WINVER >= 0x0500) return ::TrackPopupMenu(m_hMenu, nFlags, x, y, 0, hWnd, lpRect); #else // CE specific lpRect; return ::TrackPopupMenuEx(m_hMenu, nFlags, x, y, hWnd, NULL); #endif // _WIN32_WCE } BOOL TrackPopupMenuEx(UINT uFlags, int x, int y, HWND hWnd, LPTPMPARAMS lptpm = NULL) { ATLASSERT(::IsMenu(m_hMenu)); #if (WINVER >= 0x0500) && !defined(_WIN32_WCE) x = _FixTrackMenuPopupX(x, y); #endif // (WINVER >= 0x0500) && !defined(_WIN32_WCE) return ::TrackPopupMenuEx(m_hMenu, uFlags, x, y, hWnd, lptpm); } #if (WINVER >= 0x0500) && !defined(_WIN32_WCE) // helper that fixes popup menu X position when it's off-screen static int _FixTrackMenuPopupX(int x, int y) { POINT pt = { x, y }; HMONITOR hMonitor = ::MonitorFromPoint(pt, MONITOR_DEFAULTTONULL); if(hMonitor == NULL) { HMONITOR hMonitorNear = ::MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); if(hMonitorNear != NULL) { MONITORINFO mi = { sizeof(MONITORINFO) }; if(::GetMonitorInfo(hMonitorNear, &mi) != FALSE) { if(x < mi.rcWork.left) x = mi.rcWork.left; else if(x > mi.rcWork.right) x = mi.rcWork.right; } } } return x; } BOOL GetMenuInfo(LPMENUINFO lpMenuInfo) const { ATLASSERT(::IsMenu(m_hMenu)); return ::GetMenuInfo(m_hMenu, lpMenuInfo); } BOOL SetMenuInfo(LPCMENUINFO lpMenuInfo) { ATLASSERT(::IsMenu(m_hMenu)); return ::SetMenuInfo(m_hMenu, lpMenuInfo); } #endif // (WINVER >= 0x0500) && !defined(_WIN32_WCE) // Menu Item Operations BOOL AppendMenu(UINT nFlags, UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL) { ATLASSERT(::IsMenu(m_hMenu)); return ::AppendMenu(m_hMenu, nFlags, nIDNewItem, lpszNewItem); } BOOL AppendMenu(UINT nFlags, HMENU hSubMenu, LPCTSTR lpszNewItem) { ATLASSERT(::IsMenu(m_hMenu)); ATLASSERT(::IsMenu(hSubMenu)); return ::AppendMenu(m_hMenu, nFlags | MF_POPUP, (UINT_PTR)hSubMenu, lpszNewItem); } #ifndef _WIN32_WCE BOOL AppendMenu(UINT nFlags, UINT_PTR nIDNewItem, HBITMAP hBmp) { ATLASSERT(::IsMenu(m_hMenu)); return ::AppendMenu(m_hMenu, nFlags | MF_BITMAP, nIDNewItem, (LPCTSTR)hBmp); } BOOL AppendMenu(UINT nFlags, HMENU hSubMenu, HBITMAP hBmp) { ATLASSERT(::IsMenu(m_hMenu)); ATLASSERT(::IsMenu(hSubMenu)); return ::AppendMenu(m_hMenu, nFlags | (MF_BITMAP | MF_POPUP), (UINT_PTR)hSubMenu, (LPCTSTR)hBmp); } #endif // !_WIN32_WCE UINT CheckMenuItem(UINT nIDCheckItem, UINT nCheck) { ATLASSERT(::IsMenu(m_hMenu)); return (UINT)::CheckMenuItem(m_hMenu, nIDCheckItem, nCheck); } UINT EnableMenuItem(UINT nIDEnableItem, UINT nEnable) { ATLASSERT(::IsMenu(m_hMenu)); return ::EnableMenuItem(m_hMenu, nIDEnableItem, nEnable); } #ifndef _WIN32_WCE BOOL HiliteMenuItem(HWND hWnd, UINT uIDHiliteItem, UINT uHilite) { ATLASSERT(::IsMenu(m_hMenu)); return ::HiliteMenuItem(hWnd, m_hMenu, uIDHiliteItem, uHilite); } int GetMenuItemCount() const { ATLASSERT(::IsMenu(m_hMenu)); return ::GetMenuItemCount(m_hMenu); } UINT GetMenuItemID(int nPos) const { ATLASSERT(::IsMenu(m_hMenu)); return ::GetMenuItemID(m_hMenu, nPos); } UINT GetMenuState(UINT nID, UINT nFlags) const { ATLASSERT(::IsMenu(m_hMenu)); return ::GetMenuState(m_hMenu, nID, nFlags); } int GetMenuString(UINT nIDItem, LPTSTR lpString, int nMaxCount, UINT nFlags) const { ATLASSERT(::IsMenu(m_hMenu)); return ::GetMenuString(m_hMenu, nIDItem, lpString, nMaxCount, nFlags); } int GetMenuStringLen(UINT nIDItem, UINT nFlags) const { ATLASSERT(::IsMenu(m_hMenu)); return ::GetMenuString(m_hMenu, nIDItem, NULL, 0, nFlags); } #ifndef _ATL_NO_COM BOOL GetMenuString(UINT nIDItem, BSTR& bstrText, UINT nFlags) const { USES_CONVERSION; ATLASSERT(::IsMenu(m_hMenu)); ATLASSERT(bstrText == NULL); int nLen = GetMenuStringLen(nIDItem, nFlags); if(nLen == 0) { bstrText = ::SysAllocString(OLESTR("")); return (bstrText != NULL) ? TRUE : FALSE; } nLen++; // increment to include terminating NULL char CTempBuffer buff; LPTSTR lpszText = buff.Allocate(nLen); if(lpszText == NULL) return FALSE; if(!GetMenuString(nIDItem, lpszText, nLen, nFlags)) return FALSE; bstrText = ::SysAllocString(T2OLE(lpszText)); return (bstrText != NULL) ? TRUE : FALSE; } #endif // !_ATL_NO_COM #elif (_ATL_VER >= 0x0800) int GetMenuItemCount() const { ATLASSERT(::IsMenu(m_hMenu)); return ATL::GetMenuItemCount(m_hMenu); } UINT GetMenuItemID(int nPos) const { ATLASSERT(::IsMenu(m_hMenu)); return ATL::GetMenuItemID(m_hMenu, nPos); } UINT GetMenuState(UINT nID, UINT nFlags) const { ATLASSERT(::IsMenu(m_hMenu)); return ATL::GetMenuState(m_hMenu, nID, nFlags); } int GetMenuString(UINT nIDItem, LPTSTR lpString, int nMaxCount, UINT nFlags) const { ATLASSERT(::IsMenu(m_hMenu)); return ATL::GetMenuString(m_hMenu, nIDItem, lpString, nMaxCount, nFlags); } int GetMenuStringLen(UINT nIDItem, UINT nFlags) const { ATLASSERT(::IsMenu(m_hMenu)); return ATL::GetMenuString(m_hMenu, nIDItem, NULL, 0, nFlags); } #endif // (_ATL_VER >= 0x0800) #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) int GetMenuString(UINT nIDItem, _CSTRING_NS::CString& strText, UINT nFlags) const { ATLASSERT(::IsMenu(m_hMenu)); int nLen = GetMenuStringLen(nIDItem, nFlags); if(nLen == 0) return 0; nLen++; // increment to include terminating NULL char LPTSTR lpstr = strText.GetBufferSetLength(nLen); if(lpstr == NULL) return 0; int nRet = GetMenuString(nIDItem, lpstr, nLen, nFlags); strText.ReleaseBuffer(); return nRet; } #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) CMenuHandle GetSubMenu(int nPos) const { ATLASSERT(::IsMenu(m_hMenu)); return CMenuHandle(::GetSubMenu(m_hMenu, nPos)); } BOOL InsertMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL) { ATLASSERT(::IsMenu(m_hMenu)); return ::InsertMenu(m_hMenu, nPosition, nFlags, nIDNewItem, lpszNewItem); } BOOL InsertMenu(UINT nPosition, UINT nFlags, HMENU hSubMenu, LPCTSTR lpszNewItem) { ATLASSERT(::IsMenu(m_hMenu)); ATLASSERT(::IsMenu(hSubMenu)); return ::InsertMenu(m_hMenu, nPosition, nFlags | MF_POPUP, (UINT_PTR)hSubMenu, lpszNewItem); } #ifndef _WIN32_WCE BOOL InsertMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem, HBITMAP hBmp) { ATLASSERT(::IsMenu(m_hMenu)); return ::InsertMenu(m_hMenu, nPosition, nFlags | MF_BITMAP, nIDNewItem, (LPCTSTR)hBmp); } BOOL InsertMenu(UINT nPosition, UINT nFlags, HMENU hSubMenu, HBITMAP hBmp) { ATLASSERT(::IsMenu(m_hMenu)); ATLASSERT(::IsMenu(hSubMenu)); return ::InsertMenu(m_hMenu, nPosition, nFlags | (MF_BITMAP | MF_POPUP), (UINT_PTR)hSubMenu, (LPCTSTR)hBmp); } BOOL ModifyMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL) { ATLASSERT(::IsMenu(m_hMenu)); return ::ModifyMenu(m_hMenu, nPosition, nFlags, nIDNewItem, lpszNewItem); } BOOL ModifyMenu(UINT nPosition, UINT nFlags, HMENU hSubMenu, LPCTSTR lpszNewItem) { ATLASSERT(::IsMenu(m_hMenu)); ATLASSERT(::IsMenu(hSubMenu)); return ::ModifyMenu(m_hMenu, nPosition, nFlags | MF_POPUP, (UINT_PTR)hSubMenu, lpszNewItem); } BOOL ModifyMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem, HBITMAP hBmp) { ATLASSERT(::IsMenu(m_hMenu)); return ::ModifyMenu(m_hMenu, nPosition, nFlags | MF_BITMAP, nIDNewItem, (LPCTSTR)hBmp); } BOOL ModifyMenu(UINT nPosition, UINT nFlags, HMENU hSubMenu, HBITMAP hBmp) { ATLASSERT(::IsMenu(m_hMenu)); ATLASSERT(::IsMenu(hSubMenu)); return ::ModifyMenu(m_hMenu, nPosition, nFlags | (MF_BITMAP | MF_POPUP), (UINT_PTR)hSubMenu, (LPCTSTR)hBmp); } #endif // !_WIN32_WCE BOOL RemoveMenu(UINT nPosition, UINT nFlags) { ATLASSERT(::IsMenu(m_hMenu)); return ::RemoveMenu(m_hMenu, nPosition, nFlags); } #ifndef _WIN32_WCE BOOL SetMenuItemBitmaps(UINT nPosition, UINT nFlags, HBITMAP hBmpUnchecked, HBITMAP hBmpChecked) { ATLASSERT(::IsMenu(m_hMenu)); return ::SetMenuItemBitmaps(m_hMenu, nPosition, nFlags, hBmpUnchecked, hBmpChecked); } #endif // !_WIN32_WCE BOOL CheckMenuRadioItem(UINT nIDFirst, UINT nIDLast, UINT nIDItem, UINT nFlags) { ATLASSERT(::IsMenu(m_hMenu)); return ::CheckMenuRadioItem(m_hMenu, nIDFirst, nIDLast, nIDItem, nFlags); } BOOL GetMenuItemInfo(UINT uItem, BOOL bByPosition, LPMENUITEMINFO lpmii) const { ATLASSERT(::IsMenu(m_hMenu)); return (BOOL)::GetMenuItemInfo(m_hMenu, uItem, bByPosition, lpmii); } BOOL SetMenuItemInfo(UINT uItem, BOOL bByPosition, LPMENUITEMINFO lpmii) { ATLASSERT(::IsMenu(m_hMenu)); return (BOOL)::SetMenuItemInfo(m_hMenu, uItem, bByPosition, lpmii); } #ifndef _WIN32_WCE BOOL InsertMenuItem(UINT uItem, BOOL bByPosition, LPMENUITEMINFO lpmii) { ATLASSERT(::IsMenu(m_hMenu)); return (BOOL)::InsertMenuItem(m_hMenu, uItem, bByPosition, lpmii); } UINT GetMenuDefaultItem(BOOL bByPosition = FALSE, UINT uFlags = 0U) const { ATLASSERT(::IsMenu(m_hMenu)); return ::GetMenuDefaultItem(m_hMenu, (UINT)bByPosition, uFlags); } BOOL SetMenuDefaultItem(UINT uItem = (UINT)-1, BOOL bByPosition = FALSE) { ATLASSERT(::IsMenu(m_hMenu)); return ::SetMenuDefaultItem(m_hMenu, uItem, (UINT)bByPosition); } BOOL GetMenuItemRect(HWND hWnd, UINT uItem, LPRECT lprcItem) const { ATLASSERT(::IsMenu(m_hMenu)); return ::GetMenuItemRect(hWnd, m_hMenu, uItem, lprcItem); } int MenuItemFromPoint(HWND hWnd, POINT point) const { ATLASSERT(::IsMenu(m_hMenu)); return ::MenuItemFromPoint(hWnd, m_hMenu, point); } // Context Help Functions BOOL SetMenuContextHelpId(DWORD dwContextHelpId) { ATLASSERT(::IsMenu(m_hMenu)); return ::SetMenuContextHelpId(m_hMenu, dwContextHelpId); } DWORD GetMenuContextHelpId() const { ATLASSERT(::IsMenu(m_hMenu)); return ::GetMenuContextHelpId(m_hMenu); } #endif // !_WIN32_WCE }; /////////////////////////////////////////////////////////////////////////////// // CAccelerator template class CAcceleratorT { public: HACCEL m_hAccel; // Constructor/destructor/operators CAcceleratorT(HACCEL hAccel = NULL) : m_hAccel(hAccel) { } ~CAcceleratorT() { if(t_bManaged && m_hAccel != NULL) ::DestroyAcceleratorTable(m_hAccel); } CAcceleratorT& operator =(HACCEL hAccel) { Attach(hAccel); return *this; } void Attach(HACCEL hAccel) { if(t_bManaged && m_hAccel != NULL) ::DestroyAcceleratorTable(m_hAccel); m_hAccel = hAccel; } HACCEL Detach() { HACCEL hAccel = m_hAccel; m_hAccel = NULL; return hAccel; } operator HACCEL() const { return m_hAccel; } bool IsNull() const { return m_hAccel == NULL; } // Create/destroy methods HACCEL LoadAccelerators(ATL::_U_STRINGorID accel) { ATLASSERT(m_hAccel == NULL); m_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), accel.m_lpstr); return m_hAccel; } HACCEL CreateAcceleratorTable(LPACCEL pAccel, int cEntries) { ATLASSERT(m_hAccel == NULL); ATLASSERT(pAccel != NULL); m_hAccel = ::CreateAcceleratorTable(pAccel, cEntries); return m_hAccel; } void DestroyObject() { if(m_hAccel != NULL) { ::DestroyAcceleratorTable(m_hAccel); m_hAccel = NULL; } } // Operations #ifndef _WIN32_WCE int CopyAcceleratorTable(LPACCEL lpAccelDst, int cEntries) { ATLASSERT(m_hAccel != NULL); ATLASSERT(lpAccelDst != NULL); return ::CopyAcceleratorTable(m_hAccel, lpAccelDst, cEntries); } int GetEntriesCount() const { ATLASSERT(m_hAccel != NULL); return ::CopyAcceleratorTable(m_hAccel, NULL, 0); } #endif // !_WIN32_WCE BOOL TranslateAccelerator(HWND hWnd, LPMSG pMsg) { ATLASSERT(m_hAccel != NULL); ATLASSERT(::IsWindow(hWnd)); ATLASSERT(pMsg != NULL); return ::TranslateAccelerator(hWnd, m_hAccel, pMsg); } }; typedef CAcceleratorT CAcceleratorHandle; typedef CAcceleratorT CAccelerator; /////////////////////////////////////////////////////////////////////////////// // CIcon template class CIconT { public: HICON m_hIcon; // Constructor/destructor/operators CIconT(HICON hIcon = NULL) : m_hIcon(hIcon) { } ~CIconT() { if(t_bManaged && m_hIcon != NULL) ::DestroyIcon(m_hIcon); } CIconT& operator =(HICON hIcon) { Attach(hIcon); return *this; } void Attach(HICON hIcon) { if(t_bManaged && m_hIcon != NULL) ::DestroyIcon(m_hIcon); m_hIcon = hIcon; } HICON Detach() { HICON hIcon = m_hIcon; m_hIcon = NULL; return hIcon; } operator HICON() const { return m_hIcon; } bool IsNull() const { return m_hIcon == NULL; } // Create/destroy methods HICON LoadIcon(ATL::_U_STRINGorID icon) { ATLASSERT(m_hIcon == NULL); m_hIcon = ::LoadIcon(ModuleHelper::GetResourceInstance(), icon.m_lpstr); return m_hIcon; } HICON LoadIcon(ATL::_U_STRINGorID icon, int cxDesired, int cyDesired, UINT fuLoad = 0) { ATLASSERT(m_hIcon == NULL); m_hIcon = (HICON) ::LoadImage(ModuleHelper::GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad); return m_hIcon; } #ifndef _WIN32_WCE HICON LoadOEMIcon(LPCTSTR lpstrIconName) { ATLASSERT(m_hIcon == NULL); ATLASSERT(IsOEMIcon(lpstrIconName)); m_hIcon = ::LoadIcon(NULL, lpstrIconName); return m_hIcon; } HICON CreateIcon(int nWidth, int nHeight, BYTE cPlanes, BYTE cBitsPixel, CONST BYTE* lpbANDbits, CONST BYTE *lpbXORbits) { ATLASSERT(m_hIcon == NULL); ATLASSERT(lpbANDbits != NULL); ATLASSERT(lpbXORbits != NULL); m_hIcon = ::CreateIcon(ModuleHelper::GetResourceInstance(), nWidth, nHeight, cPlanes, cBitsPixel, lpbANDbits, lpbXORbits); return m_hIcon; } HICON CreateIconFromResource(PBYTE pBits, DWORD dwResSize, DWORD dwVersion = 0x00030000) { ATLASSERT(m_hIcon == NULL); ATLASSERT(pBits != NULL); m_hIcon = ::CreateIconFromResource(pBits, dwResSize, TRUE, dwVersion); return m_hIcon; } HICON CreateIconFromResourceEx(PBYTE pbBits, DWORD cbBits, DWORD dwVersion = 0x00030000, int cxDesired = 0, int cyDesired = 0, UINT uFlags = LR_DEFAULTCOLOR) { ATLASSERT(m_hIcon == NULL); ATLASSERT(pbBits != NULL); ATLASSERT(cbBits > 0); m_hIcon = ::CreateIconFromResourceEx(pbBits, cbBits, TRUE, dwVersion, cxDesired, cyDesired, uFlags); return m_hIcon; } #endif // !_WIN32_WCE HICON CreateIconIndirect(PICONINFO pIconInfo) { ATLASSERT(m_hIcon == NULL); ATLASSERT(pIconInfo != NULL); m_hIcon = ::CreateIconIndirect(pIconInfo); return m_hIcon; } #ifndef _WIN32_WCE HICON ExtractIcon(LPCTSTR lpszExeFileName, UINT nIconIndex) { ATLASSERT(m_hIcon == NULL); ATLASSERT(lpszExeFileName != NULL); m_hIcon = ::ExtractIcon(ModuleHelper::GetModuleInstance(), lpszExeFileName, nIconIndex); return m_hIcon; } HICON ExtractAssociatedIcon(HINSTANCE hInst, LPTSTR lpIconPath, LPWORD lpiIcon) { ATLASSERT(m_hIcon == NULL); ATLASSERT(lpIconPath != NULL); ATLASSERT(lpiIcon != NULL); m_hIcon = ::ExtractAssociatedIcon(hInst, lpIconPath, lpiIcon); return m_hIcon; } #endif // !_WIN32_WCE BOOL DestroyIcon() { ATLASSERT(m_hIcon != NULL); BOOL bRet = ::DestroyIcon(m_hIcon); if(bRet != FALSE) m_hIcon = NULL; return bRet; } // Operations #ifndef _WIN32_WCE HICON CopyIcon() { ATLASSERT(m_hIcon != NULL); return ::CopyIcon(m_hIcon); } HICON DuplicateIcon() { ATLASSERT(m_hIcon != NULL); return ::DuplicateIcon(NULL, m_hIcon); } #endif // !_WIN32_WCE BOOL DrawIcon(HDC hDC, int x, int y) { ATLASSERT(m_hIcon != NULL); #ifndef _WIN32_WCE return ::DrawIcon(hDC, x, y, m_hIcon); #else // CE specific return ::DrawIconEx(hDC, x, y, m_hIcon, 0, 0, 0, NULL, DI_NORMAL); #endif // _WIN32_WCE } BOOL DrawIcon(HDC hDC, POINT pt) { ATLASSERT(m_hIcon != NULL); #ifndef _WIN32_WCE return ::DrawIcon(hDC, pt.x, pt.y, m_hIcon); #else // CE specific return ::DrawIconEx(hDC, pt.x, pt.y, m_hIcon, 0, 0, 0, NULL, DI_NORMAL); #endif // _WIN32_WCE } BOOL DrawIconEx(HDC hDC, int x, int y, int cxWidth, int cyWidth, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL) { ATLASSERT(m_hIcon != NULL); return ::DrawIconEx(hDC, x, y, m_hIcon, cxWidth, cyWidth, uStepIfAniCur, hbrFlickerFreeDraw, uFlags); } BOOL DrawIconEx(HDC hDC, POINT pt, SIZE size, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL) { ATLASSERT(m_hIcon != NULL); return ::DrawIconEx(hDC, pt.x, pt.y, m_hIcon, size.cx, size.cy, uStepIfAniCur, hbrFlickerFreeDraw, uFlags); } #ifndef _WIN32_WCE BOOL GetIconInfo(PICONINFO pIconInfo) const { ATLASSERT(m_hIcon != NULL); ATLASSERT(pIconInfo != NULL); return ::GetIconInfo(m_hIcon, pIconInfo); } #if (_WIN32_WINNT >= 0x0600) BOOL GetIconInfoEx(PICONINFOEX pIconInfo) const { ATLASSERT(m_hIcon != NULL); ATLASSERT(pIconInfo != NULL); return ::GetIconInfoEx(m_hIcon, pIconInfo); } #endif // (_WIN32_WINNT >= 0x0600) #if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) HRESULT LoadIconMetric(ATL::_U_STRINGorID icon, int lims) { ATLASSERT(m_hIcon == NULL); USES_CONVERSION; return ::LoadIconMetric(ModuleHelper::GetResourceInstance(), T2CW(icon.m_lpstr), lims, &m_hIcon); } HRESULT LoadIconWithScaleDown(ATL::_U_STRINGorID icon, int cx, int cy) { ATLASSERT(m_hIcon == NULL); USES_CONVERSION; return ::LoadIconWithScaleDown(ModuleHelper::GetResourceInstance(), T2CW(icon.m_lpstr), cx, cy, &m_hIcon); } HRESULT LoadOEMIconMetric(LPCTSTR lpstrIconName, int lims) { ATLASSERT(m_hIcon == NULL); ATLASSERT(IsOEMIcon(lpstrIconName)); return ::LoadIconMetric(NULL, (LPCWSTR)lpstrIconName, lims, &m_hIcon); } HRESULT LoadOEMIconWithScaleDown(LPCTSTR lpstrIconName, int cx, int cy) { ATLASSERT(m_hIcon == NULL); ATLASSERT(IsOEMIcon(lpstrIconName)); USES_CONVERSION; return ::LoadIconWithScaleDown(NULL, (LPCWSTR)lpstrIconName, cx, cy, &m_hIcon); } #endif // defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_LONGHORN) #endif // !_WIN32_WCE // Helper #ifndef _WIN32_WCE static bool IsOEMIcon(LPCTSTR lpstrIconName) { #if (WINVER >= 0x0600) return (lpstrIconName == IDI_APPLICATION || lpstrIconName == IDI_ASTERISK || lpstrIconName == IDI_EXCLAMATION || lpstrIconName == IDI_HAND || lpstrIconName == IDI_QUESTION || lpstrIconName == IDI_WINLOGO || lpstrIconName == IDI_SHIELD); #else // !(WINVER >= 0x0600) return (lpstrIconName == IDI_APPLICATION || lpstrIconName == IDI_ASTERISK || lpstrIconName == IDI_EXCLAMATION || lpstrIconName == IDI_HAND || lpstrIconName == IDI_QUESTION || lpstrIconName == IDI_WINLOGO); #endif // !(WINVER >= 0x0600) } #endif // !_WIN32_WCE }; typedef CIconT CIconHandle; typedef CIconT CIcon; /////////////////////////////////////////////////////////////////////////////// // CCursor // protect template member from a winuser.h macro #ifdef CopyCursor #undef CopyCursor #endif template class CCursorT { public: HCURSOR m_hCursor; // Constructor/destructor/operators CCursorT(HCURSOR hCursor = NULL) : m_hCursor(hCursor) { } ~CCursorT() { if(t_bManaged && m_hCursor != NULL) DestroyCursor(); } CCursorT& operator =(HCURSOR hCursor) { Attach(hCursor); return *this; } void Attach(HCURSOR hCursor) { if(t_bManaged && m_hCursor != NULL) DestroyCursor(); m_hCursor = hCursor; } HCURSOR Detach() { HCURSOR hCursor = m_hCursor; m_hCursor = NULL; return hCursor; } operator HCURSOR() const { return m_hCursor; } bool IsNull() const { return m_hCursor == NULL; } // Create/destroy methods HCURSOR LoadCursor(ATL::_U_STRINGorID cursor) { ATLASSERT(m_hCursor == NULL); m_hCursor = ::LoadCursor(ModuleHelper::GetResourceInstance(), cursor.m_lpstr); return m_hCursor; } HCURSOR LoadSysCursor(LPCTSTR lpstrCursorName) { ATLASSERT(m_hCursor == NULL); #if (WINVER >= 0x0500) ATLASSERT(lpstrCursorName == IDC_ARROW || lpstrCursorName == IDC_IBEAM || lpstrCursorName == IDC_WAIT || lpstrCursorName == IDC_CROSS || lpstrCursorName == IDC_UPARROW || lpstrCursorName == IDC_SIZE || lpstrCursorName == IDC_ICON || lpstrCursorName == IDC_SIZENWSE || lpstrCursorName == IDC_SIZENESW || lpstrCursorName == IDC_SIZEWE || lpstrCursorName == IDC_SIZENS || lpstrCursorName == IDC_SIZEALL || lpstrCursorName == IDC_NO || lpstrCursorName == IDC_APPSTARTING || lpstrCursorName == IDC_HELP || lpstrCursorName == IDC_HAND); #else // !(WINVER >= 0x0500) ATLASSERT(lpstrCursorName == IDC_ARROW || lpstrCursorName == IDC_IBEAM || lpstrCursorName == IDC_WAIT || lpstrCursorName == IDC_CROSS || lpstrCursorName == IDC_UPARROW || lpstrCursorName == IDC_SIZE || lpstrCursorName == IDC_ICON || lpstrCursorName == IDC_SIZENWSE || lpstrCursorName == IDC_SIZENESW || lpstrCursorName == IDC_SIZEWE || lpstrCursorName == IDC_SIZENS || lpstrCursorName == IDC_SIZEALL || lpstrCursorName == IDC_NO || lpstrCursorName == IDC_APPSTARTING || lpstrCursorName == IDC_HELP); #endif // !(WINVER >= 0x0500) m_hCursor = ::LoadCursor(NULL, lpstrCursorName); return m_hCursor; } // deprecated HCURSOR LoadOEMCursor(LPCTSTR lpstrCursorName) { return LoadSysCursor(lpstrCursorName); } HCURSOR LoadCursor(ATL::_U_STRINGorID cursor, int cxDesired, int cyDesired, UINT fuLoad = 0) { ATLASSERT(m_hCursor == NULL); m_hCursor = (HCURSOR) ::LoadImage(ModuleHelper::GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad); return m_hCursor; } #ifndef _WIN32_WCE HCURSOR LoadCursorFromFile(LPCTSTR pstrFilename) { ATLASSERT(m_hCursor == NULL); ATLASSERT(pstrFilename != NULL); m_hCursor = ::LoadCursorFromFile(pstrFilename); return m_hCursor; } #endif // !_WIN32_WCE #if !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP))) HCURSOR CreateCursor(int xHotSpot, int yHotSpot, int nWidth, int nHeight, CONST VOID *pvANDPlane, CONST VOID *pvXORPlane) { ATLASSERT(m_hCursor == NULL); m_hCursor = ::CreateCursor(ModuleHelper::GetResourceInstance(), xHotSpot, yHotSpot, nWidth, nHeight, pvANDPlane, pvXORPlane); return m_hCursor; } #endif // !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP))) #ifndef _WIN32_WCE HCURSOR CreateCursorFromResource(PBYTE pBits, DWORD dwResSize, DWORD dwVersion = 0x00030000) { ATLASSERT(m_hCursor == NULL); ATLASSERT(pBits != NULL); m_hCursor = (HCURSOR)::CreateIconFromResource(pBits, dwResSize, FALSE, dwVersion); return m_hCursor; } HCURSOR CreateCursorFromResourceEx(PBYTE pbBits, DWORD cbBits, DWORD dwVersion = 0x00030000, int cxDesired = 0, int cyDesired = 0, UINT uFlags = LR_DEFAULTCOLOR) { ATLASSERT(m_hCursor == NULL); ATLASSERT(pbBits != NULL); ATLASSERT(cbBits > 0); m_hCursor = (HCURSOR)::CreateIconFromResourceEx(pbBits, cbBits, FALSE, dwVersion, cxDesired, cyDesired, uFlags); return m_hCursor; } #endif // !_WIN32_WCE BOOL DestroyCursor() { ATLASSERT(m_hCursor != NULL); #if !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP))) BOOL bRet = ::DestroyCursor(m_hCursor); if(bRet != FALSE) m_hCursor = NULL; return bRet; #else // !(!defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)))) ATLTRACE2(atlTraceUI, 0, _T("Warning: This version of Windows CE does not have ::DestroyCursor()\n")); return FALSE; #endif // !(!defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)))) } // Operations #ifndef _WIN32_WCE HCURSOR CopyCursor() { ATLASSERT(m_hCursor != NULL); return (HCURSOR)::CopyIcon((HICON)m_hCursor); } #endif // !_WIN32_WCE #if (WINVER >= 0x0500) && !defined(_WIN32_WCE) BOOL GetCursorInfo(LPCURSORINFO pCursorInfo) { ATLASSERT(m_hCursor != NULL); ATLASSERT(pCursorInfo != NULL); return ::GetCursorInfo(pCursorInfo); } #endif // (WINVER >= 0x0500) && !defined(_WIN32_WCE) }; typedef CCursorT CCursorHandle; typedef CCursorT CCursor; /////////////////////////////////////////////////////////////////////////////// // CResource - Wraps a generic Windows resource. // Use it with custom resource types other than the // standard RT_CURSOR, RT_BITMAP, etc. class CResource { public: HGLOBAL m_hGlobal; HRSRC m_hResource; // Constructor/destructor CResource() : m_hGlobal(NULL), m_hResource(NULL) { } ~CResource() { Release(); } // Load methods bool Load(ATL::_U_STRINGorID Type, ATL::_U_STRINGorID ID) { ATLASSERT(m_hResource == NULL); ATLASSERT(m_hGlobal == NULL); m_hResource = ::FindResource(ModuleHelper::GetResourceInstance(), ID.m_lpstr, Type.m_lpstr); if(m_hResource == NULL) return false; m_hGlobal = ::LoadResource(ModuleHelper::GetResourceInstance(), m_hResource); if(m_hGlobal == NULL) { m_hResource = NULL; return false; } return true; } #ifndef _WIN32_WCE bool LoadEx(ATL::_U_STRINGorID Type, ATL::_U_STRINGorID ID, WORD wLanguage) { ATLASSERT(m_hResource == NULL); ATLASSERT(m_hGlobal == NULL); m_hResource = ::FindResourceEx(ModuleHelper::GetResourceInstance(), ID.m_lpstr, Type.m_lpstr, wLanguage); if(m_hResource == NULL) return false; m_hGlobal = ::LoadResource(ModuleHelper::GetResourceInstance(), m_hResource); if(m_hGlobal == NULL) { m_hResource = NULL; return false; } return true; } #endif // !_WIN32_WCE // Misc. operations DWORD GetSize() const { ATLASSERT(m_hResource != NULL); return ::SizeofResource(ModuleHelper::GetResourceInstance(), m_hResource); } LPVOID Lock() { ATLASSERT(m_hResource != NULL); ATLASSERT(m_hGlobal != NULL); LPVOID pVoid = ::LockResource(m_hGlobal); ATLASSERT(pVoid != NULL); return pVoid; } void Release() { if(m_hGlobal != NULL) { FreeResource(m_hGlobal); m_hGlobal = NULL; m_hResource = NULL; } } }; /////////////////////////////////////////////////////////////////////////////// // Toolbar resource descriptor struct _AtlToolBarData { WORD wVersion; WORD wWidth; WORD wHeight; WORD wItemCount; WORD* items() { return (WORD*)(this+1); } }; /////////////////////////////////////////////////////////////////////////////// // Global functions for loading resources inline HACCEL AtlLoadAccelerators(ATL::_U_STRINGorID table) { return ::LoadAccelerators(ModuleHelper::GetResourceInstance(), table.m_lpstr); } inline HMENU AtlLoadMenu(ATL::_U_STRINGorID menu) { return ::LoadMenu(ModuleHelper::GetResourceInstance(), menu.m_lpstr); } inline HBITMAP AtlLoadBitmap(ATL::_U_STRINGorID bitmap) { return ::LoadBitmap(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr); } #ifdef OEMRESOURCE inline HBITMAP AtlLoadSysBitmap(ATL::_U_STRINGorID bitmap) { #ifdef _DEBUG WORD wID = (WORD)bitmap.m_lpstr; ATLASSERT(wID >= 32734 && wID <= 32767); #endif // _DEBUG return ::LoadBitmap(NULL, bitmap.m_lpstr); } #endif // OEMRESOURCE inline HCURSOR AtlLoadCursor(ATL::_U_STRINGorID cursor) { return ::LoadCursor(ModuleHelper::GetResourceInstance(), cursor.m_lpstr); } inline HCURSOR AtlLoadSysCursor(LPCTSTR lpCursorName) { #if (WINVER >= 0x0500) ATLASSERT(lpCursorName == IDC_ARROW || lpCursorName == IDC_IBEAM || lpCursorName == IDC_WAIT || lpCursorName == IDC_CROSS || lpCursorName == IDC_UPARROW || lpCursorName == IDC_SIZE || lpCursorName == IDC_ICON || lpCursorName == IDC_SIZENWSE || lpCursorName == IDC_SIZENESW || lpCursorName == IDC_SIZEWE || lpCursorName == IDC_SIZENS || lpCursorName == IDC_SIZEALL || lpCursorName == IDC_NO || lpCursorName == IDC_APPSTARTING || lpCursorName == IDC_HELP || lpCursorName == IDC_HAND); #else // !(WINVER >= 0x0500) ATLASSERT(lpCursorName == IDC_ARROW || lpCursorName == IDC_IBEAM || lpCursorName == IDC_WAIT || lpCursorName == IDC_CROSS || lpCursorName == IDC_UPARROW || lpCursorName == IDC_SIZE || lpCursorName == IDC_ICON || lpCursorName == IDC_SIZENWSE || lpCursorName == IDC_SIZENESW || lpCursorName == IDC_SIZEWE || lpCursorName == IDC_SIZENS || lpCursorName == IDC_SIZEALL || lpCursorName == IDC_NO || lpCursorName == IDC_APPSTARTING || lpCursorName == IDC_HELP); #endif // !(WINVER >= 0x0500) return ::LoadCursor(NULL, lpCursorName); } inline HICON AtlLoadIcon(ATL::_U_STRINGorID icon) { return ::LoadIcon(ModuleHelper::GetResourceInstance(), icon.m_lpstr); } #ifndef _WIN32_WCE inline HICON AtlLoadSysIcon(LPCTSTR lpIconName) { #if (WINVER >= 0x0600) ATLASSERT(lpIconName == IDI_APPLICATION || lpIconName == IDI_ASTERISK || lpIconName == IDI_EXCLAMATION || lpIconName == IDI_HAND || lpIconName == IDI_QUESTION || lpIconName == IDI_WINLOGO || lpIconName == IDI_SHIELD); #else // !(WINVER >= 0x0600) ATLASSERT(lpIconName == IDI_APPLICATION || lpIconName == IDI_ASTERISK || lpIconName == IDI_EXCLAMATION || lpIconName == IDI_HAND || lpIconName == IDI_QUESTION || lpIconName == IDI_WINLOGO); #endif // !(WINVER >= 0x0600) return ::LoadIcon(NULL, lpIconName); } #endif // !_WIN32_WCE inline HBITMAP AtlLoadBitmapImage(ATL::_U_STRINGorID bitmap, UINT fuLoad = LR_DEFAULTCOLOR) { return (HBITMAP)::LoadImage(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr, IMAGE_BITMAP, 0, 0, fuLoad); } inline HCURSOR AtlLoadCursorImage(ATL::_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0) { return (HCURSOR)::LoadImage(ModuleHelper::GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad); } inline HICON AtlLoadIconImage(ATL::_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0) { return (HICON)::LoadImage(ModuleHelper::GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad); } #ifdef OEMRESOURCE inline HBITMAP AtlLoadSysBitmapImage(WORD wBitmapID, UINT fuLoad = LR_DEFAULTCOLOR) { ATLASSERT(wBitmapID >= 32734 && wBitmapID <= 32767); ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0); // this one doesn't load from a file return (HBITMAP)::LoadImage(NULL, MAKEINTRESOURCE(wBitmapID), IMAGE_BITMAP, 0, 0, fuLoad); } #endif // OEMRESOURCE inline HCURSOR AtlLoadSysCursorImage(ATL::_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0) { #ifdef _DEBUG WORD wID = (WORD)cursor.m_lpstr; ATLASSERT((wID >= 32512 && wID <= 32516) || (wID >= 32640 && wID <= 32648) || (wID == 32650) || (wID == 32651)); ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0); // this one doesn't load from a file #endif // _DEBUG return (HCURSOR)::LoadImage(NULL, cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad); } inline HICON AtlLoadSysIconImage(ATL::_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0) { #ifdef _DEBUG WORD wID = (WORD)icon.m_lpstr; ATLASSERT(wID >= 32512 && wID <= 32517); ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0); // this one doesn't load from a file #endif // _DEBUG return (HICON)::LoadImage(NULL, icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad); } #if (_ATL_VER < 0x0700) inline int AtlLoadString(UINT uID, LPTSTR lpBuffer, int nBufferMax) { return ::LoadString(ModuleHelper::GetResourceInstance(), uID, lpBuffer, nBufferMax); } #else using ATL::AtlLoadString; #endif // (_ATL_VER < 0x0700) #ifdef _WIN32_WCE // CE only direct access to the resource inline LPCTSTR AtlLoadString(UINT uID) { LPCTSTR s = (LPCTSTR)::LoadString(ModuleHelper::GetResourceInstance(), uID, NULL, 0); #ifdef DEBUG // Check for null-termination if(s != NULL) // Note: RC -n compiles null-terminated resource strings ATLASSERT(s[*((WORD*)s -1) - 1] == L'\0'); #endif return s; } #endif // _WIN32_WCE inline bool AtlLoadString(UINT uID, BSTR& bstrText) { USES_CONVERSION; ATLASSERT(bstrText == NULL); LPTSTR lpstrText = NULL; int nRes = 0; for(int nLen = 256; ; nLen *= 2) { ATLTRY(lpstrText = new TCHAR[nLen]); if(lpstrText == NULL) break; nRes = ::LoadString(ModuleHelper::GetResourceInstance(), uID, lpstrText, nLen); if(nRes < nLen - 1) break; delete [] lpstrText; lpstrText = NULL; } if(lpstrText != NULL) { if(nRes != 0) bstrText = ::SysAllocString(T2OLE(lpstrText)); delete [] lpstrText; } return (bstrText != NULL) ? true : false; } }; // namespace WTL #endif // __ATLUSER_H__ ================================================ FILE: src/Setup/wtl90/atlwince.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLWINCE_H__ #define __ATLWINCE_H__ #pragma once #ifndef __ATLAPP_H__ #error atlwince.h requires atlapp.h to be included first #endif #ifndef __ATLWIN_H__ #error atlwince.h requires atlwin.h to be included first #endif #ifndef _WIN32_WCE #error atlwince.h compiles under Windows CE only #endif #if (_WIN32_WCE < 300) #error atlwince.h requires Windows CE 3.0 or higher. #endif #if defined(WIN32_PLATFORM_WFSP) && _MSC_VER < 1400 // EVC compiling SmartPhone code #if (WIN32_PLATFORM_WFSP < 200) #error atlwince.h requires Smartphone 2003 or higher #endif #endif // WIN32_PLATFORM_WFSP #if defined(WIN32_PLATFORM_PSPC) && _MSC_VER < 1400 // EVC compiling Pocket PC code #if (WIN32_PLATFORM_PSPC < 310) #error atlwince.h requires Pocket PC 2002 or higher #endif #endif // WIN32_PLATFORM_PSPC #if !defined(_AYGSHELL_H_) && !defined(__AYGSHELL_H__) #error atlwince.h requires aygshell.h to be included first #else #if defined(WIN32_PLATFORM_WFSP) && !defined(_TPCSHELL_H_) #error SmartPhone dialog classes require tpcshell.h to be included first #endif #endif #if (_MSC_VER >= 1400) // VS2005 #include #define _WTL_CE_DRA #endif // (_MSC_VER >= 1400) #if !defined(_WTL_CE_NO_DIALOGS) && !defined(__ATLFRAME_H__) #error Orientation aware dialog classes require atlframe.h to be included first #endif #if !defined(_WTL_CE_NO_APPWINDOW) && !defined(__ATLFRAME_H__) #error Application window class require atlframe.h to be included first #endif #if !defined(_WTL_CE_NO_ZOOMSCROLL) && !defined(__ATLSCRL_H__) #error ZoomScroll implementation requires atlscrl.h to be included first #endif #if !defined(_WTL_CE_NO_ZOOMSCROLL) #if !(defined(__ATLTYPES_H__) || (defined(__ATLMISC_H__) && !defined(_WTL_NO_WTYPES))) #error ZoomScroll requires _WTL_NO_WTYPES not to be defined and either atlmisc.h or atltypes.h to be included first #endif // !(defined(__ATLTYPES_H__) || (defined(__ATLMISC_H__) && !defined(_WTL_NO_WTYPES))) #endif // !defined(_WTL_CE_NO_ZOOMSCROLL) #if !defined(WIN32_PLATFORM_WFSP) && !defined(WIN32_PLATFORM_PSPC) #define _WTL_CE_NO_CONTROLS #endif // !defined(WIN32_PLATFORM_WFSP) && !defined(WIN32_PLATFORM_PSPC) #ifndef _WTL_CE_NO_CONTROLS #ifndef __ATLCTRLS_H__ #error The PPC/SmartPhone controls classes require atlctrls.h to be included first #endif #include #pragma comment(lib, "htmlview.lib") #include #pragma comment(lib, "voicectl.lib") #ifdef WIN32_PLATFORM_PSPC #include #pragma comment(lib, "richink.lib") #include #pragma comment(lib, "inkx.lib") #include #pragma comment(lib, "doclist.lib") #endif #endif /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // CStdDialogBase : Standard PPC/SmartPhone dialog base class // CStdDialogImplBase - Base implementation of standard dialog // CStdDialogImpl : Standard dialog implementation // CStdIndirectDialogImpl - implementation of standard indirect PPC/SmartPhone dialog // CStdAxDialogImpl : Standard AxDialog implementation // CStdSimpleDialog : Standard simple dialog // CStdDialogResizeImplBase - Base implementation of orientation resizing standard dialog // CStdDialogResizeImpl : Orientation resizing standard dialog implementation // CStdAxDialogResizeImpl - implementation of orientation resizing standard AxDialog // CStdSimpleDialogResizeImpl : Standard resizing simple dialog implementation // CStdOrientedDialogBase - Oriented PPC standard dialog base class // CStdOrientedDialogImplBase - Oriented PPC standard dialog base implementation // CStdOrientedDialogImpl : Oriented PPC standard dialog implementation // CStdAxOrientedDialogImpl - Oriented PPC standard AxDialog implementation // CStdSimpleOrientedDialog : Standard simple orientable dialog // // CAppInfoBase : Helper for application state save/restore to registry // CAppInfoT : CAppInfoBase constructed from a CAppWindow // CAppWindowBase : Base class for PPC/SmartPhone well-behaved application window or dialog // CAppWindow : PPC/SmartPhone well-behaved application window class // CAppDialog : PPC/SmartPhone well-behaved application dialog class // CAppStdDialogImplBase - Base implementation of standard application dialogs // CAppStdDialogImpl : Implementation of standard application dialog // CAppStdDialogResizeImpl - implementation of orientation resizing standard application dialog // CAppStdAxDialogImpl - Implementation of standard application AxDialog // CAppStdAxDialogResizeImpl - implementation of orientation resizing standard application AxDialog // CAppStdOrientedDialogImpl - implementation of oriented PPC standard application dialog // CAppStdAxOrientedDialogImpl - implementation of oriented PPC standard application AxDialog // // CFullScreenFrame : Full screen frame class // // CZoomScrollImpl : WinCE zooming implementation // // CBottomTabViewImpl - CBottomTabView // CHtmlCtrlT - CHtmlCtrl // CRichInkCtrlT - CRichInkCtrl // CInkXCtrlT - CInkXCtrl // CVoiceRecorderCtrlT - CVoiceRecorderCtrl // CDocListCtrlT - CDocListCtrl // CCapEditT - CCapEdit // CTTStaticT - CTTStatic // CTTButtonT - CTTButton // // CSpinCtrlT - CSpinCtrl : SmartPhone specific UpDown control // CSpinned : SmartPhone association of control and Spin // CSpinListBox : SmartPhone spinned ListBox control // CExpandListBox : SmartPhone expandable ListBox control // CExpandEdit : SmartPhone expandable Edit control // CExpandCapEdit : SmartPhone expandable CapEdit control // // Global functions: // AtlCreateMenuBar() // AtlCreateEmptyMenuBar() // AtlIsEditFocus() // AtlActivateBackKey() namespace WTL { /////////////////////////////////////////////////////////////////////////////// // MenuBar creation functions for property sheets and dialogs // Frame windows use CreateSimpleCEMenuBar inline HWND AtlCreateMenuBar(SHMENUBARINFO& mbi) { ATLASSERT(::IsWindow(mbi.hwndParent)); ATLVERIFY(::SHCreateMenuBar(&mbi) != FALSE); return mbi.hwndMB; }; inline HWND AtlCreateMenuBar(HWND hWnd, UINT nToolBarId = ATL_IDW_TOOLBAR, DWORD dwFlags = 0, int nBmpId = 0, int cBmpImages = 0, COLORREF clrBk = 0) { SHMENUBARINFO mbi = { sizeof(mbi), hWnd, dwFlags, nToolBarId, ModuleHelper::GetResourceInstance(), nBmpId, cBmpImages, 0, clrBk }; return AtlCreateMenuBar(mbi); } inline HWND AtlCreateEmptyMenuBar(HWND hWnd, bool bSip = true) { SHMENUBARINFO embi = { sizeof(SHMENUBARINFO), hWnd, SHCMBF_EMPTYBAR }; if (!bSip) embi.dwFlags |= SHCMBF_HIDESIPBUTTON; return AtlCreateMenuBar(embi); } /////////////////////////////////////////////////////////////////////////////// // Helper functions for SmartPhone back key handling inline bool AtlIsEditFocus() { ATL::CWindow wCtrl = GetFocus(); if (wCtrl.IsWindow()) { TCHAR szClassName[8] = {0}; ATLVERIFY(::GetClassName(wCtrl.m_hWnd, szClassName, 8)); return !_tcscmp(szClassName, _T("Edit")) || !_tcscmp(szClassName, WC_CAPEDIT); } return false; } #if defined WIN32_PLATFORM_WFSP inline void AtlActivateBackKey(HWND hMenuBar) { ATLASSERT(::IsWindow(hMenuBar)); ::SendMessage(hMenuBar, SHCMBM_OVERRIDEKEY, VK_TBACK, MAKELPARAM(SHMBOF_NODEFAULT | SHMBOF_NOTIFY, SHMBOF_NODEFAULT | SHMBOF_NOTIFY)); } #endif // WIN32_PLATFORM_WFSP // --- Standard PPC/SmartPhone dialogs --- #ifndef _WTL_CE_NO_DIALOGS /////////////////////////////////////////////////////////////////////////////// // CStdDialogBase - base class for standard PPC/SmartPhone dialogs #define WTL_STD_SHIDIF SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN #define WTL_SP_SHIDIF SHIDIF_SIZEDLGFULLSCREEN // Title setting macros #define WTL_DLG_TITLEHEIGHT(iHeight) static const int GetTitleHeight(){return iHeight;} #define WTL_DLG_NOTITLE WTL_DLG_TITLEHEIGHT(0) /////////////////////////////////////////////////////////////////////////////// // CStdDialogBase - Base class for standard PPC/SmartPhone dialog template class CStdDialogBase { public: #ifdef WIN32_PLATFORM_PSPC // Pocket PC only Dialog title handling const int nTitleHeight; CStdDialogBase() : nTitleHeight(T::GetTitleHeight()) { } // Overloads BOOL GetClientRect(LPRECT lpRect) { T* pT = static_cast(this); ATLASSERT(pT->IsWindow()); BOOL bRes = ::GetClientRect(pT->m_hWnd, lpRect); if (nTitleHeight) lpRect->top += nTitleHeight + 1; return bRes; } BOOL SetWindowText(LPCTSTR lpszString) { T* pT = static_cast(this); ATLASSERT(pT->IsWindow()); BOOL bRes = ::SetWindowText(pT->m_hWnd, lpszString); if (nTitleHeight != 0) pT->DoPaintTitle(); return bRes; } // Overrideables static const int GetTitleHeight() { #ifdef _WTL_CE_DRA return DRA::SCALEY(24); #else // !_WTL_CE_DRA CWindowDC dc(NULL); return dc.GetDeviceCaps(LOGPIXELSY) >> 2; // LOGPIXELSY * 24 / 96, #endif // !_WTL_CE_DRA } // Title painting bool DoPaintTitle() { T* pT = static_cast(this); ATLASSERT(pT->IsWindow()); TCHAR sTitle[48] = { 0 }; // Preparation CPaintDC dc(pT->m_hWnd); CFont fontTitle = AtlCreateBoldFont(); CFontHandle fontOld = dc.SelectFont(fontTitle); dc.SetTextColor(GetSysColor(COLOR_HIGHLIGHT)); int nLen = pT->GetWindowText(sTitle, 48); int nWidth = dc.GetDeviceCaps(HORZRES); // Display title text RECT rTitle = { 0, 0, nWidth, nTitleHeight }; dc.FillRect(&rTitle, COLOR_3DHIGHLIGHT); #ifdef _WTL_CE_DRA rTitle.left = DRA::SCALEX(8); #else // !_WTL_CE_DRA rTitle.left = nTitleHeight / 3; // 8 == 24 / 3 #endif // !_WTL_CE_DRA dc.DrawText(sTitle, nLen, &rTitle, DT_VCENTER | DT_SINGLELINE); dc.SelectFont(fontOld); // Draw bottom line, 2 pixels thick if HI_RES_AWARE CPenHandle penOld = dc.SelectStockPen(BLACK_PEN); POINT line[4] = {{0, nTitleHeight}, {nWidth, nTitleHeight}, {0, nTitleHeight - 1}, {nWidth, nTitleHeight - 1}}; #ifdef _WTL_CE_DRA int nSeg = DRA::SCALEY(1); #else // !_WTL_CE_DRA int nSeg = nTitleHeight / 24; #endif // !_WTL_CE_DRA dc.Polyline(line, nSeg <= 2 ? nSeg * 2 : 4); dc.SelectPen(penOld); return false; } // Title preparation: move the dialog controls down to make room for title void DialogTitleInit() { T* pT = static_cast(this); ATLASSERT(pT->IsWindow()); ATL::CWindow wCtl = pT->GetWindow(GW_CHILD); while (wCtl.IsWindow()) { RECT rCtl = { 0 }; wCtl.GetWindowRect(&rCtl); ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rCtl, 2); ::OffsetRect(&rCtl, 0, nTitleHeight); wCtl.MoveWindow(&rCtl, FALSE); wCtl = wCtl.GetWindow(GW_HWNDNEXT); } } // SIP management void DoSipInfo() { T* pT = static_cast(this); ATLASSERT(pT->IsWindow()); SIPINFO si = {sizeof(SIPINFO)}; SipGetInfo(&si); if ((si.fdwFlags & SIPF_ON) ^ SIPF_ON) si.rcVisibleDesktop.bottom = si.rcSipRect.bottom; pT->MoveWindow(&si.rcVisibleDesktop, FALSE); } // Title painting handler LRESULT OnPaintTitle(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); return bHandled = nTitleHeight ? pT->DoPaintTitle() : FALSE; } // SIP handler LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); if (wParam == SPI_SETSIPINFO) { pT->DoSipInfo(); return TRUE; } return bHandled = FALSE; } #elif defined WIN32_PLATFORM_WFSP // SmartPhone VK_TBACK key standard management LRESULT OnHotKey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { T* pT = static_cast(this); const UINT uModif = (UINT)LOWORD(lParam); const UINT uVirtKey = (UINT)HIWORD(lParam); if(uVirtKey == VK_TBACK) if (AtlIsEditFocus()) ::SHSendBackToFocusWindow(uMsg, wParam, lParam); else if (uModif & MOD_KEYUP) pT->StdCloseDialog(IDCANCEL); return 1; } // SmartPhone MenuBar and VK_TBACK key initialization void StdSPInit() { T* pT = static_cast(this); HWND hMenuBar = ::SHFindMenuBar(pT->m_hWnd); if (!hMenuBar && (t_shidiFlags & SHIDIF_DONEBUTTON)) hMenuBar = CreateMenuBar(ATL_IDM_MENU_DONE); if(hMenuBar != NULL) AtlActivateBackKey(hMenuBar); } void SetStaticBold() { T* pT = static_cast(this); ATLASSERT(pT->IsWindow()); CFontHandle fontBold = AtlCreateBoldFont(pT->GetFont()); ATL::CWindow wCtl = pT->GetWindow(GW_CHILD); while (wCtl.IsWindow()) { if ((short int)wCtl.GetDlgCtrlID() == IDC_STATIC) wCtl.SetFont(fontBold); wCtl = wCtl.GetWindow(GW_HWNDNEXT); } } #endif // WIN32_PLATFORM_WFSP // Platform dependant initialization void StdPlatformInit() { T* pT = static_cast(this); #ifdef WIN32_PLATFORM_PSPC // Pocket PC title initialization if (nTitleHeight != 0) pT->DialogTitleInit(); #elif defined(WIN32_PLATFORM_WFSP) pT->StdSPInit(); SetStaticBold(); #endif // WIN32_PLATFORM_WFSP } // Menu bar creation HWND CreateMenuBar(UINT uiMB = T::IDD, int nBmpImages = 0) { T* pT = static_cast(this); return AtlCreateMenuBar(pT->m_hWnd, uiMB, 0, nBmpImages ? uiMB : 0, nBmpImages); } // Dialog closing void StdCloseDialog(WORD wID) { T* pT = static_cast(this); if (t_bModal) ::EndDialog(pT->m_hWnd, wID); else pT->DestroyWindow(); } // Shell dialog layout initialization void StdShidInit() { T* pT = static_cast(this); SHINITDLGINFO shidi = { SHIDIM_FLAGS, pT->m_hWnd, t_shidiFlags }; ::SHInitDialog(&shidi); } // IDC_INFOSTATIC background setting LRESULT OnColorStatic(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if (::GetDlgCtrlID((HWND)lParam) == IDC_INFOSTATIC) { ::SetBkMode((HDC)wParam, TRANSPARENT); return (LRESULT)::GetSysColorBrush(COLOR_INFOBK); } return bHandled = FALSE; } // Menu dialog ending LRESULT OnMenuClose(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->StdCloseDialog((WORD)(wID - ID_MENU_OK + IDOK)); return 0; } // Standard dialog ending: may be used with any command LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->StdCloseDialog(wID); return 0; } }; /////////////////////////////////////////////////////////////////////////////// // CStdDialogImplBase - Base implementation of standard PPC/SmartPhone dialog template > class ATL_NO_VTABLE CStdDialogImplBase : public TBase, public CStdDialogBase { public: #ifdef WIN32_PLATFORM_PSPC BOOL GetClientRect(LPRECT lpRect) { return CStdDialogBase::GetClientRect(lpRect); } BOOL SetWindowText(LPCTSTR lpszString) { return CStdDialogBase::SetWindowText(lpszString); } #endif BEGIN_MSG_MAP(CStdDialogImplBase) #ifdef WIN32_PLATFORM_PSPC // Pocket PC title and SIP MESSAGE_HANDLER(WM_PAINT, OnPaintTitle) MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange) #elif defined(WIN32_PLATFORM_WFSP) // SmartPhone VK_TBACK key MESSAGE_HANDLER(WM_HOTKEY, OnHotKey) #endif MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnColorStatic) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_RANGE_HANDLER(IDOK, IDCANCEL, OnCloseCmd) COMMAND_RANGE_HANDLER(ID_MENU_OK, ID_MENU_CANCEL, OnMenuClose) END_MSG_MAP() LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); ATLASSERT(t_bModal == pT->m_bModal); pT->StdPlatformInit(); pT->StdShidInit(); return bHandled = FALSE; } }; /////////////////////////////////////////////////////////////////////////////// // CStdDialogImpl - implementation of standard PPC/SmartPhone dialog template class ATL_NO_VTABLE CStdDialogImpl : public CStdDialogImplBase< T, t_shidiFlags, t_bModal> {}; /////////////////////////////////////////////////////////////////////////////// // CStdIndirectDialogImpl - implementation of standard indirect PPC/SmartPhone dialog #if defined __ATLDLGS_H__ template class ATL_NO_VTABLE CStdIndirectDialogImpl : public CIndirectDialogImpl< T, CMemDlgTemplate, CStdDialogImpl > { public: typedef CIndirectDialogImpl< T, CMemDlgTemplate, CStdDialogImpl > _baseClass; typedef CStdDialogImpl _baseStd; void CheckStyle() { // Mobile devices don't support DLGTEMPLATEEX ATLASSERT(!m_Template.IsTemplateEx()); // Standard dialogs need only DS_CENTER DWORD &dwStyle = m_Template.GetTemplatePtr()->style; if (dwStyle & DS_CENTER) if(t_bModal) { ATLASSERT((dwStyle & WS_CHILD) != WS_CHILD); dwStyle |= WS_POPUP; } else { if((dwStyle & WS_CHILD) != WS_CHILD) dwStyle |= WS_POPUP; } } INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow(), LPARAM dwInitParam = NULL) { ATLASSERT(t_bModal); if (!m_Template.IsValid()) CreateTemplate(); CheckStyle(); return _baseClass::DoModal(hWndParent, dwInitParam); } HWND Create(HWND hWndParent, LPARAM dwInitParam = NULL) { ATLASSERT(!t_bModal); if (!m_Template.IsValid()) CreateTemplate(); CheckStyle(); return _baseClass::Create(hWndParent, dwInitParam); } BEGIN_MSG_MAP(CStdIndirectDialogImpl) CHAIN_MSG_MAP(_baseStd) END_MSG_MAP() }; #endif // defined __ATLDLGS_H__ #ifndef _ATL_NO_HOSTING /////////////////////////////////////////////////////////////////////////////// // CStdAxDialogImpl - implementation of standard PPC/SmartPhone AxDialog template class ATL_NO_VTABLE CStdAxDialogImpl : public CStdDialogImplBase< T, t_shidiFlags, t_bModal, ATL::CAxDialogImpl< T > > {}; #endif // _ATL_NO_HOSTING /////////////////////////////////////////////////////////////////////////////// // CStdSimpleDialog - standard PPC/SmartPhone simple dialog with SHIDIF_xxx flags template class CStdSimpleDialog : public ATL::CSimpleDialog, public CStdDialogBase, t_shidiFlags> { public: typedef CStdDialogBase, t_shidiFlags> baseClass; #ifdef WIN32_PLATFORM_PSPC BOOL GetClientRect(LPRECT lpRect) { return baseClass::GetClientRect(lpRect); } BOOL SetWindowText(LPCTSTR lpszString) { return baseClass::SetWindowText(lpszString); } #endif BEGIN_MSG_MAP(CStdSimpleDialog) #ifdef WIN32_PLATFORM_PSPC // Pocket PC title and SIP MESSAGE_HANDLER(WM_PAINT, OnPaintTitle) MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange) #elif defined(WIN32_PLATFORM_WFSP) // SmartPhone VK_TBACK key MESSAGE_HANDLER(WM_HOTKEY, OnHotKey) #endif MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnColorStatic) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_RANGE_HANDLER(ID_MENU_OK, ID_MENU_CANCEL, OnMenuClose) COMMAND_RANGE_HANDLER(IDOK, IDCANCEL, baseClass::OnCloseCmd) END_MSG_MAP() LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { StdPlatformInit(); StdShidInit(); return bHandled = FALSE; } }; /////////////////////////////////////////////////////////////////////////////// // CStdDialogResizeImplBase - Base implementation of orientation resizing standard PPC/SmartPhone dialog template > class ATL_NO_VTABLE CStdDialogResizeImplBase : public CStdDialogImplBase< T, t_shidiFlags, t_bModal, TBase>, public CDialogResize { public: // Note: BEGIN_DLGRESIZE_MAP is required in the derived class. BEGIN_MSG_MAP(CStdResizeDialogImplBase) #ifdef WIN32_PLATFORM_PSPC // Pocket PC title MESSAGE_HANDLER(WM_PAINT, OnPaintTitle) #elif defined(WIN32_PLATFORM_WFSP) // SmartPhone VK_TBACK key MESSAGE_HANDLER(WM_HOTKEY, OnHotKey) #endif MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnColorStatic) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_RANGE_HANDLER(IDOK, IDCANCEL, OnCloseCmd) COMMAND_RANGE_HANDLER(ID_MENU_OK, ID_MENU_CANCEL, OnMenuClose) CHAIN_MSG_MAP(CDialogResize< T >) END_MSG_MAP() LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); ATLASSERT(t_bModal == pT->m_bModal); pT->StdPlatformInit(); pT->DlgResize_Init(FALSE); pT->StdShidInit(); return bHandled = FALSE; } }; /////////////////////////////////////////////////////////////////////////////// // CStdDialogResizeImpl - implementation of orientation resizing standard PPC/SmartPhone dialog template class ATL_NO_VTABLE CStdDialogResizeImpl : public CStdDialogResizeImplBase< T, t_shidiFlags, t_bModal> {}; #ifndef _ATL_NO_HOSTING /////////////////////////////////////////////////////////////////////////////// // CStdAxDialogResizeImpl - implementation of orientation resizing standard PPC/SmartPhone AxDialog template class ATL_NO_VTABLE CStdAxDialogResizeImpl : public CStdDialogResizeImplBase< T, t_shidiFlags, t_bModal, ATL::CAxDialogImpl > {}; #endif // _ATL_NO_HOSTING /////////////////////////////////////////////////////////////////////////////// // CStdSimpleDialogResizeImpl - implementation of standard resizing simple dialog with SHIDIF_xxx flags // Usage: // class CMyDlg : public CStdSimpleDialogResize // { // public: // BEGIN_DLGRESIZE_MAP(CMyDlg) // ... // END_DLGRESIZE_MAP() // }; template class ATL_NO_VTABLE CStdSimpleDialogResizeImpl : public CStdSimpleDialog, public CDialogResize< T > { public: typedef CStdSimpleDialog::baseClass baseClass; BEGIN_MSG_MAP(CStdSimpleDialogResizeImpl) #ifdef WIN32_PLATFORM_PSPC // Pocket PC title MESSAGE_HANDLER(WM_PAINT, OnPaintTitle) #elif defined(WIN32_PLATFORM_WFSP) // SmartPhone VK_TBACK key MESSAGE_HANDLER(WM_HOTKEY, OnHotKey) #endif MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnColorStatic) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_RANGE_HANDLER(IDOK, IDCANCEL, baseClass::OnCloseCmd) COMMAND_RANGE_HANDLER(ID_MENU_OK, ID_MENU_CANCEL, OnMenuClose) CHAIN_MSG_MAP(CDialogResize< T >) END_MSG_MAP() LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); pT->StdPlatformInit(); pT->DlgResize_Init(FALSE); pT->StdShidInit(); return bHandled = FALSE; } }; #if defined(_WTL_CE_DRA) && defined(WIN32_PLATFORM_PSPC) /////////////////////////////////////////////////////////////////////////////// // CStdOrientedDialogBase - Oriented PPC standard dialog base class template class CStdOrientedDialogBase { public: // Operation BOOL SetOrientation(DRA::DisplayMode mode) { T* pT = static_cast(this); ATLASSERT(pT->IsWindow()); ATLASSERT(mode == DRA::GetDisplayMode()); // Derived dialog must enumerate TWO dialog templates with the same control ids and types ie: // enum { IDD = IDD_MYDLG, IDD_LANDSCAPE = IDD_MYDLG_L }; UINT iResource = (mode == DRA::Landscape)? T::IDD_LANDSCAPE : T::IDD; BOOL bRes = DRA::RelayoutDialog(ModuleHelper::GetResourceInstance(), pT->m_hWnd, MAKEINTRESOURCE(iResource)); pT->OnOrientation(mode); return bRes; } // Override void OnOrientation(DRA::DisplayMode /*mode*/) {} // Message handlers LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); ATLASSERT(pT->IsWindow()); if (wParam == SETTINGCHANGE_RESET) { pT->SetOrientation(DRA::GetDisplayMode()); pT->StdPlatformInit(); pT->StdShidInit(); } else if (wParam == SPI_SETSIPINFO) { pT->DoSipInfo(); return TRUE; } return bHandled = FALSE; } }; /////////////////////////////////////////////////////////////////////////////// // CStdOrientedDialogImplBase - Oriented PPC standard dialog base implementation template > class ATL_NO_VTABLE CStdOrientedDialogImplBase : public CStdDialogImplBase< T, t_shidiFlags, t_bModal, TBase>, public CStdOrientedDialogBase { public: BEGIN_MSG_MAP(CStdOrientedDialogImpl) MESSAGE_HANDLER(WM_PAINT, OnPaintTitle) MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnColorStatic) MESSAGE_HANDLER(WM_SETTINGCHANGE, CStdOrientedDialogBase::OnSettingChange) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_RANGE_HANDLER(IDOK, IDCANCEL, OnCloseCmd) COMMAND_RANGE_HANDLER(ID_MENU_OK, ID_MENU_CANCEL, OnMenuClose) END_MSG_MAP() LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); #ifdef _DEBUG ATLASSERT(t_bModal == pT->m_bModal); #endif if (DRA::GetDisplayMode() == DRA::Landscape) pT->SetOrientation(DRA::Landscape); pT->StdPlatformInit(); pT->StdShidInit(); return bHandled = FALSE; } }; /////////////////////////////////////////////////////////////////////////////// // CStdOrientedDialogImpl - Oriented PPC standard dialog implementation template class ATL_NO_VTABLE CStdOrientedDialogImpl : public CStdOrientedDialogImplBase< T, t_shidiFlags, t_bModal> {}; #ifndef _ATL_NO_HOSTING /////////////////////////////////////////////////////////////////////////////// // CStdAxOrientedDialogImpl - Oriented PPC standard AxDialog implementation template class ATL_NO_VTABLE CStdAxOrientedDialogImpl : public CStdOrientedDialogImplBase< T, t_shidiFlags, t_bModal, ATL::CAxDialogImpl > {}; #endif // _ATL_NO_HOSTING /////////////////////////////////////////////////////////////////////////////// // CStdSimpleOrientedDialog - Standard simple orientable dialog template class CStdSimpleOrientedDialog : public CStdSimpleDialog, public CStdOrientedDialogBase > { public: typedef CStdSimpleDialog::baseClass baseClass; typedef CStdOrientedDialogBase > baseOriented; enum {IDD = t_wDlgTemplateID, IDD_LANDSCAPE = t_wDlgLandscapeID}; BEGIN_MSG_MAP(CStdSimpleDialog) MESSAGE_HANDLER(WM_PAINT, OnPaintTitle) MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnColorStatic) MESSAGE_HANDLER(WM_SETTINGCHANGE, baseOriented::OnSettingChange) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_RANGE_HANDLER(IDOK, IDCANCEL, baseClass::OnCloseCmd) COMMAND_RANGE_HANDLER(ID_MENU_OK, ID_MENU_CANCEL, OnMenuClose) END_MSG_MAP() LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if (DRA::GetDisplayMode() == DRA::Landscape) SetOrientation(DRA::Landscape); StdPlatformInit(); StdShidInit(); return bHandled = FALSE; } }; #endif // _WTL_CE_DRA #endif // _WTL_CE_NO_DIALOGS // --- PPC/SmartPhone application window and helpers --- #ifndef _WTL_CE_NO_APPWINDOW /////////////////////////////////////////////////////////////////////////////// // CAppInfoBase - Helper for application state save/restore to registry class CAppInfoBase { public: CRegKeyEx m_Key; CAppInfoBase(ATL::_U_STRINGorID sAppKey) { m_Key.Create(HKEY_CURRENT_USER, sAppKey.m_lpstr); ATLASSERT(m_Key.m_hKey); } template LONG Save(V& val, ATL::_U_STRINGorID sName) { return m_Key.SetBinaryValue(sName.m_lpstr, &val, sizeof(V)); } template LONG Save(int nb, V& val0, ATL::_U_STRINGorID sName) { return m_Key.SetBinaryValue(sName.m_lpstr, &val0, nb * sizeof(V)); } template LONG Restore(V& val, ATL::_U_STRINGorID sName) { ULONG bufSize = sizeof(V); return m_Key.QueryBinaryValue(sName.m_lpstr, &val, &bufSize); } template LONG Restore(int nb, V& val0, ATL::_U_STRINGorID sName) { ULONG bufSize = nb * sizeof(V); return m_Key.QueryBinaryValue(sName.m_lpstr, &val0, &bufSize); } #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) LONG Save(_CSTRING_NS::CString& sval, ATL::_U_STRINGorID sName) { return m_Key.SetStringValue(sName.m_lpstr, sval); } LONG Restore(_CSTRING_NS::CString& sval, ATL::_U_STRINGorID sName) { DWORD size = MAX_PATH; LONG res = m_Key.QueryStringValue(sName.m_lpstr, sval.GetBuffer(size), &size); sval.ReleaseBuffer(); return res; } #else #pragma message("Warning: CAppInfoBase compiles without CString support. Do not use CString in Save or Restore.") #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) LONG Save(LPCTSTR sval, ATL::_U_STRINGorID sName) { return m_Key.SetStringValue(sName.m_lpstr, sval); } LONG Restore(LPTSTR sval, ATL::_U_STRINGorID sName, DWORD *plength) { return m_Key.QueryStringValue(sName.m_lpstr, sval, plength); } LONG Delete(ATL::_U_STRINGorID sName) { return m_Key.DeleteValue(sName.m_lpstr); } }; /////////////////////////////////////////////////////////////////////////////// // CAppInfoT - CAppInfoBase constructed from a class with T::GetAppKey() // Macro for declaring AppKey #define DECLARE_APPKEY(uAppKey) \ static LPCTSTR GetAppKey() \ { \ static LPCTSTR sAppKey = ATL::_U_STRINGorID(uAppKey).m_lpstr; \ return sAppKey; \ } template class CAppInfoT : public CAppInfoBase { public: CAppInfoT() : CAppInfoBase(T::GetAppKey()){} }; /////////////////////////////////////////////////////////////////////////////// // CAppWindowBase - Base class for PPC/SmartPhone "well-behaved" application window or dialog // Macros for declaring frame WNDCLASS and AppKey #define DECLARE_APP_FRAME_CLASS(WndClassName, uCommonResourceID, uAppKey) \ DECLARE_FRAME_WND_CLASS(WndClassName, uCommonResourceID) \ DECLARE_APPKEY(uAppKey) #define DECLARE_APP_FRAME_CLASS_EX(WndClassName, uCommonResourceID, style, bkgnd, uAppKey) \ DECLARE_FRAME_WND_CLASS_EX(WndClassName, uCommonResourceID, style, bkgnd) \ DECLARE_APPKEY(uAppKey) template class CAppWindowBase { public: typedef class CAppInfoT< T > CAppInfo; #ifndef WIN32_PLATFORM_WFSP SHACTIVATEINFO m_sai; // NoOp on SmartPhones #endif // WIN32_PLATFORM_WFSP bool m_bHibernate; CAppWindowBase< T >() : m_bHibernate(false) { #ifndef WIN32_PLATFORM_WFSP SHACTIVATEINFO sai = { sizeof(SHACTIVATEINFO) }; m_sai = sai; #endif // WIN32_PLATFORM_WFSP }; // Same as WTL 7.1 AppWizard generated ActivatePreviousInstance + SendMessage WM_COPYDATA static HRESULT ActivatePreviousInstance(HINSTANCE hInstance, LPCTSTR lpstrCmdLine, bool bDialog) { // requires T does DECLARE_APP_FRAME_CLASS, DECLARE_APP_FRAME_CLASS_EX or DECLARE_APP_DLG_CLASS CFrameWndClassInfo& classInfo = T::GetWndClassInfo(); ATLVERIFY(::LoadString(hInstance, classInfo.m_uCommonResourceID, classInfo.m_szAutoName, sizeof(classInfo.m_szAutoName)/sizeof(classInfo.m_szAutoName[0])) != 0); classInfo.m_wc.lpszClassName = classInfo.m_szAutoName; const TCHAR* pszClass = classInfo.m_wc.lpszClassName; if(NULL == pszClass || '\0' == *pszClass) { return E_FAIL; } const DWORD dRetryInterval = 100; const int iMaxRetries = 25; for(int i = 0; i < iMaxRetries; ++i) { HANDLE hMutex = CreateMutex(NULL, FALSE, pszClass); DWORD dw = GetLastError(); if(NULL == hMutex) { HRESULT hr; switch(dw) { case ERROR_INVALID_HANDLE: // A non-mutext object with this name already exists. hr = E_INVALIDARG; break; default: // This should never happen... hr = E_FAIL; } return hr; } // If the mutex already exists, then there should be another instance running if(dw == ERROR_ALREADY_EXISTS) { CloseHandle(hMutex); HWND hwnd = NULL; if (bDialog) hwnd = FindWindow(NULL, pszClass); else hwnd = FindWindow(pszClass, NULL); if(hwnd == NULL) { Sleep(dRetryInterval); continue; } else { // Transmit our params to previous instance if (lpstrCmdLine && *lpstrCmdLine) { COPYDATASTRUCT cd = { NULL, sizeof(TCHAR) * (wcslen(lpstrCmdLine) + 1), (PVOID)lpstrCmdLine }; ::SendMessage(hwnd, WM_COPYDATA, NULL, (LPARAM)&cd); } // Set the previous instance as the foreground window if(0 != SetForegroundWindow(reinterpret_cast(reinterpret_cast(hwnd) | 0x1))) return S_FALSE; } } else { return S_OK; } } return S_OK; } // Operations overriden in derived class bool AppHibernate(bool /*bHibernate*/) { return false; } bool AppNewInstance(LPCTSTR /*lpstrCmdLine*/) { return false; } void AppSave() { } #ifdef WIN32_PLATFORM_WFSP void AppBackKey() { ::SHNavigateBack(); } #endif // Message map and handlers BEGIN_MSG_MAP(CAppWindowBase) MESSAGE_HANDLER(WM_ACTIVATE, OnActivate) #ifdef WIN32_PLATFORM_WFSP MESSAGE_HANDLER(WM_HOTKEY, OnHotKey) #else MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange) #endif // WIN32_PLATFORM_WFSP MESSAGE_HANDLER(WM_HIBERNATE, OnHibernate) MESSAGE_HANDLER(WM_COPYDATA, OnNewInstance) MESSAGE_HANDLER(WM_CLOSE, OnClose) END_MSG_MAP() LRESULT OnActivate(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { T* pT = static_cast(this); if (m_bHibernate) m_bHibernate = pT->AppHibernate(false); #ifndef WIN32_PLATFORM_WFSP ::SHHandleWMActivate(pT->m_hWnd, wParam, lParam, &m_sai, 0); #else wParam; lParam; #endif // WIN32_PLATFORM_WFSP return bHandled = FALSE; } #ifdef WIN32_PLATFORM_WFSP // SmartPhone VK_TBACK key standard management LRESULT OnHotKey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { T* pT = static_cast(this); const UINT uModif = (UINT)LOWORD(lParam); const UINT uVirtKey = (UINT)HIWORD(lParam); if(uVirtKey == VK_TBACK) if (AtlIsEditFocus()) ::SHSendBackToFocusWindow(uMsg, wParam, lParam); else if (uModif & MOD_KEYUP) pT->AppBackKey(); return 1; } #else // !WIN32_PLATFORM_WFSP // PPC SIP handling LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { T* pT = static_cast(this); bHandled = FALSE; return ::SHHandleWMSettingChange(pT->m_hWnd, wParam, lParam, &m_sai); } #endif // !WIN32_PLATFORM_WFSP LRESULT OnHibernate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); return m_bHibernate = pT->AppHibernate(true); } LRESULT OnNewInstance(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/) { T* pT = static_cast(this); PCOPYDATASTRUCT pcds = (PCOPYDATASTRUCT)lParam; return pT->AppNewInstance((LPCTSTR)pcds->lpData); } LRESULT OnClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); pT->AppSave(); bHandled = FALSE; return 1; } }; /////////////////////////////////////////////////////////////////////////////// // CAppWindow - PPC/SmartPhone "well-behaved" application window class template class CAppWindow : public CAppWindowBase< T > { public: // Same as WTL 7.1 AppWizard generated Run + lpstrCmdLine in CreateEx static int AppRun(LPTSTR lpstrCmdLine = NULL, int nCmdShow = SW_SHOWNORMAL) { CMessageLoop theLoop; _Module.AddMessageLoop(&theLoop); T wndMain; if(wndMain.CreateEx(NULL, NULL, 0, 0, lpstrCmdLine) == NULL) { ATLTRACE2(atlTraceUI, 0, _T("Main window creation failed!\n")); return 0; } wndMain.ShowWindow(nCmdShow); int nRet = theLoop.Run(); _Module.RemoveMessageLoop(); return nRet; } static HRESULT ActivatePreviousInstance(HINSTANCE hInstance, LPCTSTR lpstrCmdLine) { return CAppWindowBase< T >::ActivatePreviousInstance(hInstance, lpstrCmdLine, false); } }; #ifndef _WTL_CE_NO_DIALOGS /////////////////////////////////////////////////////////////////////////////// // CAppDialog - PPC/SmartPhone "well-behaved" dialog application class // Macro for declaring dialog WNDCLASS and AppKey #define DECLARE_APP_DLG_CLASS(WndClassName, uCommonResourceID, uAppKey) \ static WTL::CFrameWndClassInfo& GetWndClassInfo() \ { \ static WTL::CFrameWndClassInfo wc = \ { \ { 0, (WNDPROC)StartDialogProc, \ 0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName }, \ NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \ }; \ return wc; \ }; \ DECLARE_APPKEY(uAppKey) template class CAppDialog : public CAppWindowBase< T > { public: static int AppRun(LPTSTR lpstrCmdLine = NULL, int nCmdShow = SW_SHOWNORMAL) { CMessageLoop theLoop; _Module.AddMessageLoop(&theLoop); T dlgMain; if(dlgMain.Create(NULL, (LPARAM)lpstrCmdLine) == NULL) { ATLTRACE2(atlTraceUI, 0, _T("Main dialog creation failed!\n")); return 0; } dlgMain.ShowWindow(nCmdShow); int nRet = theLoop.Run(); _Module.RemoveMessageLoop(); return nRet; } static HRESULT ActivatePreviousInstance(HINSTANCE hInstance, LPCTSTR lpstrCmdLine) { return CAppWindowBase< T >::ActivatePreviousInstance(hInstance, lpstrCmdLine, true); }; }; // PPC/SmartPhone standard application dialogs #ifdef WIN32_PLATFORM_WFSP #define WTL_APP_SHIDIF WTL_SP_SHIDIF #else #define WTL_APP_SHIDIF WTL_STD_SHIDIF #endif /////////////////////////////////////////////////////////////////////////////// // CAppStdDialogImplBase - Base implementation of standard application dialogs template class ATL_NO_VTABLE CAppStdDialogImplBase : public TImplBase, public CAppDialog< T > { public: WTL_DLG_NOTITLE; void StdCloseDialog(int nVal) { T* pT = static_cast(this); if (nVal != IDCANCEL) pT->AppSave(); if (t_bModal == false) { pT->DestroyWindow(); ::PostQuitMessage(nVal); } else ::EndDialog(pT->m_hWnd, nVal); } BEGIN_MSG_MAP(CAppStdDialogImplBase) MESSAGE_HANDLER(WM_CLOSE, OnSystemClose) CHAIN_MSG_MAP(TImplBase) CHAIN_MSG_MAP(CAppDialog< T >) END_MSG_MAP() LRESULT OnSystemClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { T* pT = static_cast(this); pT->StdCloseDialog(IDCANCEL); return 0; } }; /////////////////////////////////////////////////////////////////////////////// // CAppStdDialogImpl - Implementation of standard application dialog template class ATL_NO_VTABLE CAppStdDialogImpl : public CAppStdDialogImplBase, t_shidiFlags, t_bModal> {}; /////////////////////////////////////////////////////////////////////////////// // CAppStdDialogResizeImpl - implementation of orientation resizing standard application dialog template class ATL_NO_VTABLE CAppStdDialogResizeImpl : public CAppStdDialogImplBase, t_shidiFlags, t_bModal> {}; #ifndef _ATL_NO_HOSTING /////////////////////////////////////////////////////////////////////////////// // CAppStdAxDialogImpl - Implementation of standard application AxDialog template class ATL_NO_VTABLE CAppStdAxDialogImpl : public CAppStdDialogImplBase, t_shidiFlags, t_bModal> {}; /////////////////////////////////////////////////////////////////////////////// // CAppStdAxDialogResizeImpl - implementation of orientation resizing standard application AxDialog template class ATL_NO_VTABLE CAppStdAxDialogResizeImpl : public CAppStdDialogImplBase, t_shidiFlags, t_bModal> {}; #endif // _ATL_NO_HOSTING #if defined(_WTL_CE_DRA) && defined(WIN32_PLATFORM_PSPC) /////////////////////////////////////////////////////////////////////////////// // CAppStdOrientedDialogImpl - implementation of oriented PPC standard application dialog template class ATL_NO_VTABLE CAppStdOrientedDialogImpl : public CAppStdDialogImplBase, t_shidiFlags, t_bModal> {}; #ifndef _ATL_NO_HOSTING /////////////////////////////////////////////////////////////////////////////// // CAppStdAxOrientedDialogImpl - implementation of oriented PPC standard application AxDialog template class ATL_NO_VTABLE CAppStdAxOrientedDialogImpl : public CAppStdDialogImplBase, t_shidiFlags, t_bModal> {}; #endif // _ATL_NO_HOSTING #endif // defined(_WTL_CE_DRA) && defined(WIN32_PLATFORM_PSPC) #endif // _WTL_CE_NO_DIALOGS #endif // _WTL_CE_NO_APPWINDOW // --- Full screen support --- #ifndef _WTL_CE_NO_FULLSCREEN /////////////////////////////////////////////////////////////////////////////// // CFullScreenFrame - full screen frame implementation template class CFullScreenFrame { public: bool m_bFullScreen; CFullScreenFrame() : m_bFullScreen(false) { } // Operation void SetFullScreen(bool bFull) { m_bFullScreen = bFull; ShowTaskBar(!bFull, false); ShowMenuBar(!bFull); } // Manage TaskBar for modal dialogs and property sheets template int FSDoModal(D& dlg) { T* pT = static_cast(this); pT; // avoid level 4 warning ATLASSERT(pT->IsWindow()); if (m_bFullScreen) // Show taskbar if hidden ShowTaskBar(true, false); int iRet = dlg.DoModal(); if (m_bFullScreen) // Hide taskbar if restored ShowTaskBar(false); return iRet; } // Implementation void ShowMenuBar(bool bShow) { T* pT = static_cast(this); ATLASSERT(pT->IsWindow()); ATL::CWindow MenuBar = pT->m_hWndCECommandBar; ATLASSERT(MenuBar.IsWindow()); MenuBar.ShowWindow(bShow ? SW_SHOWNORMAL : SW_HIDE); pT->SizeToMenuBar(); } void ShowTaskBar(bool bShow, bool bRepaint = true) { T* pT = static_cast(this); ATLASSERT(pT->IsWindow()); RECT rect = { 0 }; SystemParametersInfo(SPI_GETWORKAREA, NULL, &rect, FALSE); if (!bShow) rect.top = 0; #ifdef WIN32_PLATFORM_PSPC // Pocket PC code UINT uShow = t_bHasSip ? SHFS_SHOWTASKBAR | SHFS_SHOWSIPBUTTON : SHFS_SHOWTASKBAR | SHFS_HIDESIPBUTTON; SHFullScreen(pT->m_hWnd, bShow ? uShow : SHFS_HIDETASKBAR | SHFS_HIDESIPBUTTON); #elif _WIN32_WCE > 0x500 // Smartphone 2005 code SHFullScreen(pT->m_hWnd, bShow ? SHFS_SHOWTASKBAR : SHFS_HIDETASKBAR); #else // Smartphone 2003 HWND hTaskBar = FindWindow(_T("tray"), NULL); ATLASSERT(::IsWindow(hTaskBar)); ::ShowWindow(hTaskBar, bShow ? SW_SHOW : SW_HIDE); #endif // WIN32_PLATFORM_PSPC pT->MoveWindow(&rect, bRepaint); } // Message map and handler BEGIN_MSG_MAP(CFullScreenFrame) MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange) MESSAGE_HANDLER(WM_ACTIVATE, OnActivate) END_MSG_MAP() LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { #ifndef SETTINGCHANGE_RESET // not defined for PPC 2002 #define SETTINGCHANGE_RESET SPI_SETWORKAREA #endif if (m_bFullScreen && (wParam == SETTINGCHANGE_RESET)) SetFullScreen(m_bFullScreen); return bHandled = FALSE; } LRESULT OnActivate(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { if (m_bFullScreen) { ShowTaskBar(!wParam); ShowMenuBar(!wParam); } return bHandled = FALSE; } }; #endif // _WTL_CE_NO_FULLSCREEN // --- WinCE zoom support --- #ifndef _WTL_CE_NO_ZOOMSCROLL /////////////////////////////////////////////////////////////////////////////// // CZoomScrollImpl - WinCE zooming implementation on top of CScrollImpl template class CZoomScrollImpl: public CScrollImpl< T > { public: // Data members _WTYPES_NS::CSize m_sizeTrue; double m_fzoom; // Creation CZoomScrollImpl() : m_sizeTrue(0), m_fzoom(1.) { } // Zoom operations and access void SetZoomScrollSize(_WTYPES_NS::CSize sizeTrue, double fzoom = 1., BOOL bRedraw = TRUE) { ATLASSERT(fzoom > 0.); m_sizeTrue = sizeTrue; m_fzoom = fzoom; CScrollImpl< T >::SetScrollSize(sizeTrue / fzoom, bRedraw); } void SetZoomScrollSize(int cx, int cy, double fzoom=1., BOOL bRedraw = TRUE) { SetZoomScrollSize(_WTYPES_NS::CSize(cx, cy), fzoom, bRedraw); } void SetZoom(double fzoom, BOOL bRedraw = TRUE) { _WTYPES_NS::CPoint ptCenter = WndtoTrue(m_sizeClient / 2); _WTYPES_NS::CSize sizePage = GetScrollPage(); _WTYPES_NS::CSize sizeLine = GetScrollLine(); SetZoomScrollSize(GetScrollSize(), fzoom, bRedraw); SetScrollLine(sizeLine); SetScrollPage(sizePage); _WTYPES_NS::CPoint ptOffset = ptCenter - (m_sizeClient / 2) * fzoom; SetScrollOffset(ptOffset, bRedraw); } double GetZoom() { return m_fzoom; } // CScrollImpl overrides void SetScrollOffset(int x, int y, BOOL bRedraw = TRUE) { CScrollImpl< T >::SetScrollOffset((int)(x / m_fzoom), (int)(y / m_fzoom), bRedraw); } void SetScrollOffset(POINT ptOffset, BOOL bRedraw = TRUE) { SetScrollOffset(ptOffset.x, ptOffset.y, bRedraw); } void GetScrollOffset(POINT& ptOffset) { ptOffset.x = (LONG)(m_ptOffset.x * m_fzoom); ptOffset.y = (LONG)(m_ptOffset.y * m_fzoom); } void SetScrollSize(int cx, int cy, BOOL bRedraw = TRUE) { SetZoomScrollSize(cx, cy, GetZoom(), bRedraw); } void SetScrollSize(SIZE sizeTrue, BOOL bRedraw = TRUE) { SetZoomScrollSize(sizeTrue, GetZoom(), bRedraw); } void GetScrollSize(SIZE& sizeTrue) const { sizeTrue = m_sizeTrue; } void SetScrollPage(int cxPage, int cyPage) { SetScrollPage(_WTYPES_NS::CSize(cxPage, cyPage)); } void SetScrollPage(SIZE sizePage) { CScrollImpl< T >::SetScrollPage(sizePage / m_fzoom); } void GetScrollPage(SIZE& sizePage) const { sizePage = m_sizePage * m_fzoom; } void SetScrollLine(int cxLine, int cyLine) { SetScrollLine(_WTYPES_NS::CSize(cxLine, cyLine)); } void SetScrollLine(SIZE sizeLine) { CScrollImpl< T >::SetScrollLine(sizeLine / m_fzoom); } void GetScrollLine(SIZE& sizeLine) const { sizeLine = m_sizeLine * m_fzoom; } // Data access complements _WTYPES_NS::CSize GetScrollSize() { return m_sizeTrue; } _WTYPES_NS::CSize GetScrollPage() { return m_sizePage * m_fzoom; } _WTYPES_NS::CSize GetScrollLine() { return m_sizeLine * m_fzoom; } _WTYPES_NS::CPoint GetScrollOffset() { return (_WTYPES_NS::CSize)m_ptOffset * m_fzoom; } // Helper coordinate functions _WTYPES_NS::CPoint WndtoTrue(CPoint ptW) { return (_WTYPES_NS::CSize)ptW * GetZoom() + GetScrollOffset(); } void WndtoTrue(LPPOINT aptW, int nPts) // in place coord transformation { for (int i = 0 ; i < nPts ; i++) aptW[i] = WndtoTrue(aptW[i]); } void WndtoTrue(LPRECT prectW) // in place coord transformation { WndtoTrue((LPPOINT)prectW, 2); } _WTYPES_NS::CPoint TruetoWnd(CPoint ptT) { return (ptT - GetScrollOffset()) / GetZoom(); } void TruetoWnd(LPPOINT aptT, int nPts) // in place coord transformation { for (int i = 0 ; i < nPts ; i++) aptT[i] = TruetoWnd(aptT[i]); } void TruetoWnd(LPRECT prectT) // in place coord transformation { TruetoWnd((LPPOINT)prectT, 2); } // Drawing operations : assume adequate setting of data members BOOL Draw(HBITMAP hbm, HDC hdestDC, DWORD dwROP = SRCCOPY) { CDC memDC = CreateCompatibleDC(hdestDC); CBitmapHandle bmpOld = memDC.SelectBitmap(hbm); BOOL bRes = Draw(memDC, hdestDC, dwROP); memDC.SelectBitmap(bmpOld); return bRes; } BOOL Draw(HDC hsourceDC, HDC hdestDC, DWORD dwROP = SRCCOPY) { CDCHandle destDC = hdestDC; destDC.SetViewportOrg(0,0); _WTYPES_NS::CPoint ptOffset = GetScrollOffset(); _WTYPES_NS::CSize sizeZClient = m_sizeClient * GetZoom(); return destDC.StretchBlt(0, 0, m_sizeClient.cx, m_sizeClient.cy, hsourceDC, ptOffset.x, ptOffset.y, sizeZClient.cx, sizeZClient.cy, dwROP); } #ifdef _IMAGING_H BOOL Draw(IImage* pIImage, HDC hdestDC) { CDCHandle destDC = hdestDC; destDC.SetViewportOrg(0,0); return SUCCEEDED(pIImage->Draw(destDC, _WTYPES_NS::CRect(-_WTYPES_NS::CPoint(m_ptOffset), m_sizeAll), NULL)); } #endif // Message map and handlers BEGIN_MSG_MAP(CZoomScrollImpl< T >) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) CHAIN_MSG_MAP(CScrollImpl< T >) END_MSG_MAP() LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled) { T* pT = static_cast(this); ATLASSERT(::IsWindow(pT->m_hWnd)); if ((GetScrollExtendedStyle() & SCRL_ERASEBACKGROUND)) { _WTYPES_NS::CRect rect; pT->GetClientRect(rect); _WTYPES_NS::CSize sizeClient=rect.Size(); if (m_sizeAll.cx < sizeClient.cx || m_sizeAll.cy < sizeClient.cy) { CDCHandle hdc = (HDC)wParam; HBRUSH hbr = GetSysColorBrush((int)T::GetWndClassInfo().m_wc.hbrBackground - 1); if (m_sizeAll.cx < sizeClient.cx) { _WTYPES_NS::CRect rectBG(_WTYPES_NS::CPoint(m_sizeAll.cx, 0), sizeClient); hdc.FillRect(rectBG, hbr); } if (m_sizeAll.cy < sizeClient.cy) { _WTYPES_NS::CRect rectBG(_WTYPES_NS::CPoint(0, m_sizeAll.cy), sizeClient); hdc.FillRect(rectBG, hbr); } } } else { bHandled = FALSE; } return 1; } }; #endif // _WTL_CE_NO_ZOOMSCROLL #ifndef _WTL_CE_NO_CONTROLS // --- PPC bottom TabView control --- #if defined(__ATLCTRLX_H__) && defined(WIN32_PLATFORM_PSPC) /////////////////////////////////////////////////////////////////////////////// // CBottomTabViewImpl template class ATL_NO_VTABLE CBottomTabViewImpl : public CTabViewImpl { public: DECLARE_WND_CLASS_EX(NULL, 0, COLOR_APPWORKSPACE) // Implementation overrideables bool CreateTabControl() { m_tab.Create(m_hWnd, rcDefault, NULL, WS_CHILD | TCS_BOTTOM, 0, m_nTabID); ATLASSERT(m_tab.m_hWnd != NULL); if(m_tab.m_hWnd == NULL) return false; m_tab.SendMessage(CCM_SETVERSION, COMCTL32_VERSION); m_tab.SetItemExtra(sizeof(TABVIEWPAGE)); T* pT = static_cast(this); m_cyTabHeight = pT->CalcTabHeight(); return true; } int CalcTabHeight() { int nCount = m_tab.GetItemCount(); TCITEMEXTRA tcix = { 0 }; tcix.tciheader.mask = TCIF_TEXT; tcix.tciheader.pszText = _T("NS"); int nIndex = m_tab.InsertItem(nCount, tcix); RECT rect = { 0 }; SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0); RECT rcWnd = rect; m_tab.AdjustRect(FALSE, &rect); rcWnd.top = rect.bottom; ::AdjustWindowRectEx(&rcWnd, m_tab.GetStyle(), FALSE, m_tab.GetExStyle()); m_tab.DeleteItem(nIndex); return rcWnd.bottom - rcWnd.top; } void UpdateLayout() { RECT rect = { 0 }; GetClientRect(&rect); if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0)) m_tab.SetWindowPos(NULL, 0, rect.bottom - m_cyTabHeight, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER /*| SWP_SHOWWINDOW*/); if(m_nActivePage != -1) ::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, 0, rect.right - rect.left, rect.bottom - m_cyTabHeight, SWP_NOZORDER); } }; class CBottomTabView : public CBottomTabViewImpl { public: DECLARE_WND_CLASS_EX(_T("WTL_BottomTabView"), 0, COLOR_APPWORKSPACE) }; #endif // defined(__ATLCTRLX_H__) && defined(WIN32_PLATFORM_PSPC) // --- PPC/SmartPhone controls --- //////////////////////////////////////////////////////////////////////////////// // These are wrapper classes for the Pocket PC 2002/2003 and SmartPhone 2003 controls // To implement a window based on a control, use following: // Example: Implementing a window based on a Html control // // class CMyHtml : CWindowImpl // { // public: // BEGIN_MSG_MAP(CMyHtml) // // put your message handler entries here // END_MSG_MAP() // }; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // CHtmlCtrl template class CHtmlCtrlT : public TBase { public: // Constructors CHtmlCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CHtmlCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { HWND hWnd = TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); ATLASSERT(hWnd != NULL); // Did you remember to call InitHTMLControl(hInstance) ?? return hWnd; } // Attributes static LPCTSTR GetWndClassName() { return WC_HTML; } #if (_WIN32_WCE >= 400) void AddStyle(LPCWSTR pszStyle) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_ADDSTYLE, 0, (LPARAM)pszStyle); } #endif // (_WIN32_WCE >= 400) void AddText(BOOL bPlainText, LPCSTR pszText) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_ADDTEXT, (WPARAM)bPlainText, (LPARAM)pszText); } void AddHTML(LPCSTR pszHTML) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_ADDTEXT, (WPARAM)FALSE, (LPARAM)pszHTML); } void AddText(BOOL bPlainText, LPCWSTR pszText) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_ADDTEXTW, (WPARAM)bPlainText, (LPARAM)pszText); } void AddHTML(LPCWSTR pszHTML) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_ADDTEXTW, (WPARAM)FALSE, (LPARAM)pszHTML); } void Anchor(LPCSTR pszAnchor) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_ANCHOR, 0, (LPARAM)pszAnchor); } void Anchor(LPCWSTR pszAnchor) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_ANCHORW, 0, (LPARAM)pszAnchor); } #if (_WIN32_WCE >= 420) void GetBrowserDispatch(IDispatch** ppDispatch) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(ppDispatch); ATLASSERT(*ppDispatch==NULL); ::SendMessage(m_hWnd, DTM_BROWSERDISPATCH, 0, (LPARAM)ppDispatch); } void GetDocumentDispatch(IDispatch** ppDispatch) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(ppDispatch); ATLASSERT(*ppDispatch==NULL); ::SendMessage(m_hWnd, DTM_DOCUMENTDISPATCH , 0, (LPARAM)ppDispatch); } #endif // (_WIN32_WCE >= 420) void Clear() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_CLEAR, 0, 0L); } void EnableClearType(BOOL bEnable = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_ENABLECLEARTYPE, 0, (LPARAM)bEnable); } void EnableContextMenu(BOOL bEnable = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_ENABLECONTEXTMENU, 0, (LPARAM)bEnable); } void EnableScripting(BOOL bEnable = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_ENABLESCRIPTING, 0, (LPARAM)bEnable); } void EnableShrink(BOOL bEnable = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_ENABLESHRINK, 0, (LPARAM)bEnable); } void EndOfSource() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_ENDOFSOURCE, 0, 0L); } void ImageFail(DWORD dwCookie) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_IMAGEFAIL, 0, (LPARAM)dwCookie); } int GetLayoutHeight() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, DTM_LAYOUTHEIGHT, 0, 0L); } int GetLayoutWidth() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, DTM_LAYOUTWIDTH, 0, 0L); } void Navigate(LPCTSTR pstrURL, UINT uFlags = 0) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pstrURL); ::SendMessage(m_hWnd, DTM_NAVIGATE, (WPARAM)uFlags, (LPARAM)pstrURL); } void SelectAll() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_SELECTALL, 0, 0L); } void SetImage(INLINEIMAGEINFO* pImageInfo) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pImageInfo); ::SendMessage(m_hWnd, DTM_SETIMAGE, 0, (LPARAM)pImageInfo); } void ZoomLevel(int iLevel) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_ZOOMLEVEL, 0, (LPARAM)iLevel); } #if (_WIN32_WCE >= 400) void Stop() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DTM_STOP, 0, 0L); } #endif // (_WIN32_WCE >= 400) void GetScriptDispatch(IDispatch** ppDispatch) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(ppDispatch); ATLASSERT(*ppDispatch==NULL); ::SendMessage(m_hWnd, DTM_SCRIPTDISPATCH, 0, (LPARAM)ppDispatch); } }; typedef CHtmlCtrlT CHtmlCtrl; #ifdef WIN32_PLATFORM_PSPC /////////////////////////////////////////////////////////////////////////////// // CRichInkCtrl template class CRichInkCtrlT : public TBase { public: // Constructors CRichInkCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CRichInkCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { HWND hWnd = TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); ATLASSERT(hWnd != NULL); // Did you remember to call InitRichInkDLL() ?? return hWnd; } // Attributes static LPCTSTR GetWndClassName() { return WC_RICHINK; } BOOL CanPaste(UINT uFormat = 0) const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_CANPASTE, (WPARAM)uFormat, 0L); } BOOL CanRedo() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_CANREDO, 0, 0L); } BOOL CanUndo() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_CANUNDO, 0, 0L); } void ClearAll(BOOL bRepaint = TRUE) const { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_CLEARALL, (WPARAM)bRepaint, 0L); } BOOL GetModify() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, EM_GETMODIFY, 0, 0L); } UINT GetPageStyle() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, EM_GETPAGESTYLE, 0, 0L); } UINT GetPenMode() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, EM_GETPENMODE, 0, 0L); } UINT GetViewStyle() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, EM_GETVIEW, 0, 0L); } UINT GetWrapMode() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, EM_GETWRAPMODE, 0, 0L); } UINT GetZoomPercent() const { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, EM_GETZOOMPERCENT, 0, 0L); } void InsertLinks(LPWSTR lpString, int cchLength = -1) { ATLASSERT(::IsWindow(m_hWnd)); if(cchLength == -1) cchLength = lstrlen(lpString); ::SendMessage(m_hWnd, EM_INSERTLINKS, (WPARAM)cchLength, (LPARAM)lpString); } void RedoEvent() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_REDOEVENT, 0, 0L); } UINT SetInkLayer(UINT uLayer) { ATLASSERT(::IsWindow(m_hWnd)); return (UINT)::SendMessage(m_hWnd, EM_SETINKLAYER, (WPARAM)uLayer, 0L); } void SetPageStyle(UINT uStyle) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETPAGESTYLE, (WPARAM)uStyle, 0L); } void SetPenMode(UINT uMode) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETPENMODE, (WPARAM)uMode, 0L); } void SetViewStyle(UINT uStyle) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETVIEW, (WPARAM)uStyle, 0L); } void SetViewAttributes(VIEWATTRIBUTES* pAttribs) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pAttribs); ::SendMessage(m_hWnd, EM_SETVIEWATTRIBUTES, 0, (LPARAM)pAttribs); } void SetWrapMode(UINT uMode) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETWRAPMODE, (WPARAM)uMode, 0L); } void SetZoomPercent(UINT uPercent) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETZOOMPERCENT, (WPARAM)uPercent, 0L); } LONG StreamIn(UINT uFormat, EDITSTREAM& es) { ATLASSERT(::IsWindow(m_hWnd)); return (LONG)::SendMessage(m_hWnd, EM_STREAMIN, (WPARAM)uFormat, (LPARAM)&es); } LONG StreamOut(UINT uFormat, EDITSTREAM& es) { ATLASSERT(::IsWindow(m_hWnd)); return (LONG)::SendMessage(m_hWnd, EM_STREAMOUT, (WPARAM)uFormat, (LPARAM)&es); } void UndoEvent() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_UNDOEVENT, 0, 0L); } void Undo() { UndoEvent(); } // Standard EM_xxx messages DWORD GetSel() const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetViewStyle() != VT_DRAWINGVIEW); return (DWORD)::SendMessage(m_hWnd, EM_GETSEL, 0, 0L); } void GetSel(int& nStartChar, int& nEndChar) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetViewStyle() != VT_DRAWINGVIEW); ::SendMessage(m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar); } void SetSel(int nStartChar, int nEndChar) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetViewStyle() != VT_DRAWINGVIEW); ::SendMessage(m_hWnd, EM_SETSEL, nStartChar, nEndChar); } void ReplaceSel(LPCTSTR lpszNewText, BOOL bCanUndo = FALSE) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(GetViewStyle() != VT_DRAWINGVIEW); ::SendMessage(m_hWnd, EM_REPLACESEL, (WPARAM)bCanUndo, (LPARAM)lpszNewText); } void SetModify(BOOL bModified = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, EM_SETMODIFY, (WPARAM)bModified, 0L); } int GetTextLength() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, WM_GETTEXTLENGTH, 0, 0L); } // Clipboard operations void Clear() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_CLEAR, 0, 0L); } void Copy() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_COPY, 0, 0L); } void Cut() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_CUT, 0, 0L); } void Paste() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, WM_PASTE, 0, 0L); } }; typedef CRichInkCtrlT CRichInkCtrl; /////////////////////////////////////////////////////////////////////////////// // CInkXCtrl template class CInkXCtrlT : public TBase { public: // Constructors CInkXCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CInkXCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { HWND hWnd = TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); ATLASSERT(hWnd != NULL); // Did you remember to call InitInkX() ?? return hWnd; } // Attributes static LPCTSTR GetWndClassName() { return WC_INKX; } static UINT GetHotRecordingMessage() { return ::RegisterWindowMessage(szHotRecording); } void ClearAll() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, IM_CLEARALL, 0, 0L); } int GetData(BYTE* lpBuffer, INT cbBuffer) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(lpBuffer); return (int)::SendMessage(m_hWnd, IM_GETDATA, (WPARAM)cbBuffer, (LPARAM)lpBuffer); } int GetDataLen() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, IM_GETDATALEN, 0, 0L); } CRichInkCtrl GetRichInk() const { ATLASSERT(::IsWindow(m_hWnd)); return (HWND)::SendMessage(m_hWnd, IM_GETRICHINK, 0, 0L); } BOOL IsRecording() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, IM_RECORDING, 0, 0L); } void ReInit() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, IM_REINIT, 0, 0L); } void SetData(const BYTE* lpInkData, INT cbInkData) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(lpInkData); ::SendMessage(m_hWnd, IM_SETDATA, (WPARAM)cbInkData, (LPARAM)lpInkData); } void VoicePlay() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, IM_VOICE_PLAY, 0, 0L); } BOOL IsVoicePlaying() const { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, IM_VOICE_PLAYING, 0, 0L); } BOOL VoiceRecord() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, IM_VOICE_RECORD, 0, 0L); } void VoiceStop() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, IM_VOICE_STOP, 0, 0L); } void ShowVoiceBar(BOOL bShow = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, IM_VOICEBAR, (WPARAM)bShow, 0L); } }; typedef CInkXCtrlT CInkXCtrl; #endif // WIN32_PLATFORM_PSPC /////////////////////////////////////////////////////////////////////////////// // CVoiceRecorderCtrl template class CVoiceRecorderCtrlT : public TBase { public: // Constructors CVoiceRecorderCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CVoiceRecorderCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, const POINT pt, LPTSTR pstrFileName, UINT nID, DWORD dwStyle = 0) { ATLASSERT(pstrFileName != NULL); CM_VOICE_RECORDER cmvr = { 0 }; cmvr.cb = sizeof(CM_VOICE_RECORDER); cmvr.dwStyle = dwStyle; cmvr.xPos = pt.x; cmvr.yPos = pt.y; cmvr.hwndParent = hWndParent; cmvr.id = nID; cmvr.lpszRecordFileName = pstrFileName; m_hWnd = VoiceRecorder_Create(&cmvr); return m_hWnd; } HWND Create(LPCM_VOICE_RECORDER pAttribs) { ATLASSERT(pAttribs); m_hWnd = VoiceRecorder_Create(pAttribs); return m_hWnd; } // Attributes void Record() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, VRM_RECORD, 0, 0L); } void Play() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, VRM_PLAY, 0, 0L); } void Stop() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, VRM_STOP, 0, 0L); } void Cancel() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, VRM_CANCEL, 0, 0L); } void Done() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, VRM_OK, 0, 0L); } }; typedef CVoiceRecorderCtrlT CVoiceRecorderCtrl; #ifdef WIN32_PLATFORM_PSPC /////////////////////////////////////////////////////////////////////////////// // CDocListCtrl template class CDocListCtrlT : public TBase { public: // Attributes DOCLISTCREATE m_dlc; TCHAR m_szPath[MAX_PATH]; // Constructors CDocListCtrlT(HWND hWnd = NULL) : TBase(hWnd) { } CDocListCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, WORD wId, LPCTSTR pszFolder = NULL, LPCTSTR pstrFilter = NULL, WORD wFilterIndex = 0, DWORD dwFlags = DLF_SHOWEXTENSION) { ATLASSERT(pstrFilter != NULL); // It seems to need a filter badly!! ::ZeroMemory(&m_dlc, sizeof(DOCLISTCREATE)); ::ZeroMemory(m_szPath, sizeof(m_szPath)); if(pszFolder != NULL) SecureHelper::strncpy_x(m_szPath, MAX_PATH, pszFolder, MAX_PATH - 1); m_dlc.dwStructSize = sizeof(DOCLISTCREATE); m_dlc.hwndParent = hWndParent; m_dlc.pszFolder = m_szPath; m_dlc.pstrFilter = pstrFilter; m_dlc.wFilterIndex = wFilterIndex; m_dlc.wId = wId; m_dlc.dwFlags = dwFlags; m_hWnd = DocList_Create(&m_dlc); return m_hWnd; } HWND Create(DOCLISTCREATE* pDlc) { m_dlc = *pDlc; m_hWnd = DocList_Create(&m_dlc); return m_hWnd; } // Attributes void DeleteSel() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DLM_DELETESEL, 0, 0L); } void DisableUpdates() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DLM_DISABLEUPDATES, 0, 0L); } void EnableUpdates() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DLM_ENABLEUPDATES, 0, 0L); } int GetFilterIndex() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, DLM_GETFILTERINDEX, 0, 0L); } int GetItemCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, DLM_GETITEMCOUNT, 0, 0L); } int GetNextItem(int iIndex, DWORD dwRelation = LVNI_ALL) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, DLM_GETNEXTITEM, (WPARAM)iIndex, (LPARAM)dwRelation); } int GetFirstItem(DWORD dwRelation = LVNI_ALL) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, DLM_GETNEXTITEM, (WPARAM)-1, (LPARAM)dwRelation); } BOOL GetNextWave(int* pIndex) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pIndex); return (BOOL)::SendMessage(m_hWnd, DLM_GETNEXTWAVE, 0, (LPARAM)pIndex); } BOOL GetPrevWave(int* pIndex) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pIndex); return (BOOL)::SendMessage(m_hWnd, DLM_GETPREVWAVE, 0, (LPARAM)pIndex); } int GetSelCount() const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, DLM_GETSELCOUNT, 0, 0L); } BOOL GetSelPathName(LPTSTR pstrPath, int cchMax) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pstrPath); return (BOOL)::SendMessage(m_hWnd, DLM_GETSELPATHNAME, (WPARAM)cchMax, (LPARAM)pstrPath); } void ReceiveIR(LPCTSTR pstrPath) const { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pstrPath); ::SendMessage(m_hWnd, DLM_RECEIVEIR, 0, (LPARAM)pstrPath); } void Refresh() { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DLM_REFRESH, 0, 0L); } BOOL RenameMoveSelectedItems() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, DLM_RENAMEMOVE, 0, 0L); } int SelectAll() { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, DLM_SELECTALL, 0, 0L); } HRESULT SelectItem(LPCTSTR pstrPath, BOOL bVisible = TRUE) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pstrPath); return (HRESULT)::SendMessage(m_hWnd, DLM_SELECTITEM, (WPARAM)bVisible, (LPARAM)pstrPath); } void SendEMail(LPCTSTR pstrAttachment) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DLM_SENDEMAIL, 0, (LPARAM)pstrAttachment); } void SendIR(LPCTSTR pstrPath) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DLM_SENDIR, 0, (LPARAM)pstrPath); } HRESULT SetFilterIndex(int iIndex) { ATLASSERT(::IsWindow(m_hWnd)); return (HRESULT)::SendMessage(m_hWnd, DLM_SETFILTERINDEX, (WPARAM)iIndex, 0L); } void SetFolder(LPCTSTR pstrPath) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pstrPath); ::SendMessage(m_hWnd, DLM_SETFOLDER, 0, (LPARAM)pstrPath); } BOOL SetItemState(int iIndex, const LVITEM* pItem) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pItem); return (BOOL)::SendMessage(m_hWnd, DLM_SETITEMSTATE, (WPARAM)iIndex, (LPARAM)pItem); } BOOL SetItemState(int iIndex, UINT uState, UINT uMask) { ATLASSERT(::IsWindow(m_hWnd)); LVITEM lvi = { 0 }; lvi.stateMask = uMask; lvi.state = uState; return (BOOL)::SendMessage(m_hWnd, DLM_SETITEMSTATE, (WPARAM)iIndex, (LPARAM)&lvi); } void SetOneItem(int iIndex, LPCVOID pPA) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DLM_SETONEITEM, (WPARAM)iIndex, (LPARAM)pPA); } void SetSelect(int iIndex) { ATLASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, DLM_SETSELECT, (WPARAM)iIndex, 0L); } void SetSelPathName(LPCTSTR pstrPath) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pstrPath); ::SendMessage(m_hWnd, DLM_SETSELPATHNAME, 0, (LPARAM)pstrPath); } BOOL SetSortOrder() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, DLM_SETSORTORDER, 0, 0L); } HRESULT Update() { ATLASSERT(::IsWindow(m_hWnd)); return (HRESULT)::SendMessage(m_hWnd, DLM_UPDATE, 0, 0L); } BOOL ValidateFolder() { ATLASSERT(::IsWindow(m_hWnd)); return (BOOL)::SendMessage(m_hWnd, DLM_VALIDATEFOLDER, 0, 0L); } // Functions BOOL GetFirstSelectedWaveFile(int* pIndex, LPTSTR szPath, const size_t cchPath) { ATLASSERT(::IsWindow(m_hWnd)); return DocList_GetFirstSelectedWaveFile(m_hWnd, pIndex, szPath, cchPath); } BOOL GetNextSelectedWaveFile(int* pIndex, LPTSTR szPath, const size_t cchPath) { ATLASSERT(::IsWindow(m_hWnd)); return DocList_GetNextSelectedWaveFile(m_hWnd, pIndex, szPath, cchPath); } }; typedef CDocListCtrlT CDocListCtrl; #endif // WIN32_PLATFORM_PSPC /////////////////////////////////////////////////////////////////////////////// // CCapEdit template class CCapEditT : public TBase { public: // Constructors CCapEditT(HWND hWnd = NULL) : TBase(hWnd) { } CCapEditT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { HWND hWnd = /*TBase*/CWindow::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); ATLASSERT(hWnd != NULL); // Did you remember to call SHInitExtraControls() ?? return hWnd; } // Attributes static LPCTSTR GetWndClassName() { return WC_CAPEDIT; } }; typedef CCapEditT CCapEdit; /////////////////////////////////////////////////////////////////////////////// // CTTStatic #ifndef WIN32_PLATFORM_WFSP // Tooltips not supported on SmartPhone template class CTTStaticT : public TBase { public: // Constructors CTTStaticT(HWND hWnd = NULL) : TBase(hWnd) { } CTTStaticT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { HWND hWnd = TBase::Create(hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); ATLASSERT(hWnd != NULL); // Did you remember to call SHInitExtraControls() ?? return hWnd; } // Attributes static LPCTSTR GetWndClassName() { return WC_TSTATIC; } // Operations BOOL SetToolTipText(LPCTSTR pstrTipText) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pstrTipText); ATLASSERT(lstrlen(pstrTipText) <= 253); CTempBuffer buff; int cchLen = lstrlen(pstrTipText) + 3; LPTSTR pstr = buff.Allocate(cchLen); if(pstr == NULL) return FALSE; SecureHelper::strcpy_x(pstr, cchLen, _T("~~")); SecureHelper::strcat_x(pstr, cchLen, pstrTipText); return SetWindowText(pstr); } }; typedef CTTStaticT CTTStatic; /////////////////////////////////////////////////////////////////////////////// // CTTButton template class CTTButtonT : public TBase { public: // Constructors CTTButtonT(HWND hWnd = NULL) : TBase(hWnd) { } CTTButtonT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { HWND hWnd = TBase::Create(hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam); ATLASSERT(hWnd != NULL); // Did you remember to call SHInitExtraControls() ?? return hWnd; } // Attributes static LPCTSTR GetWndClassName() { return WC_TBUTTON; } // Operations BOOL SetToolTipText(LPCTSTR pstrTipText) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(pstrTipText); ATLASSERT(lstrlen(pstrTipText) <= 253); CTempBuffer buff; int cchLen = lstrlen(pstrTipText) + 3; LPTSTR pstr = buff.Allocate(cchLen); if(pstr == NULL) return FALSE; SecureHelper::strcpy_x(pstr, cchLen, _T("~~")); SecureHelper::strcat_x(pstr, cchLen, pstrTipText); return SetWindowText(pstr); } }; typedef CTTButtonT CTTButton; #endif // !WIN32_PLATFORM_WFSP // --- SmartPhone specific controls --- #ifdef WIN32_PLATFORM_WFSP /////////////////////////////////////////////////////////////////////////////// // CSpinCtrlT - CSpinCtrl : SmartPhone adapted UpDown control template class CSpinCtrlT : public CUpDownCtrlT< TBase > { public: // Constructors CSpinCtrlT(HWND hWnd = NULL) : CUpDownCtrlT< TBase >(hWnd) { } CSpinCtrlT< TBase >& operator =(HWND hWnd) { m_hWnd = hWnd; return *this; } HWND Create(HWND hWndParent, HWND hBuddy, DWORD dwStyle, int nID, LPCTSTR szExpandedName = NULL) { ATLASSERT(::IsWindow(hWndParent)); CUpDownCtrlT< TBase >::Create(hWndParent, NULL, szExpandedName, dwStyle, 0, nID, NULL); ATLASSERT(m_hWnd != NULL); // Did you remember to call AtlInitCommonControls(ICC_UPDOWN_CLASS)? if (hBuddy != NULL) { ATLASSERT(::IsWindow(hBuddy)); SetBuddy(hBuddy); } return m_hWnd; } }; typedef CSpinCtrlT CSpinCtrl; /////////////////////////////////////////////////////////////////////////////// // CSpinned - SmartPhone association of control and Spin template class CSpinned : public TBase { public: CSpinCtrl m_SpinCtrl; DWORD m_dwSpinnedStyle; // Constructors CSpinned(HWND hWnd = NULL) : TBase(hWnd) { m_dwSpinnedStyle = WS_VISIBLE | UDS_ALIGNRIGHT | UDS_EXPANDABLE; if (t_bExpandOnly == true) m_dwSpinnedStyle |= UDS_NOSCROLL; else m_dwSpinnedStyle |= UDS_HORZ | UDS_ARROWKEYS | UDS_SETBUDDYINT | UDS_WRAP; if (hWnd != NULL) AttachOrCreateSpinCtrl(); } CSpinned& operator =(HWND hWnd) { Attach(hWnd); return *this; } void Attach(HWND hWnd) { ATLASSERT(!IsWindow()); TBase* pT = static_cast(this); pT->m_hWnd = hWnd; if (hWnd != NULL) AttachOrCreateSpinCtrl(); } HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szExpandedName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) { TBase* pT = static_cast(this); TBase::Create(hWndParent, rect, NULL, dwStyle, dwExStyle, MenuOrID, lpCreateParam); ATLASSERT(pT->m_hWnd != NULL); m_SpinCtrl.Create(hWndParent, pT->m_hWnd, m_dwSpinnedStyle, ATL_IDW_SPIN_ID + (int)MenuOrID.m_hMenu, szExpandedName); ATLASSERT(m_SpinCtrl.m_hWnd != NULL); // Did you remember to call AtlInitCommonControls(ICC_UPDOWN_CLASS)? return pT->m_hWnd; } // Attributes CSpinCtrl& GetSpinCtrl() { return m_SpinCtrl; } // Implementation // Attach our existing SpinCtrl or create one bool AttachOrCreateSpinCtrl() { TBase* pT = static_cast(this); HWND hSpin = ::GetDlgItem(pT->GetParent(), ATL_IDW_SPIN_ID + pT->GetDlgCtrlID()); if (hSpin != NULL) { m_SpinCtrl.Attach(hSpin); #ifdef DEBUG TCHAR sClassName[16] = { 0 }; ::GetClassName(hSpin, sClassName, 16); ATLASSERT(!_tcscmp(sClassName, UPDOWN_CLASS)); ATLASSERT(m_SpinCtrl.GetBuddy().m_hWnd == pT->m_hWnd); #endif // DEBUG } else { m_SpinCtrl.Create(pT->GetParent(), pT->m_hWnd, m_dwSpinnedStyle, ATL_IDW_SPIN_ID + pT->GetDlgCtrlID()); } return m_SpinCtrl.m_hWnd != NULL; } }; /////////////////////////////////////////////////////////////////////////////// // CSpinListBox - SmartPhone spinned ListBox control // CExpandListBox - SmartPhone expandable ListBox control // CExpandEdit - SmartPhone expandable Edit control // CExpandCapEdit - SmartPhone expandable CapEdit control typedef CSpinned CSpinListBox; typedef CSpinned CExpandListBox; typedef CSpinned CExpandEdit; typedef CSpinned CExpandCapEdit; #endif // WIN32_PLATFORM_WFSP #endif // _WTL_CE_NO_CONTROLS }; // namespace WTL #endif // __ATLWINCE_H__ ================================================ FILE: src/Setup/wtl90/atlwinx.h ================================================ // Windows Template Library - WTL version 9.0 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. // // This file is a part of the Windows Template Library. // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLWINX_H__ #define __ATLWINX_H__ #pragma once #ifndef __ATLAPP_H__ #error atlwinx.h requires atlapp.h to be included first #endif #if (_ATL_VER >= 0x0700) #include #endif // (_ATL_VER >= 0x0700) /////////////////////////////////////////////////////////////////////////////// // Classes in this file: // // _U_RECT // _U_MENUorID // _U_STRINGorID /////////////////////////////////////////////////////////////////////////////// // Command Chaining Macros #define CHAIN_COMMANDS(theChainClass) \ if(uMsg == WM_COMMAND) \ CHAIN_MSG_MAP(theChainClass) #define CHAIN_COMMANDS_ALT(theChainClass, msgMapID) \ if(uMsg == WM_COMMAND) \ CHAIN_MSG_MAP_ALT(theChainClass, msgMapID) #define CHAIN_COMMANDS_MEMBER(theChainMember) \ if(uMsg == WM_COMMAND) \ CHAIN_MSG_MAP_MEMBER(theChainMember) #define CHAIN_COMMANDS_ALT_MEMBER(theChainMember, msgMapID) \ if(uMsg == WM_COMMAND) \ CHAIN_MSG_MAP_ALT_MEMBER(theChainMember, msgMapID) /////////////////////////////////////////////////////////////////////////////// // Macros for parent message map to selectively reflect control messages // NOTE: ReflectNotifications is a member of ATL's CWindowImplRoot // (and overridden in 2 cases - CContainedWindowT and CAxHostWindow) // Since we can't modify ATL, we'll provide the needed additions // in a separate function (that is not a member of CWindowImplRoot) namespace WTL { inline LRESULT WtlReflectNotificationsFiltered(HWND hWndParent, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled, UINT uMsgFilter = WM_NULL, UINT_PTR idFromFilter = 0, HWND hWndChildFilter = NULL) { if((uMsgFilter != WM_NULL) && (uMsgFilter != uMsg)) { // The notification message doesn't match the filter. bHandled = FALSE; return 1; } HWND hWndChild = NULL; UINT_PTR idFrom = 0; switch(uMsg) { case WM_COMMAND: if(lParam != NULL) // not from a menu { hWndChild = (HWND)lParam; idFrom = (UINT_PTR)LOWORD(wParam); } break; case WM_NOTIFY: hWndChild = ((LPNMHDR)lParam)->hwndFrom; idFrom = ((LPNMHDR)lParam)->idFrom; break; #ifndef _WIN32_WCE case WM_PARENTNOTIFY: switch(LOWORD(wParam)) { case WM_CREATE: case WM_DESTROY: hWndChild = (HWND)lParam; idFrom = (UINT_PTR)HIWORD(wParam); break; default: hWndChild = ::GetDlgItem(hWndParent, HIWORD(wParam)); idFrom = (UINT_PTR)::GetDlgCtrlID(hWndChild); break; } break; #endif // !_WIN32_WCE case WM_DRAWITEM: if(wParam) // not from a menu { hWndChild = ((LPDRAWITEMSTRUCT)lParam)->hwndItem; idFrom = (UINT_PTR)wParam; } break; case WM_MEASUREITEM: if(wParam) // not from a menu { hWndChild = ::GetDlgItem(hWndParent, ((LPMEASUREITEMSTRUCT)lParam)->CtlID); idFrom = (UINT_PTR)wParam; } break; case WM_COMPAREITEM: if(wParam) // not from a menu { hWndChild = ((LPCOMPAREITEMSTRUCT)lParam)->hwndItem; idFrom = (UINT_PTR)wParam; } break; case WM_DELETEITEM: if(wParam) // not from a menu { hWndChild = ((LPDELETEITEMSTRUCT)lParam)->hwndItem; idFrom = (UINT_PTR)wParam; } break; case WM_VKEYTOITEM: case WM_CHARTOITEM: case WM_HSCROLL: case WM_VSCROLL: hWndChild = (HWND)lParam; idFrom = (UINT_PTR)::GetDlgCtrlID(hWndChild); break; case WM_CTLCOLORBTN: case WM_CTLCOLORDLG: case WM_CTLCOLOREDIT: case WM_CTLCOLORLISTBOX: case WM_CTLCOLORMSGBOX: case WM_CTLCOLORSCROLLBAR: case WM_CTLCOLORSTATIC: hWndChild = (HWND)lParam; idFrom = (UINT_PTR)::GetDlgCtrlID(hWndChild); break; default: break; } if((hWndChild == NULL) || ((hWndChildFilter != NULL) && (hWndChildFilter != hWndChild))) { // Either hWndChild isn't valid, or // hWndChild doesn't match the filter. bHandled = FALSE; return 1; } if((idFromFilter != 0) && (idFromFilter != idFrom)) { // The dialog control id doesn't match the filter. bHandled = FALSE; return 1; } ATLASSERT(::IsWindow(hWndChild)); LRESULT lResult = ::SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam); if((lResult == 0) && (uMsg >= WM_CTLCOLORMSGBOX) && (uMsg <= WM_CTLCOLORSTATIC)) { // Try to prevent problems with WM_CTLCOLOR* messages when // the message wasn't really handled bHandled = FALSE; } return lResult; } }; // namespace WTL // Try to prevent problems with WM_CTLCOLOR* messages when // the message wasn't really handled #define REFLECT_NOTIFICATIONS_EX() \ { \ bHandled = TRUE; \ lResult = ReflectNotifications(uMsg, wParam, lParam, bHandled); \ if((lResult == 0) && (uMsg >= WM_CTLCOLORMSGBOX) && (uMsg <= WM_CTLCOLORSTATIC)) \ bHandled = FALSE; \ if(bHandled) \ return TRUE; \ } #define REFLECT_NOTIFICATIONS_MSG_FILTERED(uMsgFilter) \ { \ bHandled = TRUE; \ lResult = WTL::WtlReflectNotificationsFiltered(m_hWnd, uMsg, wParam, lParam, bHandled, uMsgFilter, 0, NULL); \ if(bHandled) \ return TRUE; \ } #define REFLECT_NOTIFICATIONS_ID_FILTERED(idFromFilter) \ { \ bHandled = TRUE; \ lResult = WTL::WtlReflectNotificationsFiltered(m_hWnd, uMsg, wParam, lParam, bHandled, WM_NULL, idFromFilter, NULL); \ if(bHandled) \ return TRUE; \ } #define REFLECT_NOTIFICATIONS_HWND_FILTERED(hWndChildFilter) \ { \ bHandled = TRUE; \ lResult = WTL::WtlReflectNotificationsFiltered(m_hWnd, uMsg, wParam, lParam, bHandled, WM_NULL, 0, hWndChildFilter); \ if(bHandled) \ return TRUE; \ } #define REFLECT_NOTIFICATIONS_MSG_ID_FILTERED(uMsgFilter, idFromFilter) \ { \ bHandled = TRUE; \ lResult = WTL::WtlReflectNotificationsFiltered(m_hWnd, uMsg, wParam, lParam, bHandled, uMsgFilter, idFromFilter, NULL); \ if(bHandled) \ return TRUE; \ } #define REFLECT_NOTIFICATIONS_MSG_HWND_FILTERED(uMsgFilter, hWndChildFilter) \ { \ bHandled = TRUE; \ lResult = WTL::WtlReflectNotificationsFiltered(m_hWnd, uMsg, wParam, lParam, bHandled, uMsgFilter, 0, hWndChildFilter); \ if(bHandled) \ return TRUE; \ } #define REFLECT_COMMAND(id, code) \ if(uMsg == WM_COMMAND && id == LOWORD(wParam) && code == HIWORD(wParam)) \ { \ bHandled = TRUE; \ lResult = ReflectNotifications(uMsg, wParam, lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECT_COMMAND_ID(id) \ if(uMsg == WM_COMMAND && id == LOWORD(wParam)) \ { \ bHandled = TRUE; \ lResult = ReflectNotifications(uMsg, wParam, lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECT_COMMAND_CODE(code) \ if(uMsg == WM_COMMAND && code == HIWORD(wParam)) \ { \ bHandled = TRUE; \ lResult = ReflectNotifications(uMsg, wParam, lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECT_COMMAND_RANGE(idFirst, idLast) \ if(uMsg == WM_COMMAND && LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) \ { \ bHandled = TRUE; \ lResult = ReflectNotifications(uMsg, wParam, lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECT_COMMAND_RANGE_CODE(idFirst, idLast, code) \ if(uMsg == WM_COMMAND && code == HIWORD(wParam) && LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) \ { \ bHandled = TRUE; \ lResult = ReflectNotifications(uMsg, wParam, lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECT_NOTIFY(id, cd) \ if(uMsg == WM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom && cd == ((LPNMHDR)lParam)->code) \ { \ bHandled = TRUE; \ lResult = ReflectNotifications(uMsg, wParam, lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECT_NOTIFY_ID(id) \ if(uMsg == WM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom) \ { \ bHandled = TRUE; \ lResult = ReflectNotifications(uMsg, wParam, lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECT_NOTIFY_CODE(cd) \ if(uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code) \ { \ bHandled = TRUE; \ lResult = ReflectNotifications(uMsg, wParam, lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECT_NOTIFY_RANGE(idFirst, idLast) \ if(uMsg == WM_NOTIFY && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) \ { \ bHandled = TRUE; \ lResult = ReflectNotifications(uMsg, wParam, lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECT_NOTIFY_RANGE_CODE(idFirst, idLast, cd) \ if(uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) \ { \ bHandled = TRUE; \ lResult = ReflectNotifications(uMsg, wParam, lParam, bHandled); \ if(bHandled) \ return TRUE; \ } /////////////////////////////////////////////////////////////////////////////// // Reflected message handler macros for message maps (for ATL 3.0) #if (_ATL_VER < 0x0700) #define REFLECTED_COMMAND_HANDLER(id, code, func) \ if(uMsg == OCM_COMMAND && id == LOWORD(wParam) && code == HIWORD(wParam)) \ { \ bHandled = TRUE; \ lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECTED_COMMAND_ID_HANDLER(id, func) \ if(uMsg == OCM_COMMAND && id == LOWORD(wParam)) \ { \ bHandled = TRUE; \ lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECTED_COMMAND_CODE_HANDLER(code, func) \ if(uMsg == OCM_COMMAND && code == HIWORD(wParam)) \ { \ bHandled = TRUE; \ lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECTED_COMMAND_RANGE_HANDLER(idFirst, idLast, func) \ if(uMsg == OCM_COMMAND && LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) \ { \ bHandled = TRUE; \ lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECTED_COMMAND_RANGE_CODE_HANDLER(idFirst, idLast, code, func) \ if(uMsg == OCM_COMMAND && code == HIWORD(wParam) && LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) \ { \ bHandled = TRUE; \ lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECTED_NOTIFY_HANDLER(id, cd, func) \ if(uMsg == OCM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom && cd == ((LPNMHDR)lParam)->code) \ { \ bHandled = TRUE; \ lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECTED_NOTIFY_ID_HANDLER(id, func) \ if(uMsg == OCM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom) \ { \ bHandled = TRUE; \ lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECTED_NOTIFY_CODE_HANDLER(cd, func) \ if(uMsg == OCM_NOTIFY && cd == ((LPNMHDR)lParam)->code) \ { \ bHandled = TRUE; \ lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECTED_NOTIFY_RANGE_HANDLER(idFirst, idLast, func) \ if(uMsg == OCM_NOTIFY && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) \ { \ bHandled = TRUE; \ lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define REFLECTED_NOTIFY_RANGE_CODE_HANDLER(idFirst, idLast, cd, func) \ if(uMsg == OCM_NOTIFY && cd == ((LPNMHDR)lParam)->code && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) \ { \ bHandled = TRUE; \ lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #endif // (_ATL_VER < 0x0700) /////////////////////////////////////////////////////////////////////////////// // Dual argument helper classes (for ATL 3.0) #if (_ATL_VER < 0x0700) namespace ATL { class _U_RECT { public: _U_RECT(LPRECT lpRect) : m_lpRect(lpRect) { } _U_RECT(RECT& rc) : m_lpRect(&rc) { } LPRECT m_lpRect; }; class _U_MENUorID { public: _U_MENUorID(HMENU hMenu) : m_hMenu(hMenu) { } _U_MENUorID(UINT nID) : m_hMenu((HMENU)LongToHandle(nID)) { } HMENU m_hMenu; }; class _U_STRINGorID { public: _U_STRINGorID(LPCTSTR lpString) : m_lpstr(lpString) { } _U_STRINGorID(UINT nID) : m_lpstr(MAKEINTRESOURCE(nID)) { } LPCTSTR m_lpstr; }; }; // namespace ATL #endif // (_ATL_VER < 0x0700) namespace WTL { /////////////////////////////////////////////////////////////////////////////// // Forward notifications support for message maps (for ATL 3.0) #if (_ATL_VER < 0x0700) // forward notifications support #define FORWARD_NOTIFICATIONS() \ { \ bHandled = TRUE; \ lResult = WTL::Atl3ForwardNotifications(m_hWnd, uMsg, wParam, lParam, bHandled); \ if(bHandled) \ return TRUE; \ } static LRESULT Atl3ForwardNotifications(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { LRESULT lResult = 0; switch(uMsg) { case WM_COMMAND: case WM_NOTIFY: #ifndef _WIN32_WCE case WM_PARENTNOTIFY: #endif // !_WIN32_WCE case WM_DRAWITEM: case WM_MEASUREITEM: case WM_COMPAREITEM: case WM_DELETEITEM: case WM_VKEYTOITEM: case WM_CHARTOITEM: case WM_HSCROLL: case WM_VSCROLL: case WM_CTLCOLORBTN: case WM_CTLCOLORDLG: case WM_CTLCOLOREDIT: case WM_CTLCOLORLISTBOX: case WM_CTLCOLORMSGBOX: case WM_CTLCOLORSCROLLBAR: case WM_CTLCOLORSTATIC: lResult = ::SendMessage(::GetParent(hWnd), uMsg, wParam, lParam); break; default: bHandled = FALSE; break; } return lResult; } #endif // (_ATL_VER < 0x0700) }; // namespace WTL #endif // __ATLWINX_H__ ================================================ FILE: src/Squirrel/ApplyReleasesProgress.cs ================================================ namespace Squirrel { using System; internal class ApplyReleasesProgress : Progress { private readonly int _releasesToApply; private int _appliedReleases; private int _currentReleaseProgress; public ApplyReleasesProgress(int releasesToApply, Action handler) : base(handler) { _releasesToApply = releasesToApply; } public void ReportReleaseProgress(int progressOfCurrentRelease) { _currentReleaseProgress = progressOfCurrentRelease; CalculateProgress(); } public void FinishRelease() { _appliedReleases++; _currentReleaseProgress = 0; CalculateProgress(); } private void CalculateProgress() { // Per release progress var perReleaseProgressRange = 100 / _releasesToApply; var appliedReleases = Math.Min(_appliedReleases, _releasesToApply); var basePercentage = appliedReleases * perReleaseProgressRange; var currentReleasePercentage = (perReleaseProgressRange / 100d) * _currentReleaseProgress; var percentage = basePercentage + currentReleasePercentage; OnReport((int)percentage); } } } ================================================ FILE: src/Squirrel/BinaryPatchUtility.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Threading; using SharpCompress.Compressors; using SharpCompress.Compressors.BZip2; // Adapted from https://github.com/LogosBible/bsdiff.net/blob/master/src/bsdiff/BinaryPatchUtility.cs namespace Squirrel.Bsdiff { /* The original bsdiff.c source code (http://www.daemonology.net/bsdiff/) is distributed under the following license: Copyright 2003-2005 Colin Percival All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted providing that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ class BinaryPatchUtility { /// /// Creates a binary patch (in bsdiff format) that can be used /// (by ) to transform into . /// /// The original binary data. /// The new binary data. /// A to which the patch will be written. public static void Create(byte[] oldData, byte[] newData, Stream output) { // NB: If you diff a file big enough, we blow the stack. This doesn't // solve it, just buys us more space. The solution is to rewrite Split // using iteration instead of recursion, but that's Hard(tm). var ex = default(Exception); var t = new Thread(() => { try { CreateInternal(oldData, newData, output); } catch (Exception exc) { ex = exc; } }, 40 * 1048576); t.Start(); t.Join(); if (ex != null) throw ex; } static void CreateInternal(byte[] oldData, byte[] newData, Stream output) { // check arguments if (oldData == null) throw new ArgumentNullException("oldData"); if (newData == null) throw new ArgumentNullException("newData"); if (output == null) throw new ArgumentNullException("output"); if (!output.CanSeek) throw new ArgumentException("Output stream must be seekable.", "output"); if (!output.CanWrite) throw new ArgumentException("Output stream must be writable.", "output"); /* Header is 0 8 "BSDIFF40" 8 8 length of bzip2ed ctrl block 16 8 length of bzip2ed diff block 24 8 length of new file */ /* File is 0 32 Header 32 ?? Bzip2ed ctrl block ?? ?? Bzip2ed diff block ?? ?? Bzip2ed extra block */ byte[] header = new byte[c_headerSize]; WriteInt64(c_fileSignature, header, 0); // "BSDIFF40" WriteInt64(0, header, 8); WriteInt64(0, header, 16); WriteInt64(newData.Length, header, 24); long startPosition = output.Position; output.Write(header, 0, header.Length); int[] I = SuffixSort(oldData); byte[] db = new byte[newData.Length]; byte[] eb = new byte[newData.Length]; int dblen = 0; int eblen = 0; using (WrappingStream wrappingStream = new WrappingStream(output, Ownership.None)) using (var bz2Stream = new BZip2Stream(wrappingStream, CompressionMode.Compress)) { // compute the differences, writing ctrl as we go int scan = 0; int pos = 0; int len = 0; int lastscan = 0; int lastpos = 0; int lastoffset = 0; while (scan < newData.Length) { int oldscore = 0; for (int scsc = scan += len; scan < newData.Length; scan++) { len = Search(I, oldData, newData, scan, 0, oldData.Length, out pos); for (; scsc < scan + len; scsc++) { if ((scsc + lastoffset < oldData.Length) && (oldData[scsc + lastoffset] == newData[scsc])) oldscore++; } if ((len == oldscore && len != 0) || (len > oldscore + 8)) break; if ((scan + lastoffset < oldData.Length) && (oldData[scan + lastoffset] == newData[scan])) oldscore--; } if (len != oldscore || scan == newData.Length) { int s = 0; int sf = 0; int lenf = 0; for (int i = 0; (lastscan + i < scan) && (lastpos + i < oldData.Length); ) { if (oldData[lastpos + i] == newData[lastscan + i]) s++; i++; if (s * 2 - i > sf * 2 - lenf) { sf = s; lenf = i; } } int lenb = 0; if (scan < newData.Length) { s = 0; int sb = 0; for (int i = 1; (scan >= lastscan + i) && (pos >= i); i++) { if (oldData[pos - i] == newData[scan - i]) s++; if (s * 2 - i > sb * 2 - lenb) { sb = s; lenb = i; } } } if (lastscan + lenf > scan - lenb) { int overlap = (lastscan + lenf) - (scan - lenb); s = 0; int ss = 0; int lens = 0; for (int i = 0; i < overlap; i++) { if (newData[lastscan + lenf - overlap + i] == oldData[lastpos + lenf - overlap + i]) s++; if (newData[scan - lenb + i] == oldData[pos - lenb + i]) s--; if (s > ss) { ss = s; lens = i + 1; } } lenf += lens - overlap; lenb -= lens; } for (int i = 0; i < lenf; i++) db[dblen + i] = (byte)(newData[lastscan + i] - oldData[lastpos + i]); for (int i = 0; i < (scan - lenb) - (lastscan + lenf); i++) eb[eblen + i] = newData[lastscan + lenf + i]; dblen += lenf; eblen += (scan - lenb) - (lastscan + lenf); byte[] buf = new byte[8]; WriteInt64(lenf, buf, 0); bz2Stream.Write(buf, 0, 8); WriteInt64((scan - lenb) - (lastscan + lenf), buf, 0); bz2Stream.Write(buf, 0, 8); WriteInt64((pos - lenb) - (lastpos + lenf), buf, 0); bz2Stream.Write(buf, 0, 8); lastscan = scan - lenb; lastpos = pos - lenb; lastoffset = pos - scan; } } } // compute size of compressed ctrl data long controlEndPosition = output.Position; WriteInt64(controlEndPosition - startPosition - c_headerSize, header, 8); // write compressed diff data using (WrappingStream wrappingStream = new WrappingStream(output, Ownership.None)) using (var bz2Stream = new BZip2Stream(wrappingStream, CompressionMode.Compress)) { bz2Stream.Write(db, 0, dblen); } // compute size of compressed diff data long diffEndPosition = output.Position; WriteInt64(diffEndPosition - controlEndPosition, header, 16); // write compressed extra data, if any if (eblen > 0) { using (WrappingStream wrappingStream = new WrappingStream(output, Ownership.None)) using (var bz2Stream = new BZip2Stream(wrappingStream, CompressionMode.Compress)) { bz2Stream.Write(eb, 0, eblen); } } // seek to the beginning, write the header, then seek back to end long endPosition = output.Position; output.Position = startPosition; output.Write(header, 0, header.Length); output.Position = endPosition; } /// /// Applies a binary patch (in bsdiff format) to the data in /// and writes the results of patching to . /// /// A containing the input data. /// A func that can open a positioned at the start of the patch data. /// This stream must support reading and seeking, and must allow multiple streams on /// the patch to be opened concurrently. /// A to which the patched data is written. public static void Apply(Stream input, Func openPatchStream, Stream output) { // check arguments if (input == null) throw new ArgumentNullException("input"); if (openPatchStream == null) throw new ArgumentNullException("openPatchStream"); if (output == null) throw new ArgumentNullException("output"); /* File format: 0 8 "BSDIFF40" 8 8 X 16 8 Y 24 8 sizeof(newfile) 32 X bzip2(control block) 32+X Y bzip2(diff block) 32+X+Y ??? bzip2(extra block) with control block a set of triples (x,y,z) meaning "add x bytes from oldfile to x bytes from the diff block; copy y bytes from the extra block; seek forwards in oldfile by z bytes". */ // read header long controlLength, diffLength, newSize; using (Stream patchStream = openPatchStream()) { // check patch stream capabilities if (!patchStream.CanRead) throw new ArgumentException("Patch stream must be readable.", "openPatchStream"); if (!patchStream.CanSeek) throw new ArgumentException("Patch stream must be seekable.", "openPatchStream"); byte[] header = patchStream.ReadExactly(c_headerSize); // check for appropriate magic long signature = ReadInt64(header, 0); if (signature != c_fileSignature) throw new InvalidOperationException("Corrupt patch."); // read lengths from header controlLength = ReadInt64(header, 8); diffLength = ReadInt64(header, 16); newSize = ReadInt64(header, 24); if (controlLength < 0 || diffLength < 0 || newSize < 0) throw new InvalidOperationException("Corrupt patch."); } // preallocate buffers for reading and writing const int c_bufferSize = 1048576; byte[] newData = new byte[c_bufferSize]; byte[] oldData = new byte[c_bufferSize]; // prepare to read three parts of the patch in parallel using (Stream compressedControlStream = openPatchStream()) using (Stream compressedDiffStream = openPatchStream()) using (Stream compressedExtraStream = openPatchStream()) { // seek to the start of each part compressedControlStream.Seek(c_headerSize, SeekOrigin.Current); compressedDiffStream.Seek(c_headerSize + controlLength, SeekOrigin.Current); compressedExtraStream.Seek(c_headerSize + controlLength + diffLength, SeekOrigin.Current); // the stream might end here if there is no extra data var hasExtraData = compressedExtraStream.Position < compressedExtraStream.Length; // decompress each part (to read it) using (var controlStream = new BZip2Stream(compressedControlStream, CompressionMode.Decompress)) using (var diffStream = new BZip2Stream(compressedDiffStream, CompressionMode.Decompress)) using (var extraStream = hasExtraData ? new BZip2Stream(compressedExtraStream, CompressionMode.Decompress) : null) { long[] control = new long[3]; byte[] buffer = new byte[8]; int oldPosition = 0; int newPosition = 0; while (newPosition < newSize) { // read control data for (int i = 0; i < 3; i++) { controlStream.ReadExactly(buffer, 0, 8); control[i] = ReadInt64(buffer, 0); } // sanity-check if (newPosition + control[0] > newSize) throw new InvalidOperationException("Corrupt patch."); // seek old file to the position that the new data is diffed against input.Position = oldPosition; int bytesToCopy = (int)control[0]; while (bytesToCopy > 0) { int actualBytesToCopy = Math.Min(bytesToCopy, c_bufferSize); // read diff string diffStream.ReadExactly(newData, 0, actualBytesToCopy); // add old data to diff string int availableInputBytes = Math.Min(actualBytesToCopy, (int)(input.Length - input.Position)); input.ReadExactly(oldData, 0, availableInputBytes); for (int index = 0; index < availableInputBytes; index++) newData[index] += oldData[index]; output.Write(newData, 0, actualBytesToCopy); // adjust counters newPosition += actualBytesToCopy; oldPosition += actualBytesToCopy; bytesToCopy -= actualBytesToCopy; } // sanity-check if (newPosition + control[1] > newSize) throw new InvalidOperationException("Corrupt patch."); // read extra string bytesToCopy = (int)control[1]; while (bytesToCopy > 0) { int actualBytesToCopy = Math.Min(bytesToCopy, c_bufferSize); extraStream.ReadExactly(newData, 0, actualBytesToCopy); output.Write(newData, 0, actualBytesToCopy); newPosition += actualBytesToCopy; bytesToCopy -= actualBytesToCopy; } // adjust position oldPosition = (int)(oldPosition + control[2]); } } } } private static int CompareBytes(byte[] left, int leftOffset, byte[] right, int rightOffset) { for (int index = 0; index < left.Length - leftOffset && index < right.Length - rightOffset; index++) { int diff = left[index + leftOffset] - right[index + rightOffset]; if (diff != 0) return diff; } return 0; } private static int MatchLength(byte[] oldData, int oldOffset, byte[] newData, int newOffset) { int i; for (i = 0; i < oldData.Length - oldOffset && i < newData.Length - newOffset; i++) { if (oldData[i + oldOffset] != newData[i + newOffset]) break; } return i; } private static int Search(int[] I, byte[] oldData, byte[] newData, int newOffset, int start, int end, out int pos) { if (end - start < 2) { int startLength = MatchLength(oldData, I[start], newData, newOffset); int endLength = MatchLength(oldData, I[end], newData, newOffset); if (startLength > endLength) { pos = I[start]; return startLength; } else { pos = I[end]; return endLength; } } else { int midPoint = start + (end - start) / 2; return CompareBytes(oldData, I[midPoint], newData, newOffset) < 0 ? Search(I, oldData, newData, newOffset, midPoint, end, out pos) : Search(I, oldData, newData, newOffset, start, midPoint, out pos); } } private static void Split(int[] I, int[] v, int start, int len, int h) { if (len < 16) { int j; for (int k = start; k < start + len; k += j) { j = 1; int x = v[I[k] + h]; for (int i = 1; k + i < start + len; i++) { if (v[I[k + i] + h] < x) { x = v[I[k + i] + h]; j = 0; } if (v[I[k + i] + h] == x) { Swap(ref I[k + j], ref I[k + i]); j++; } } for (int i = 0; i < j; i++) v[I[k + i]] = k + j - 1; if (j == 1) I[k] = -1; } } else { int x = v[I[start + len / 2] + h]; int jj = 0; int kk = 0; for (int i2 = start; i2 < start + len; i2++) { if (v[I[i2] + h] < x) jj++; if (v[I[i2] + h] == x) kk++; } jj += start; kk += jj; int i = start; int j = 0; int k = 0; while (i < jj) { if (v[I[i] + h] < x) { i++; } else if (v[I[i] + h] == x) { Swap(ref I[i], ref I[jj + j]); j++; } else { Swap(ref I[i], ref I[kk + k]); k++; } } while (jj + j < kk) { if (v[I[jj + j] + h] == x) { j++; } else { Swap(ref I[jj + j], ref I[kk + k]); k++; } } if (jj > start) Split(I, v, start, jj - start, h); for (i = 0; i < kk - jj; i++) v[I[jj + i]] = kk - 1; if (jj == kk - 1) I[jj] = -1; if (start + len > kk) Split(I, v, kk, start + len - kk, h); } } private static int[] SuffixSort(byte[] oldData) { int[] buckets = new int[256]; foreach (byte oldByte in oldData) buckets[oldByte]++; for (int i = 1; i < 256; i++) buckets[i] += buckets[i - 1]; for (int i = 255; i > 0; i--) buckets[i] = buckets[i - 1]; buckets[0] = 0; int[] I = new int[oldData.Length + 1]; for (int i = 0; i < oldData.Length; i++) I[++buckets[oldData[i]]] = i; int[] v = new int[oldData.Length + 1]; for (int i = 0; i < oldData.Length; i++) v[i] = buckets[oldData[i]]; for (int i = 1; i < 256; i++) { if (buckets[i] == buckets[i - 1] + 1) I[buckets[i]] = -1; } I[0] = -1; for (int h = 1; I[0] != -(oldData.Length + 1); h += h) { int len = 0; int i = 0; while (i < oldData.Length + 1) { if (I[i] < 0) { len -= I[i]; i -= I[i]; } else { if (len != 0) I[i - len] = -len; len = v[I[i]] + 1 - i; Split(I, v, i, len, h); i += len; len = 0; } } if (len != 0) I[i - len] = -len; } for (int i = 0; i < oldData.Length + 1; i++) I[v[i]] = i; return I; } private static void Swap(ref int first, ref int second) { int temp = first; first = second; second = temp; } private static long ReadInt64(byte[] buf, int offset) { long value = buf[offset + 7] & 0x7F; for (int index = 6; index >= 0; index--) { value *= 256; value += buf[offset + index]; } if ((buf[offset + 7] & 0x80) != 0) value = -value; return value; } private static void WriteInt64(long value, byte[] buf, int offset) { long valueToWrite = value < 0 ? -value : value; for (int byteIndex = 0; byteIndex < 8; byteIndex++) { buf[offset + byteIndex] = (byte)(valueToWrite % 256); valueToWrite -= buf[offset + byteIndex]; valueToWrite /= 256; } if (value < 0) buf[offset + 7] |= 0x80; } const long c_fileSignature = 0x3034464649445342L; const int c_headerSize = 32; } /// /// A that wraps another stream. One major feature of is that it does not dispose the /// underlying stream when it is disposed if Ownership.None is used; this is useful when using classes such as and /// that take ownership of the stream passed to their constructors. /// /// See WrappingStream Implementation. public class WrappingStream : Stream { /// /// Initializes a new instance of the class. /// /// The wrapped stream. /// Use Owns if the wrapped stream should be disposed when this stream is disposed. public WrappingStream(Stream streamBase, Ownership ownership) { // check parameters if (streamBase == null) throw new ArgumentNullException("streamBase"); m_streamBase = streamBase; m_ownership = ownership; } /// /// Gets a value indicating whether the current stream supports reading. /// /// true if the stream supports reading; otherwise, false. public override bool CanRead { get { return m_streamBase == null ? false : m_streamBase.CanRead; } } /// /// Gets a value indicating whether the current stream supports seeking. /// /// true if the stream supports seeking; otherwise, false. public override bool CanSeek { get { return m_streamBase == null ? false : m_streamBase.CanSeek; } } /// /// Gets a value indicating whether the current stream supports writing. /// /// true if the stream supports writing; otherwise, false. public override bool CanWrite { get { return m_streamBase == null ? false : m_streamBase.CanWrite; } } /// /// Gets the length in bytes of the stream. /// public override long Length { get { ThrowIfDisposed(); return m_streamBase.Length; } } /// /// Gets or sets the position within the current stream. /// public override long Position { get { ThrowIfDisposed(); return m_streamBase.Position; } set { ThrowIfDisposed(); m_streamBase.Position = value; } } /// /// Begins an asynchronous read operation. /// public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { ThrowIfDisposed(); return m_streamBase.BeginRead(buffer, offset, count, callback, state); } /// /// Begins an asynchronous write operation. /// public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { ThrowIfDisposed(); return m_streamBase.BeginWrite(buffer, offset, count, callback, state); } /// /// Waits for the pending asynchronous read to complete. /// public override int EndRead(IAsyncResult asyncResult) { ThrowIfDisposed(); return m_streamBase.EndRead(asyncResult); } /// /// Ends an asynchronous write operation. /// public override void EndWrite(IAsyncResult asyncResult) { ThrowIfDisposed(); m_streamBase.EndWrite(asyncResult); } /// /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. /// public override void Flush() { ThrowIfDisposed(); m_streamBase.Flush(); } /// /// Reads a sequence of bytes from the current stream and advances the position /// within the stream by the number of bytes read. /// public override int Read(byte[] buffer, int offset, int count) { ThrowIfDisposed(); return m_streamBase.Read(buffer, offset, count); } /// /// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream. /// public override int ReadByte() { ThrowIfDisposed(); return m_streamBase.ReadByte(); } /// /// Sets the position within the current stream. /// /// A byte offset relative to the parameter. /// A value of type indicating the reference point used to obtain the new position. /// The new position within the current stream. public override long Seek(long offset, SeekOrigin origin) { ThrowIfDisposed(); return m_streamBase.Seek(offset, origin); } /// /// Sets the length of the current stream. /// /// The desired length of the current stream in bytes. public override void SetLength(long value) { ThrowIfDisposed(); m_streamBase.SetLength(value); } /// /// Writes a sequence of bytes to the current stream and advances the current position /// within this stream by the number of bytes written. /// public override void Write(byte[] buffer, int offset, int count) { ThrowIfDisposed(); m_streamBase.Write(buffer, offset, count); } /// /// Writes a byte to the current position in the stream and advances the position within the stream by one byte. /// public override void WriteByte(byte value) { ThrowIfDisposed(); m_streamBase.WriteByte(value); } /// /// Gets the wrapped stream. /// /// The wrapped stream. protected Stream WrappedStream { get { return m_streamBase; } } /// /// Releases the unmanaged resources used by the and optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { try { // doesn't close the base stream, but just prevents access to it through this WrappingStream if (disposing) { if (m_streamBase != null && m_ownership == Ownership.Owns) m_streamBase.Dispose(); m_streamBase = null; } } finally { base.Dispose(disposing); } } private void ThrowIfDisposed() { // throws an ObjectDisposedException if this object has been disposed if (m_streamBase == null) throw new ObjectDisposedException(GetType().Name); } Stream m_streamBase; readonly Ownership m_ownership; } /// /// Indicates whether an object takes ownership of an item. /// public enum Ownership { /// /// The object does not own this item. /// None, /// /// The object owns this item, and is responsible for releasing it. /// Owns } /// /// Provides helper methods for working with . /// public static class StreamUtility { /// /// Reads exactly bytes from . /// /// The stream to read from. /// The count of bytes to read. /// A new byte array containing the data read from the stream. public static byte[] ReadExactly(this Stream stream, int count) { if (count < 0) throw new ArgumentOutOfRangeException("count"); byte[] buffer = new byte[count]; ReadExactly(stream, buffer, 0, count); return buffer; } /// /// Reads exactly bytes from into /// , starting at the byte given by . /// /// The stream to read from. /// The buffer to read data into. /// The offset within the buffer at which data is first written. /// The count of bytes to read. public static void ReadExactly(this Stream stream, byte[] buffer, int offset, int count) { // check arguments if (stream == null) throw new ArgumentNullException("stream"); if (buffer == null) throw new ArgumentNullException("buffer"); if (offset < 0 || offset > buffer.Length) throw new ArgumentOutOfRangeException("offset"); if (count < 0 || buffer.Length - offset < count) throw new ArgumentOutOfRangeException("count"); while (count > 0) { // read data int bytesRead = stream.Read(buffer, offset, count); // check for failure to read if (bytesRead == 0) throw new EndOfStreamException(); // move to next block offset += bytesRead; count -= bytesRead; } } } } ================================================ FILE: src/Squirrel/ContentType.cs ================================================ using System; using System.Linq; using System.Xml; namespace Squirrel { internal static class ContentType { public static void Clean(XmlDocument doc) { var typesElement = doc.FirstChild.NextSibling; if (typesElement.Name.ToLowerInvariant() != "types") { throw new Exception("Invalid ContentTypes file, expected root node should be 'Types'"); } var children = typesElement.ChildNodes.OfType(); foreach (var child in children) { if (child.GetAttribute("Extension") == "") { typesElement.RemoveChild(child); } } } public static void Merge(XmlDocument doc) { var elements = new [] { Tuple.Create("Default", "diff", "application/octet" ), Tuple.Create("Default", "bsdiff", "application/octet" ), Tuple.Create("Default", "exe", "application/octet" ), Tuple.Create("Default", "dll", "application/octet" ), Tuple.Create("Default", "shasum", "text/plain" ), }; var typesElement = doc.FirstChild.NextSibling; if (typesElement.Name.ToLowerInvariant() != "types") { throw new Exception("Invalid ContentTypes file, expected root node should be 'Types'"); } var existingTypes = typesElement.ChildNodes.OfType() .Select(k => Tuple.Create(k.Name, k.GetAttribute("Extension").ToLowerInvariant(), k.GetAttribute("ContentType").ToLowerInvariant())); var toAdd = elements .Where(x => existingTypes.All(t => t.Item2 != x.Item2.ToLowerInvariant())) .Select(element => { var ret = doc.CreateElement(element.Item1, typesElement.NamespaceURI); var ext = doc.CreateAttribute("Extension"); ext.Value = element.Item2; var ct = doc.CreateAttribute("ContentType"); ct.Value = element.Item3; ret.Attributes.Append(ext); ret.Attributes.Append(ct); return ret; }); foreach (var v in toAdd) typesElement.AppendChild(v); } } } ================================================ FILE: src/Squirrel/DeltaPackage.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using Squirrel.SimpleSplat; using System.ComponentModel; using Squirrel.Bsdiff; using SharpCompress.Archives; using SharpCompress.Archives.Zip; using SharpCompress.Writers; using SharpCompress.Common; using SharpCompress.Readers; using SharpCompress.Compressors.Deflate; namespace Squirrel { public interface IDeltaPackageBuilder { ReleasePackage CreateDeltaPackage(ReleasePackage basePackage, ReleasePackage newPackage, string outputFile); ReleasePackage ApplyDeltaPackage(ReleasePackage basePackage, ReleasePackage deltaPackage, string outputFile); } public class DeltaPackageBuilder : IEnableLogger, IDeltaPackageBuilder { readonly string localAppDirectory; public DeltaPackageBuilder(string localAppDataOverride = null) { this.localAppDirectory = localAppDataOverride; } public ReleasePackage CreateDeltaPackage(ReleasePackage basePackage, ReleasePackage newPackage, string outputFile) { Contract.Requires(basePackage != null); Contract.Requires(!String.IsNullOrEmpty(outputFile) && !File.Exists(outputFile)); if (basePackage.Version > newPackage.Version) { var message = String.Format( "You cannot create a delta package based on version {0} as it is a later version than {1}", basePackage.Version, newPackage.Version); throw new InvalidOperationException(message); } if (basePackage.ReleasePackageFile == null) { throw new ArgumentException("The base package's release file is null", "basePackage"); } if (!File.Exists(basePackage.ReleasePackageFile)) { throw new FileNotFoundException("The base package release does not exist", basePackage.ReleasePackageFile); } if (!File.Exists(newPackage.ReleasePackageFile)) { throw new FileNotFoundException("The new package release does not exist", newPackage.ReleasePackageFile); } string baseTempPath = null; string tempPath = null; using (Utility.WithTempDirectory(out baseTempPath, null)) using (Utility.WithTempDirectory(out tempPath, null)) { var baseTempInfo = new DirectoryInfo(baseTempPath); var tempInfo = new DirectoryInfo(tempPath); this.Log().Info("Extracting {0} and {1} into {2}", basePackage.ReleasePackageFile, newPackage.ReleasePackageFile, tempPath); Utility.ExtractZipToDirectory(basePackage.ReleasePackageFile, baseTempInfo.FullName).Wait(); Utility.ExtractZipToDirectory(newPackage.ReleasePackageFile, tempInfo.FullName).Wait(); // Collect a list of relative paths under 'lib' and map them // to their full name. We'll use this later to determine in // the new version of the package whether the file exists or // not. var baseLibFiles = baseTempInfo.GetAllFilesRecursively() .Where(x => x.FullName.ToLowerInvariant().Contains("lib" + Path.DirectorySeparatorChar)) .ToDictionary(k => k.FullName.Replace(baseTempInfo.FullName, ""), v => v.FullName); var newLibDir = tempInfo.GetDirectories().First(x => x.Name.ToLowerInvariant() == "lib"); foreach (var libFile in newLibDir.GetAllFilesRecursively()) { createDeltaForSingleFile(libFile, tempInfo, baseLibFiles); } ReleasePackage.addDeltaFilesToContentTypes(tempInfo.FullName); Utility.CreateZipFromDirectory(outputFile, tempInfo.FullName).Wait(); } return new ReleasePackage(outputFile); } public ReleasePackage ApplyDeltaPackage(ReleasePackage basePackage, ReleasePackage deltaPackage, string outputFile) { return ApplyDeltaPackage(basePackage, deltaPackage, outputFile, x => { }); } public ReleasePackage ApplyDeltaPackage(ReleasePackage basePackage, ReleasePackage deltaPackage, string outputFile, Action progress) { Contract.Requires(deltaPackage != null); Contract.Requires(!String.IsNullOrEmpty(outputFile) && !File.Exists(outputFile)); string workingPath; string deltaPath; using (Utility.WithTempDirectory(out deltaPath, localAppDirectory)) using (Utility.WithTempDirectory(out workingPath, localAppDirectory)) { var opts = new ExtractionOptions() { ExtractFullPath = true, Overwrite = true, PreserveFileTime = true }; using (var za = ZipArchive.Open(deltaPackage.InputPackageFile)) using (var reader = za.ExtractAllEntries()) { reader.WriteAllToDirectory(deltaPath, opts); } progress(25); using (var za = ZipArchive.Open(basePackage.InputPackageFile)) using (var reader = za.ExtractAllEntries()) { reader.WriteAllToDirectory(workingPath, opts); } progress(50); var pathsVisited = new List(); var deltaPathRelativePaths = new DirectoryInfo(deltaPath).GetAllFilesRecursively() .Select(x => x.FullName.Replace(deltaPath + Path.DirectorySeparatorChar, "")) .ToArray(); // Apply all of the .diff files deltaPathRelativePaths .Where(x => x.StartsWith("lib", StringComparison.InvariantCultureIgnoreCase)) .Where(x => !x.EndsWith(".shasum", StringComparison.InvariantCultureIgnoreCase)) .Where(x => !x.EndsWith(".diff", StringComparison.InvariantCultureIgnoreCase) || !deltaPathRelativePaths.Contains(x.Replace(".diff", ".bsdiff"))) .ForEach(file => { pathsVisited.Add(Regex.Replace(file, @"\.(bs)?diff$", "").ToLowerInvariant()); applyDiffToFile(deltaPath, file, workingPath); }); progress(75); // Delete all of the files that were in the old package but // not in the new one. new DirectoryInfo(workingPath).GetAllFilesRecursively() .Select(x => x.FullName.Replace(workingPath + Path.DirectorySeparatorChar, "").ToLowerInvariant()) .Where(x => x.StartsWith("lib", StringComparison.InvariantCultureIgnoreCase) && !pathsVisited.Contains(x)) .ForEach(x => { this.Log().Info("{0} was in old package but not in new one, deleting", x); File.Delete(Path.Combine(workingPath, x)); }); progress(80); // Update all the files that aren't in 'lib' with the delta // package's versions (i.e. the nuspec file, etc etc). deltaPathRelativePaths .Where(x => !x.StartsWith("lib", StringComparison.InvariantCultureIgnoreCase)) .ForEach(x => { this.Log().Info("Updating metadata file: {0}", x); File.Copy(Path.Combine(deltaPath, x), Path.Combine(workingPath, x), true); }); this.Log().Info("Repacking into full package: {0}", outputFile); using (var za = ZipArchive.Create()) using (var tgt = File.OpenWrite(outputFile)) { za.DeflateCompressionLevel = CompressionLevel.BestSpeed; za.AddAllFromDirectory(workingPath); za.SaveTo(tgt); } progress(100); } return new ReleasePackage(outputFile); } void createDeltaForSingleFile(FileInfo targetFile, DirectoryInfo workingDirectory, Dictionary baseFileListing) { // NB: There are three cases here that we'll handle: // // 1. Exists only in new => leave it alone, we'll use it directly. // 2. Exists in both old and new => write a dummy file so we know // to keep it. // 3. Exists in old but changed in new => create a delta file // // The fourth case of "Exists only in old => delete it in new" // is handled when we apply the delta package var relativePath = targetFile.FullName.Replace(workingDirectory.FullName, ""); if (!baseFileListing.ContainsKey(relativePath)) { this.Log().Info("{0} not found in base package, marking as new", relativePath); return; } var oldData = File.ReadAllBytes(baseFileListing[relativePath]); var newData = File.ReadAllBytes(targetFile.FullName); if (bytesAreIdentical(oldData, newData)) { this.Log().Info("{0} hasn't changed, writing dummy file", relativePath); File.Create(targetFile.FullName + ".diff").Dispose(); File.Create(targetFile.FullName + ".shasum").Dispose(); targetFile.Delete(); return; } this.Log().Info("Delta patching {0} => {1}", baseFileListing[relativePath], targetFile.FullName); var msDelta = new MsDeltaCompression(); if (targetFile.Extension.Equals(".exe", StringComparison.OrdinalIgnoreCase) || targetFile.Extension.Equals(".dll", StringComparison.OrdinalIgnoreCase) || targetFile.Extension.Equals(".node", StringComparison.OrdinalIgnoreCase)) { try { msDelta.CreateDelta(baseFileListing[relativePath], targetFile.FullName, targetFile.FullName + ".diff"); goto exit; } catch (Exception) { this.Log().Warn("We couldn't create a delta for {0}, attempting to create bsdiff", targetFile.Name); } } try { using (FileStream of = File.Create(targetFile.FullName + ".bsdiff")) { BinaryPatchUtility.Create(oldData, newData, of); // NB: Create a dummy corrupt .diff file so that older // versions which don't understand bsdiff will fail out // until they get upgraded, instead of seeing the missing // file and just removing it. File.WriteAllText(targetFile.FullName + ".diff", "1"); } } catch (Exception ex) { this.Log().WarnException(String.Format("We really couldn't create a delta for {0}", targetFile.Name), ex); Utility.DeleteFileHarder(targetFile.FullName + ".bsdiff", true); Utility.DeleteFileHarder(targetFile.FullName + ".diff", true); return; } exit: var rl = ReleaseEntry.GenerateFromFile(new MemoryStream(newData), targetFile.Name + ".shasum"); File.WriteAllText(targetFile.FullName + ".shasum", rl.EntryAsString, Encoding.UTF8); targetFile.Delete(); } void applyDiffToFile(string deltaPath, string relativeFilePath, string workingDirectory) { var inputFile = Path.Combine(deltaPath, relativeFilePath); var finalTarget = Path.Combine(workingDirectory, Regex.Replace(relativeFilePath, @"\.(bs)?diff$", "")); var tempTargetFile = default(string); Utility.WithTempFile(out tempTargetFile, localAppDirectory); try { // NB: Zero-length diffs indicate the file hasn't actually changed if (new FileInfo(inputFile).Length == 0) { this.Log().Info("{0} exists unchanged, skipping", relativeFilePath); return; } if (relativeFilePath.EndsWith(".bsdiff", StringComparison.InvariantCultureIgnoreCase)) { using (var of = File.OpenWrite(tempTargetFile)) using (var inf = File.OpenRead(finalTarget)) { this.Log().Info("Applying BSDiff to {0}", relativeFilePath); BinaryPatchUtility.Apply(inf, () => File.OpenRead(inputFile), of); } verifyPatchedFile(relativeFilePath, inputFile, tempTargetFile); } else if (relativeFilePath.EndsWith(".diff", StringComparison.InvariantCultureIgnoreCase)) { this.Log().Info("Applying MSDiff to {0}", relativeFilePath); var msDelta = new MsDeltaCompression(); msDelta.ApplyDelta(inputFile, finalTarget, tempTargetFile); verifyPatchedFile(relativeFilePath, inputFile, tempTargetFile); } else { using (var of = File.OpenWrite(tempTargetFile)) using (var inf = File.OpenRead(inputFile)) { this.Log().Info("Adding new file: {0}", relativeFilePath); inf.CopyTo(of); } } if (File.Exists(finalTarget)) File.Delete(finalTarget); var targetPath = Directory.GetParent(finalTarget); if (!targetPath.Exists) targetPath.Create(); File.Move(tempTargetFile, finalTarget); } finally { if (File.Exists(tempTargetFile)) Utility.DeleteFileHarder(tempTargetFile, true); } } void verifyPatchedFile(string relativeFilePath, string inputFile, string tempTargetFile) { var shaFile = Regex.Replace(inputFile, @"\.(bs)?diff$", ".shasum"); var expectedReleaseEntry = ReleaseEntry.ParseReleaseEntry(File.ReadAllText(shaFile, Encoding.UTF8)); var actualReleaseEntry = ReleaseEntry.GenerateFromFile(tempTargetFile); if (expectedReleaseEntry.Filesize != actualReleaseEntry.Filesize) { this.Log().Warn("Patched file {0} has incorrect size, expected {1}, got {2}", relativeFilePath, expectedReleaseEntry.Filesize, actualReleaseEntry.Filesize); throw new ChecksumFailedException() {Filename = relativeFilePath}; } if (expectedReleaseEntry.SHA1 != actualReleaseEntry.SHA1) { this.Log().Warn("Patched file {0} has incorrect SHA1, expected {1}, got {2}", relativeFilePath, expectedReleaseEntry.SHA1, actualReleaseEntry.SHA1); throw new ChecksumFailedException() {Filename = relativeFilePath}; } } bool bytesAreIdentical(byte[] oldData, byte[] newData) { if (oldData == null || newData == null) { return oldData == newData; } if (oldData.LongLength != newData.LongLength) { return false; } for(long i = 0; i < newData.LongLength; i++) { if (oldData[i] != newData[i]) { return false; } } return true; } } } ================================================ FILE: src/Squirrel/EnumerableExtensions.cs ================================================ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt using System; using System.Linq; using System.Collections.Generic; namespace Squirrel { internal static class EnumerableExtensions { public static IEnumerable Return(T value) { yield return value; } /// /// Enumerates the sequence and invokes the given action for each value in the sequence. /// /// Source sequence element type. /// Source sequence. /// Action to invoke for each element. public static void ForEach(this IEnumerable source, Action onNext) { if (source == null) throw new ArgumentNullException("source"); if (onNext == null) throw new ArgumentNullException("onNext"); foreach (var item in source) onNext(item); } /// /// Returns the elements with the maximum key value by using the default comparer to compare key values. /// /// Source sequence element type. /// Key type. /// Source sequence. /// Key selector used to extract the key for each element in the sequence. /// List with the elements that share the same maximum key value. public static IList MaxBy(this IEnumerable source, Func keySelector) { if (source == null) throw new ArgumentNullException("source"); if (keySelector == null) throw new ArgumentNullException("keySelector"); return MaxBy(source, keySelector, Comparer.Default); } /// /// Returns the elements with the minimum key value by using the specified comparer to compare key values. /// /// Source sequence element type. /// Key type. /// Source sequence. /// Key selector used to extract the key for each element in the sequence. /// Comparer used to determine the maximum key value. /// List with the elements that share the same maximum key value. public static IList MaxBy(this IEnumerable source, Func keySelector, IComparer comparer) { if (source == null) throw new ArgumentNullException("source"); if (keySelector == null) throw new ArgumentNullException("keySelector"); if (comparer == null) throw new ArgumentNullException("comparer"); return ExtremaBy(source, keySelector, (key, minValue) => comparer.Compare(key, minValue)); } private static IList ExtremaBy(IEnumerable source, Func keySelector, Func compare) { var result = new List(); using (var e = source.GetEnumerator()) { if (!e.MoveNext()) throw new InvalidOperationException("Source sequence doesn't contain any elements."); var current = e.Current; var resKey = keySelector(current); result.Add(current); while (e.MoveNext()) { var cur = e.Current; var key = keySelector(cur); var cmp = compare(key, resKey); if (cmp == 0) { result.Add(cur); } else if (cmp > 0) { result = new List { cur }; resKey = key; } } } return result; } /// /// Lazily invokes an action for each value in the sequence. /// /// Source sequence element type. /// Source sequence. /// Action to invoke for each element. /// Sequence exhibiting the specified side-effects upon enumeration. public static IEnumerable Do(this IEnumerable source, Action onNext) { if (source == null) throw new ArgumentNullException("source"); if (onNext == null) throw new ArgumentNullException("onNext"); return DoHelper(source, onNext, _ => { }, () => { }); } /// /// Lazily invokes an action for each value in the sequence, and executes an action for successful termination. /// /// Source sequence element type. /// Source sequence. /// Action to invoke for each element. /// Action to invoke on successful termination of the sequence. /// Sequence exhibiting the specified side-effects upon enumeration. public static IEnumerable Do(this IEnumerable source, Action onNext, Action onCompleted) { if (source == null) throw new ArgumentNullException("source"); if (onNext == null) throw new ArgumentNullException("onNext"); if (onCompleted == null) throw new ArgumentNullException("onCompleted"); return DoHelper(source, onNext, _ => { }, onCompleted); } /// /// Lazily invokes an action for each value in the sequence, and executes an action upon exceptional termination. /// /// Source sequence element type. /// Source sequence. /// Action to invoke for each element. /// Action to invoke on exceptional termination of the sequence. /// Sequence exhibiting the specified side-effects upon enumeration. public static IEnumerable Do(this IEnumerable source, Action onNext, Action onError) { if (source == null) throw new ArgumentNullException("source"); if (onNext == null) throw new ArgumentNullException("onNext"); if (onError == null) throw new ArgumentNullException("onError"); return DoHelper(source, onNext, onError, () => { }); } /// /// Lazily invokes an action for each value in the sequence, and executes an action upon successful or exceptional termination. /// /// Source sequence element type. /// Source sequence. /// Action to invoke for each element. /// Action to invoke on exceptional termination of the sequence. /// Action to invoke on successful termination of the sequence. /// Sequence exhibiting the specified side-effects upon enumeration. public static IEnumerable Do(this IEnumerable source, Action onNext, Action onError, Action onCompleted) { if (source == null) throw new ArgumentNullException("source"); if (onNext == null) throw new ArgumentNullException("onNext"); if (onError == null) throw new ArgumentNullException("onError"); if (onCompleted == null) throw new ArgumentNullException("onCompleted"); return DoHelper(source, onNext, onError, onCompleted); } private static IEnumerable DoHelper(this IEnumerable source, Action onNext, Action onError, Action onCompleted) { using (var e = source.GetEnumerator()) { while (true) { var current = default(TSource); try { if (!e.MoveNext()) break; current = e.Current; } catch (Exception ex) { onError(ex); throw; } onNext(current); yield return current; } onCompleted(); } } /// /// Returns the source sequence prefixed with the specified value. /// /// Source sequence element type. /// Source sequence. /// Values to prefix the sequence with. /// Sequence starting with the specified prefix value, followed by the source sequence. public static IEnumerable StartWith(this IEnumerable source, params TSource[] values) { if (source == null) throw new ArgumentNullException("source"); return source.StartWith_(values); } static IEnumerable StartWith_(this IEnumerable source, params TSource[] values) { foreach (var x in values) yield return x; foreach (var item in source) yield return item; } /// /// Returns elements with a distinct key value by using the default equality comparer to compare key values. /// /// Source sequence element type. /// Key type. /// Source sequence. /// Key selector. /// Sequence that contains the elements from the source sequence with distinct key values. public static IEnumerable Distinct(this IEnumerable source, Func keySelector) { if (source == null) throw new ArgumentNullException("source"); if (keySelector == null) throw new ArgumentNullException("keySelector"); return source.Distinct_(keySelector, EqualityComparer.Default); } /// /// Returns elements with a distinct key value by using the specified equality comparer to compare key values. /// /// Source sequence element type. /// Key type. /// Source sequence. /// Key selector. /// Comparer used to compare key values. /// Sequence that contains the elements from the source sequence with distinct key values. public static IEnumerable Distinct(this IEnumerable source, Func keySelector, IEqualityComparer comparer) { if (source == null) throw new ArgumentNullException("source"); if (keySelector == null) throw new ArgumentNullException("keySelector"); if (comparer == null) throw new ArgumentNullException("comparer"); return source.Distinct_(keySelector, comparer); } static IEnumerable Distinct_(this IEnumerable source, Func keySelector, IEqualityComparer comparer) { var set = new HashSet(comparer); foreach (var item in source) { var key = keySelector(item); if (set.Add(key)) yield return item; } } } } ================================================ FILE: src/Squirrel/FileDownloader.cs ================================================ using System; using System.Net; using System.Threading.Tasks; using Squirrel.SimpleSplat; namespace Squirrel { public interface IFileDownloader { Task DownloadFile(string url, string targetFile, Action progress); Task DownloadUrl(string url); } public class FileDownloader : IFileDownloader, IEnableLogger { private readonly WebClient _providedClient; public FileDownloader(WebClient providedClient = null) { _providedClient = providedClient; } public async Task DownloadFile(string url, string targetFile, Action progress) { using (var wc = _providedClient ?? Utility.CreateWebClient()) { var failedUrl = default(string); var lastSignalled = DateTime.MinValue; wc.DownloadProgressChanged += (sender, args) => { var now = DateTime.Now; if (now - lastSignalled > TimeSpan.FromMilliseconds(500)) { lastSignalled = now; progress(args.ProgressPercentage); } }; retry: try { this.Log().Info("Downloading file: " + (failedUrl ?? url)); await this.WarnIfThrows( async () => { await wc.DownloadFileTaskAsync(failedUrl ?? url, targetFile); progress(100); }, "Failed downloading URL: " + (failedUrl ?? url)); } catch (Exception) { // NB: Some super brain-dead services are case-sensitive yet // corrupt case on upload. I can't even. if (failedUrl != null) throw; failedUrl = url.ToLower(); progress(0); goto retry; } } } public async Task DownloadUrl(string url) { using (var wc = _providedClient ?? Utility.CreateWebClient()) { var failedUrl = default(string); retry: try { this.Log().Info("Downloading url: " + (failedUrl ?? url)); return await this.WarnIfThrows(() => wc.DownloadDataTaskAsync(failedUrl ?? url), "Failed to download url: " + (failedUrl ?? url)); } catch (Exception) { // NB: Some super brain-dead services are case-sensitive yet // corrupt case on upload. I can't even. if (failedUrl != null) throw; failedUrl = url.ToLower(); goto retry; } } } } } ================================================ FILE: src/Squirrel/IUpdateManager.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.Win32; using Squirrel.SimpleSplat; using NuGet; namespace Squirrel { [Flags] public enum ShortcutLocation { StartMenu = 1 << 0, Desktop = 1 << 1, Startup = 1 << 2, /// /// A shortcut in the application folder, useful for portable applications. /// AppRoot = 1 << 3 } public enum UpdaterIntention { Install, Update } public interface IUpdateManager : IDisposable, IEnableLogger { /// /// Fetch the remote store for updates and compare against the current /// version to determine what updates to download. /// /// Indicates whether the UpdateManager is used /// in a Install or Update scenario. /// Set this flag if applying a release /// fails to fall back to a full release, which takes longer to download /// but is less error-prone. /// A Observer which can be used to report Progress - /// will return values from 0-100 and Complete, or Throw /// An UpdateInfo object representing the updates to install. /// Task CheckForUpdate(bool ignoreDeltaUpdates = false, Action progress = null, UpdaterIntention intention = UpdaterIntention.Update); /// /// Download a list of releases into the local package directory. /// /// The list of releases to download, /// almost always from UpdateInfo.ReleasesToApply. /// A Observer which can be used to report Progress - /// will return values from 0-100 and Complete, or Throw /// A completion Observable - either returns a single /// Unit.Default then Complete, or Throw Task DownloadReleases(IEnumerable releasesToDownload, Action progress = null); /// /// Take an already downloaded set of releases and apply them, /// copying in the new files from the NuGet package and rewriting /// the application shortcuts. /// /// The UpdateInfo instance acquired from /// CheckForUpdate /// A Observer which can be used to report Progress - /// will return values from 0-100 and Complete, or Throw /// The path to the installed application (i.e. the path where /// your package's contents ended up Task ApplyReleases(UpdateInfo updateInfo, Action progress = null); /// /// Completely Installs a targeted app /// /// If true, don't run the app once install completes. /// A Observer which can be used to report Progress - /// will return values from 0-100 and Complete, or Throw /// Completion Task FullInstall(bool silentInstall, Action progress = null); /// /// Completely uninstalls the targeted app /// /// Completion Task FullUninstall(); /// /// Gets the currently installed version of the given executable, or if /// not given, the currently running assembly /// /// The executable to check, or null for this /// executable /// The running version, or null if this is not a Squirrel /// installed app (i.e. you're running from VS) SemanticVersion CurrentlyInstalledVersion(string executable = null); /// /// Creates an entry in Programs and Features based on the currently /// applied package /// /// The command to run to uninstall, usually update.exe --uninstall /// The switch for silent uninstall, usually --silent /// The registry key that was created Task CreateUninstallerRegistryEntry(string uninstallCmd, string quietSwitch); /// /// Creates an entry in Programs and Features based on the currently /// applied package. Uses the built-in Update.exe to handle uninstall. /// /// The registry key that was created Task CreateUninstallerRegistryEntry(); /// /// Removes the entry in Programs and Features created via /// CreateUninstallerRegistryEntry /// void RemoveUninstallerRegistryEntry(); /// /// Create a shortcut on the Desktop / Start Menu for the given /// executable. Metadata from the currently installed NuGet package /// and information from the Version Header of the EXE will be used /// to construct the shortcut folder / name. /// /// The name of the executable, relative to the /// app install directory. /// The locations to install the shortcut /// Set to false during initial install, true /// during app update. /// The arguments to code into the shortcut /// The shortcut icon void CreateShortcutsForExecutable(string exeName, ShortcutLocation locations, bool updateOnly, string programArguments, string icon); /// /// Removes shortcuts created by CreateShortcutsForExecutable /// /// The name of the executable, relative to the /// app install directory. /// The locations to install the shortcut void RemoveShortcutsForExecutable(string exeName, ShortcutLocation locations); } public static class EasyModeMixin { public static async Task UpdateApp(this IUpdateManager This, Action progress = null) { progress = progress ?? (_ => {}); This.Log().Info("Starting automatic update"); bool ignoreDeltaUpdates = false; retry: var updateInfo = default(UpdateInfo); try { updateInfo = await This.ErrorIfThrows(() => This.CheckForUpdate(ignoreDeltaUpdates, x => progress(x / 3)), "Failed to check for updates"); await This.ErrorIfThrows(() => This.DownloadReleases(updateInfo.ReleasesToApply, x => progress(x / 3 + 33)), "Failed to download updates"); await This.ErrorIfThrows(() => This.ApplyReleases(updateInfo, x => progress(x / 3 + 66)), "Failed to apply updates"); await This.ErrorIfThrows(() => This.CreateUninstallerRegistryEntry(), "Failed to set up uninstaller"); } catch { if (ignoreDeltaUpdates == false) { ignoreDeltaUpdates = true; goto retry; } throw; } return updateInfo.ReleasesToApply.Any() ? updateInfo.ReleasesToApply.MaxBy(x => x.Version).Last() : default(ReleaseEntry); } public static void CreateShortcutForThisExe(this IUpdateManager This) { string entrypoint = Assembly.GetEntryAssembly().Location; if (String.Equals(Path.GetExtension(entrypoint), ".dll", StringComparison.OrdinalIgnoreCase)) { // This happens in .NET Core apps. A shortcut to a .dll doesn't work, so replace with the .exe. string candidateExe = Path.Combine(Path.GetDirectoryName(entrypoint), Path.GetFileNameWithoutExtension(entrypoint)) + ".exe"; if (File.Exists(candidateExe)) { entrypoint = candidateExe; } } This.CreateShortcutsForExecutable( Path.GetFileName(entrypoint), ShortcutLocation.Desktop | ShortcutLocation.StartMenu, Environment.CommandLine.Contains("squirrel-install") == false, null, null); } public static void RemoveShortcutForThisExe(this IUpdateManager This) { This.RemoveShortcutsForExecutable( Path.GetFileName(Assembly.GetEntryAssembly().Location), ShortcutLocation.Desktop | ShortcutLocation.StartMenu); } } } ================================================ FILE: src/Squirrel/MarkdownSharp.cs ================================================ /* * MarkdownSharp * ------------- * a C# Markdown processor * * Markdown is a text-to-HTML conversion tool for web writers * Copyright (c) 2004 John Gruber * http://daringfireball.net/projects/markdown/ * * Markdown.NET * Copyright (c) 2004-2009 Milan Negovan * http://www.aspnetresources.com * http://aspnetresources.com/blog/markdown_announced.aspx * * MarkdownSharp * Copyright (c) 2009-2011 Jeff Atwood * http://stackoverflow.com * http://www.codinghorror.com/blog/ * http://code.google.com/p/markdownsharp/ * * History: Milan ported the Markdown processor to C#. He granted license to me so I can open source it * and let the community contribute to and improve MarkdownSharp. * */ #region Copyright and license /* Copyright (c) 2009 - 2010 Jeff Atwood http://www.opensource.org/licenses/mit-license.php Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Copyright (c) 2003-2004 John Gruber All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name "Markdown" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. */ #endregion using System; using System.Collections.Generic; using System.Configuration; using System.Text; using System.Text.RegularExpressions; namespace MarkdownSharp { public class MarkdownOptions { /// /// when true, (most) bare plain URLs are auto-hyperlinked /// WARNING: this is a significant deviation from the markdown spec /// public bool AutoHyperlink { get; set; } /// /// when true, RETURN becomes a literal newline /// WARNING: this is a significant deviation from the markdown spec /// public bool AutoNewlines { get; set; } /// /// use ">" for HTML output, or " />" for XHTML output /// public string EmptyElementSuffix { get; set; } /// /// when true, problematic URL characters like [, ], (, and so forth will be encoded /// WARNING: this is a significant deviation from the markdown spec /// public bool EncodeProblemUrlCharacters { get; set; } /// /// when false, email addresses will never be auto-linked /// WARNING: this is a significant deviation from the markdown spec /// public bool LinkEmails { get; set; } /// /// when true, bold and italic require non-word characters on either side /// WARNING: this is a significant deviation from the markdown spec /// public bool StrictBoldItalic { get; set; } } /// /// Markdown is a text-to-HTML conversion tool for web writers. /// Markdown allows you to write using an easy-to-read, easy-to-write plain text format, /// then convert it to structurally valid XHTML (or HTML). /// public class Markdown { private const string _version = "1.13"; #region Constructors and Options /// /// Create a new Markdown instance using default options /// public Markdown() { } /// /// Create a new Markdown instance and set the options from the MarkdownOptions object. /// public Markdown(MarkdownOptions options) { _autoHyperlink = options.AutoHyperlink; _autoNewlines = options.AutoNewlines; _emptyElementSuffix = options.EmptyElementSuffix; _encodeProblemUrlCharacters = options.EncodeProblemUrlCharacters; _linkEmails = options.LinkEmails; _strictBoldItalic = options.StrictBoldItalic; } /// /// use ">" for HTML output, or " />" for XHTML output /// public string EmptyElementSuffix { get { return _emptyElementSuffix; } set { _emptyElementSuffix = value; } } private string _emptyElementSuffix = " />"; /// /// when false, email addresses will never be auto-linked /// WARNING: this is a significant deviation from the markdown spec /// public bool LinkEmails { get { return _linkEmails; } set { _linkEmails = value; } } private bool _linkEmails = true; /// /// when true, bold and italic require non-word characters on either side /// WARNING: this is a significant deviation from the markdown spec /// public bool StrictBoldItalic { get { return _strictBoldItalic; } set { _strictBoldItalic = value; } } private bool _strictBoldItalic = false; /// /// when true, RETURN becomes a literal newline /// WARNING: this is a significant deviation from the markdown spec /// public bool AutoNewLines { get { return _autoNewlines; } set { _autoNewlines = value; } } private bool _autoNewlines = false; /// /// when true, (most) bare plain URLs are auto-hyperlinked /// WARNING: this is a significant deviation from the markdown spec /// public bool AutoHyperlink { get { return _autoHyperlink; } set { _autoHyperlink = value; } } private bool _autoHyperlink = false; /// /// when true, problematic URL characters like [, ], (, and so forth will be encoded /// WARNING: this is a significant deviation from the markdown spec /// public bool EncodeProblemUrlCharacters { get { return _encodeProblemUrlCharacters; } set { _encodeProblemUrlCharacters = value; } } private bool _encodeProblemUrlCharacters = false; #endregion private enum TokenType { Text, Tag } private struct Token { public Token(TokenType type, string value) { this.Type = type; this.Value = value; } public TokenType Type; public string Value; } /// /// maximum nested depth of [] and () supported by the transform; implementation detail /// private const int _nestDepth = 6; /// /// Tabs are automatically converted to spaces as part of the transform /// this constant determines how "wide" those tabs become in spaces /// private const int _tabWidth = 4; private const string _markerUL = @"[*+-]"; private const string _markerOL = @"\d+[.]"; private static readonly Dictionary _escapeTable; private static readonly Dictionary _invertedEscapeTable; private static readonly Dictionary _backslashEscapeTable; private readonly Dictionary _urls = new Dictionary(); private readonly Dictionary _titles = new Dictionary(); private readonly Dictionary _htmlBlocks = new Dictionary(); private int _listLevel; private static string AutoLinkPreventionMarker = "\x1AP"; // temporarily replaces "://" where auto-linking shouldn't happen; /// /// In the static constuctor we'll initialize what stays the same across all transforms. /// static Markdown() { // Table of hash values for escaped characters: _escapeTable = new Dictionary(); _invertedEscapeTable = new Dictionary(); // Table of hash value for backslash escaped characters: _backslashEscapeTable = new Dictionary(); string backslashPattern = ""; foreach (char c in @"\`*_{}[]()>#+-.!/") { string key = c.ToString(); string hash = GetHashKey(key, isHtmlBlock: false); _escapeTable.Add(key, hash); _invertedEscapeTable.Add(hash, key); _backslashEscapeTable.Add(@"\" + key, hash); backslashPattern += Regex.Escape(@"\" + key) + "|"; } _backslashEscapes = new Regex(backslashPattern.Substring(0, backslashPattern.Length - 1), RegexOptions.Compiled); } /// /// current version of MarkdownSharp; /// see http://code.google.com/p/markdownsharp/ for the latest code or to contribute /// public string Version { get { return _version; } } /// /// Transforms the provided Markdown-formatted text to HTML; /// see http://en.wikipedia.org/wiki/Markdown /// /// /// The order in which other subs are called here is /// essential. Link and image substitutions need to happen before /// EscapeSpecialChars(), so that any *'s or _'s in the a /// and img tags get encoded. /// public string Transform(string text) { if (String.IsNullOrEmpty(text)) return ""; Setup(); text = Normalize(text); text = HashHTMLBlocks(text); text = StripLinkDefinitions(text); text = RunBlockGamut(text); text = Unescape(text); Cleanup(); return text + "\n"; } /// /// Perform transformations that form block-level tags like paragraphs, headers, and list items. /// private string RunBlockGamut(string text, bool unhash = true) { text = DoHeaders(text); text = DoHorizontalRules(text); text = DoLists(text); text = DoCodeBlocks(text); text = DoBlockQuotes(text); // We already ran HashHTMLBlocks() before, in Markdown(), but that // was to escape raw HTML in the original Markdown source. This time, // we're escaping the markup we've just created, so that we don't wrap //

tags around block-level tags. text = HashHTMLBlocks(text); text = FormParagraphs(text, unhash: unhash); return text; } ///

/// Perform transformations that occur *within* block-level tags like paragraphs, headers, and list items. /// private string RunSpanGamut(string text) { text = DoCodeSpans(text); text = EscapeSpecialCharsWithinTagAttributes(text); text = EscapeBackslashes(text); // Images must come first, because ![foo][f] looks like an anchor. text = DoImages(text); text = DoAnchors(text); // Must come after DoAnchors(), because you can use < and > // delimiters in inline links like [this](). text = DoAutoLinks(text); text = text.Replace(AutoLinkPreventionMarker, "://"); text = EncodeAmpsAndAngles(text); text = DoItalicsAndBold(text); text = DoHardBreaks(text); return text; } private static Regex _newlinesLeadingTrailing = new Regex(@"^\n+|\n+\z", RegexOptions.Compiled); private static Regex _newlinesMultiple = new Regex(@"\n{2,}", RegexOptions.Compiled); private static Regex _leadingWhitespace = new Regex(@"^[ ]*", RegexOptions.Compiled); private static Regex _htmlBlockHash = new Regex("\x1AH\\d+H", RegexOptions.Compiled); /// /// splits on two or more newlines, to form "paragraphs"; /// each paragraph is then unhashed (if it is a hash and unhashing isn't turned off) or wrapped in HTML p tag /// private string FormParagraphs(string text, bool unhash = true) { // split on two or more newlines string[] grafs = _newlinesMultiple.Split(_newlinesLeadingTrailing.Replace(text, "")); for (int i = 0; i < grafs.Length; i++) { if (grafs[i].StartsWith("\x1AH")) { // unhashify HTML blocks if (unhash) { int sanityCheck = 50; // just for safety, guard against an infinite loop bool keepGoing = true; // as long as replacements where made, keep going while (keepGoing && sanityCheck > 0) { keepGoing = false; grafs[i] = _htmlBlockHash.Replace(grafs[i], match => { keepGoing = true; return _htmlBlocks[match.Value]; }); sanityCheck--; } /* if (keepGoing) { // Logging of an infinite loop goes here. // If such a thing should happen, please open a new issue on http://code.google.com/p/markdownsharp/ // with the input that caused it. }*/ } } else { // do span level processing inside the block, then wrap result in

tags grafs[i] = _leadingWhitespace.Replace(RunSpanGamut(grafs[i]), "

") + "

"; } } return string.Join("\n\n", grafs); } private void Setup() { // Clear the global hashes. If we don't clear these, you get conflicts // from other articles when generating a page which contains more than // one article (e.g. an index page that shows the N most recent // articles): _urls.Clear(); _titles.Clear(); _htmlBlocks.Clear(); _listLevel = 0; } private void Cleanup() { Setup(); } private static string _nestedBracketsPattern; /// /// Reusable pattern to match balanced [brackets]. See Friedl's /// "Mastering Regular Expressions", 2nd Ed., pp. 328-331. /// private static string GetNestedBracketsPattern() { // in other words [this] and [this[also]] and [this[also[too]]] // up to _nestDepth if (_nestedBracketsPattern == null) _nestedBracketsPattern = RepeatString(@" (?> # Atomic matching [^\[\]]+ # Anything other than brackets | \[ ", _nestDepth) + RepeatString( @" \] )*" , _nestDepth); return _nestedBracketsPattern; } private static string _nestedParensPattern; /// /// Reusable pattern to match balanced (parens). See Friedl's /// "Mastering Regular Expressions", 2nd Ed., pp. 328-331. /// private static string GetNestedParensPattern() { // in other words (this) and (this(also)) and (this(also(too))) // up to _nestDepth if (_nestedParensPattern == null) _nestedParensPattern = RepeatString(@" (?> # Atomic matching [^()\s]+ # Anything other than parens or whitespace | \( ", _nestDepth) + RepeatString( @" \) )*" , _nestDepth); return _nestedParensPattern; } private static Regex _linkDef = new Regex(string.Format(@" ^[ ]{{0,{0}}}\[([^\[\]]+)\]: # id = $1 [ ]* \n? # maybe *one* newline [ ]* ? # url = $2 [ ]* \n? # maybe one newline [ ]* (?: (?<=\s) # lookbehind for whitespace [""(] (.+?) # title = $3 ["")] [ ]* )? # title is optional (?:\n+|\Z)", _tabWidth - 1), RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); /// /// Strips link definitions from text, stores the URLs and titles in hash references. /// /// /// ^[id]: url "optional title" /// private string StripLinkDefinitions(string text) { return _linkDef.Replace(text, new MatchEvaluator(LinkEvaluator)); } private string LinkEvaluator(Match match) { string linkID = match.Groups[1].Value.ToLowerInvariant(); _urls[linkID] = EncodeAmpsAndAngles(match.Groups[2].Value); if (match.Groups[3] != null && match.Groups[3].Length > 0) _titles[linkID] = match.Groups[3].Value.Replace("\"", """); return ""; } // compiling this monster regex results in worse performance. trust me. private static Regex _blocksHtml = new Regex(GetBlockPattern(), RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace); /// /// derived pretty much verbatim from PHP Markdown /// private static string GetBlockPattern() { // Hashify HTML blocks: // We only want to do this for block-level HTML tags, such as headers, // lists, and tables. That's because we still want to wrap

s around // "paragraphs" that are wrapped in non-block-level tags, such as anchors, // phrase emphasis, and spans. The list of tags we're looking for is // hard-coded: // // * List "a" is made of tags which can be both inline or block-level. // These will be treated block-level when the start tag is alone on // its line, otherwise they're not matched here and will be taken as // inline later. // * List "b" is made of tags which are always block-level; // string blockTagsA = "ins|del"; string blockTagsB = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|script|noscript|form|fieldset|iframe|math"; // Regular expression for the content of a block tag. string attr = @" (?>    # optional tag attributes \s   # starts with whitespace (?> [^>""/]+ # text outside quotes | /+(?!>)  # slash not followed by > | ""[^""]*""  # text inside double quotes (tolerate >) | '[^']*' # text inside single quotes (tolerate >) )* )? "; string content = RepeatString(@" (?> [^<]+   # content without tag | <\2   # nested opening tag " + attr + @" # attributes (?> /> | >", _nestDepth) + // end of opening tag ".*?" + // last level nested tag content RepeatString(@"  # closing nested tag ) |    <(?!/\2\s*> # other tags with a different name ) )*", _nestDepth); string content2 = content.Replace(@"\2", @"\3"); // First, look for nested blocks, e.g.: // 

//  
//   tags for inner block must be indented. //  
// 
// // The outermost tags must start at the left margin for this to match, and // the inner nested divs must be indented. // We need to do this before the next, more liberal match, because the next // match will start at the first `
` and stop at the first `
`. string pattern = @" (?> (?> (?<=\n) # Starting at the beginning of a line | # or \A\n? # the beginning of the doc ) ( # save in $1 # Match from `\n` to `\n`, handling nested tags # in between. <($block_tags_b_re) # start tag = $2 $attr> # attributes followed by > and \n $content # content, support nesting # the matching end tag [ ]* # trailing spaces (?=\n+|\Z) # followed by a newline or end of document | # Special version for tags of group a. <($block_tags_a_re) # start tag = $3 $attr>[ ]*\n # attributes followed by > $content2 # content, support nesting # the matching end tag [ ]* # trailing spaces (?=\n+|\Z) # followed by a newline or end of document | # Special case just for
. It was easier to make a special # case than to make the other regex more complicated. [ ]{0,$less_than_tab}
# the matching end tag [ ]* (?=\n{2,}|\Z) # followed by a blank line or end of document | # Special case for standalone HTML comments: (?<=\n\n|\A) # preceded by a blank line or start of document [ ]{0,$less_than_tab} (?s: ) [ ]* (?=\n{2,}|\Z) # followed by a blank line or end of document | # PHP and ASP-style processor instructions ( ) [ ]* (?=\n{2,}|\Z) # followed by a blank line or end of document ) )"; pattern = pattern.Replace("$less_than_tab", (_tabWidth - 1).ToString()); pattern = pattern.Replace("$block_tags_b_re", blockTagsB); pattern = pattern.Replace("$block_tags_a_re", blockTagsA); pattern = pattern.Replace("$attr", attr); pattern = pattern.Replace("$content2", content2); pattern = pattern.Replace("$content", content); return pattern; } /// /// replaces any block-level HTML blocks with hash entries /// private string HashHTMLBlocks(string text) { return _blocksHtml.Replace(text, new MatchEvaluator(HtmlEvaluator)); } private string HtmlEvaluator(Match match) { string text = match.Groups[1].Value; string key = GetHashKey(text, isHtmlBlock: true); _htmlBlocks[key] = text; return string.Concat("\n\n", key, "\n\n"); } private static string GetHashKey(string s, bool isHtmlBlock) { var delim = isHtmlBlock ? 'H' : 'E'; return "\x1A" + delim + Math.Abs(s.GetHashCode()).ToString() + delim; } private static Regex _htmlTokens = new Regex(@" ()| # match (<\?.*?\?>)| # match " + RepeatString(@" (<[A-Za-z\/!$](?:[^<>]|", _nestDepth) + RepeatString(@")*>)", _nestDepth) + " # match and ", RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); /// /// returns an array of HTML tokens comprising the input string. Each token is /// either a tag (possibly with nested, tags contained therein, such /// as <a href="<MTFoo>">, or a run of text between tags. Each element of the /// array is a two-element array; the first is either 'tag' or 'text'; the second is /// the actual value. /// private List TokenizeHTML(string text) { int pos = 0; int tagStart = 0; var tokens = new List(); // this regex is derived from the _tokenize() subroutine in Brad Choate's MTRegex plugin. // http://www.bradchoate.com/past/mtregex.php foreach (Match m in _htmlTokens.Matches(text)) { tagStart = m.Index; if (pos < tagStart) tokens.Add(new Token(TokenType.Text, text.Substring(pos, tagStart - pos))); tokens.Add(new Token(TokenType.Tag, m.Value)); pos = tagStart + m.Length; } if (pos < text.Length) tokens.Add(new Token(TokenType.Text, text.Substring(pos, text.Length - pos))); return tokens; } private static Regex _anchorRef = new Regex(string.Format(@" ( # wrap whole match in $1 \[ ({0}) # link text = $2 \] [ ]? # one optional space (?:\n[ ]*)? # one optional newline followed by spaces \[ (.*?) # id = $3 \] )", GetNestedBracketsPattern()), RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); private static Regex _anchorInline = new Regex(string.Format(@" ( # wrap whole match in $1 \[ ({0}) # link text = $2 \] \( # literal paren [ ]* ({1}) # href = $3 [ ]* ( # $4 (['""]) # quote char = $5 (.*?) # title = $6 \5 # matching quote [ ]* # ignore any spaces between closing quote and ) )? # title is optional \) )", GetNestedBracketsPattern(), GetNestedParensPattern()), RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); private static Regex _anchorRefShortcut = new Regex(@" ( # wrap whole match in $1 \[ ([^\[\]]+) # link text = $2; can't contain [ or ] \] )", RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); /// /// Turn Markdown link shortcuts into HTML anchor tags /// /// /// [link text](url "title") /// [link text][id] /// [id] /// private string DoAnchors(string text) { // First, handle reference-style links: [link text] [id] text = _anchorRef.Replace(text, new MatchEvaluator(AnchorRefEvaluator)); // Next, inline-style links: [link text](url "optional title") or [link text](url "optional title") text = _anchorInline.Replace(text, new MatchEvaluator(AnchorInlineEvaluator)); // Last, handle reference-style shortcuts: [link text] // These must come last in case you've also got [link test][1] // or [link test](/foo) text = _anchorRefShortcut.Replace(text, new MatchEvaluator(AnchorRefShortcutEvaluator)); return text; } private string SaveFromAutoLinking(string s) { return s.Replace("://", AutoLinkPreventionMarker); } private string AnchorRefEvaluator(Match match) { string wholeMatch = match.Groups[1].Value; string linkText = SaveFromAutoLinking(match.Groups[2].Value); string linkID = match.Groups[3].Value.ToLowerInvariant(); string result; // for shortcut links like [this][]. if (linkID == "") linkID = linkText.ToLowerInvariant(); if (_urls.ContainsKey(linkID)) { string url = _urls[linkID]; url = EncodeProblemUrlChars(url); url = EscapeBoldItalic(url); result = ""; } else result = wholeMatch; return result; } private string AnchorRefShortcutEvaluator(Match match) { string wholeMatch = match.Groups[1].Value; string linkText = SaveFromAutoLinking(match.Groups[2].Value); string linkID = Regex.Replace(linkText.ToLowerInvariant(), @"[ ]*\n[ ]*", " "); // lower case and remove newlines / extra spaces string result; if (_urls.ContainsKey(linkID)) { string url = _urls[linkID]; url = EncodeProblemUrlChars(url); url = EscapeBoldItalic(url); result = ""; } else result = wholeMatch; return result; } private string AnchorInlineEvaluator(Match match) { string linkText = SaveFromAutoLinking(match.Groups[2].Value); string url = match.Groups[3].Value; string title = match.Groups[6].Value; string result; url = EncodeProblemUrlChars(url); url = EscapeBoldItalic(url); if (url.StartsWith("<") && url.EndsWith(">")) url = url.Substring(1, url.Length - 2); // remove <>'s surrounding URL, if present result = string.Format("{0}", linkText); return result; } private static Regex _imagesRef = new Regex(@" ( # wrap whole match in $1 !\[ (.*?) # alt text = $2 \] [ ]? # one optional space (?:\n[ ]*)? # one optional newline followed by spaces \[ (.*?) # id = $3 \] )", RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.Compiled); private static Regex _imagesInline = new Regex(String.Format(@" ( # wrap whole match in $1 !\[ (.*?) # alt text = $2 \] \s? # one optional whitespace character \( # literal paren [ ]* ({0}) # href = $3 [ ]* ( # $4 (['""]) # quote char = $5 (.*?) # title = $6 \5 # matching quote [ ]* )? # title is optional \) )", GetNestedParensPattern()), RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.Compiled); /// /// Turn Markdown image shortcuts into HTML img tags. /// /// /// ![alt text][id] /// ![alt text](url "optional title") /// private string DoImages(string text) { // First, handle reference-style labeled images: ![alt text][id] text = _imagesRef.Replace(text, new MatchEvaluator(ImageReferenceEvaluator)); // Next, handle inline images: ![alt text](url "optional title") // Don't forget: encode * and _ text = _imagesInline.Replace(text, new MatchEvaluator(ImageInlineEvaluator)); return text; } // This prevents the creation of horribly broken HTML when some syntax ambiguities // collide. It likely still doesn't do what the user meant, but at least we're not // outputting garbage. private string EscapeImageAltText(string s) { s = EscapeBoldItalic(s); s = Regex.Replace(s, @"[\[\]()]", m => _escapeTable[m.ToString()]); return s; } private string ImageReferenceEvaluator(Match match) { string wholeMatch = match.Groups[1].Value; string altText = match.Groups[2].Value; string linkID = match.Groups[3].Value.ToLowerInvariant(); // for shortcut links like ![this][]. if (linkID == "") linkID = altText.ToLowerInvariant(); if (_urls.ContainsKey(linkID)) { string url = _urls[linkID]; string title = null; if (_titles.ContainsKey(linkID)) title = _titles[linkID]; return ImageTag(url, altText, title); } else { // If there's no such link ID, leave intact: return wholeMatch; } } private string ImageInlineEvaluator(Match match) { string alt = match.Groups[2].Value; string url = match.Groups[3].Value; string title = match.Groups[6].Value; if (url.StartsWith("<") && url.EndsWith(">")) url = url.Substring(1, url.Length - 2); // Remove <>'s surrounding URL, if present return ImageTag(url, alt, title); } private string ImageTag(string url, string altText, string title) { altText = EscapeImageAltText(AttributeEncode(altText)); url = EncodeProblemUrlChars(url); url = EscapeBoldItalic(url); var result = string.Format("\"{1}\"", /// Turn Markdown headers into HTML header tags /// /// /// Header 1 /// ======== /// /// Header 2 /// -------- /// /// # Header 1 /// ## Header 2 /// ## Header 2 with closing hashes ## /// ... /// ###### Header 6 /// private string DoHeaders(string text) { text = _headerSetext.Replace(text, new MatchEvaluator(SetextHeaderEvaluator)); text = _headerAtx.Replace(text, new MatchEvaluator(AtxHeaderEvaluator)); return text; } private string SetextHeaderEvaluator(Match match) { string header = match.Groups[1].Value; int level = match.Groups[2].Value.StartsWith("=") ? 1 : 2; return string.Format("{0}\n\n", RunSpanGamut(header), level); } private string AtxHeaderEvaluator(Match match) { string header = match.Groups[2].Value; int level = match.Groups[1].Value.Length; return string.Format("{0}\n\n", RunSpanGamut(header), level); } private static Regex _horizontalRules = new Regex(@" ^[ ]{0,3} # Leading space ([-*_]) # $1: First marker (?> # Repeated marker group [ ]{0,2} # Zero, one, or two spaces. \1 # Marker character ){2,} # Group repeated at least twice [ ]* # Trailing spaces $ # End of line. ", RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); /// /// Turn Markdown horizontal rules into HTML hr tags /// /// /// *** /// * * * /// --- /// - - - /// private string DoHorizontalRules(string text) { return _horizontalRules.Replace(text, " /// Turn Markdown lists into HTML ul and ol and li tags /// private string DoLists(string text, bool isInsideParagraphlessListItem = false) { // We use a different prefix before nested lists than top-level lists. // See extended comment in _ProcessListItems(). if (_listLevel > 0) text = _listNested.Replace(text, GetListEvaluator(isInsideParagraphlessListItem)); else text = _listTopLevel.Replace(text, GetListEvaluator(false)); return text; } private MatchEvaluator GetListEvaluator(bool isInsideParagraphlessListItem = false) { return new MatchEvaluator(match => { string list = match.Groups[1].Value; string listType = Regex.IsMatch(match.Groups[3].Value, _markerUL) ? "ul" : "ol"; string result; result = ProcessListItems(list, listType == "ul" ? _markerUL : _markerOL, isInsideParagraphlessListItem); result = string.Format("<{0}>\n{1}\n", listType, result); return result; }); } /// /// Process the contents of a single ordered or unordered list, splitting it /// into individual list items. /// private string ProcessListItems(string list, string marker, bool isInsideParagraphlessListItem = false) { // The listLevel global keeps track of when we're inside a list. // Each time we enter a list, we increment it; when we leave a list, // we decrement. If it's zero, we're not in a list anymore. // We do this because when we're not inside a list, we want to treat // something like this: // I recommend upgrading to version // 8. Oops, now this line is treated // as a sub-list. // As a single paragraph, despite the fact that the second line starts // with a digit-period-space sequence. // Whereas when we're inside a list (or sub-list), that line will be // treated as the start of a sub-list. What a kludge, huh? This is // an aspect of Markdown's syntax that's hard to parse perfectly // without resorting to mind-reading. Perhaps the solution is to // change the syntax rules such that sub-lists must start with a // starting cardinal number; e.g. "1." or "a.". _listLevel++; // Trim trailing blank lines: list = Regex.Replace(list, @"\n{2,}\z", "\n"); string pattern = string.Format( @"(^[ ]*) # leading whitespace = $1 ({0}) [ ]+ # list marker = $2 ((?s:.+?) # list item text = $3 (\n+)) (?= (\z | \1 ({0}) [ ]+))", marker); bool lastItemHadADoubleNewline = false; // has to be a closure, so subsequent invocations can share the bool MatchEvaluator ListItemEvaluator = (Match match) => { string item = match.Groups[3].Value; bool endsWithDoubleNewline = item.EndsWith("\n\n"); bool containsDoubleNewline = endsWithDoubleNewline || item.Contains("\n\n"); if (containsDoubleNewline || lastItemHadADoubleNewline) // we could correct any bad indentation here.. item = RunBlockGamut(Outdent(item) + "\n", unhash: false); else { // recursion for sub-lists item = DoLists(Outdent(item), isInsideParagraphlessListItem: true); item = item.TrimEnd('\n'); if (!isInsideParagraphlessListItem) // only the outer-most item should run this, otherwise it's run multiple times for the inner ones item = RunSpanGamut(item); } lastItemHadADoubleNewline = endsWithDoubleNewline; return string.Format("
  • {0}
  • \n", item); }; list = Regex.Replace(list, pattern, new MatchEvaluator(ListItemEvaluator), RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline); _listLevel--; return list; } private static Regex _codeBlock = new Regex(string.Format(@" (?:\n\n|\A\n?) ( # $1 = the code block -- one or more lines, starting with a space (?: (?:[ ]{{{0}}}) # Lines must start with a tab-width of spaces .*\n+ )+ ) ((?=^[ ]{{0,{0}}}[^ \t\n])|\Z) # Lookahead for non-space at line-start, or end of doc", _tabWidth), RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); /// /// /// Turn Markdown 4-space indented code into HTML pre code blocks /// private string DoCodeBlocks(string text) { text = _codeBlock.Replace(text, new MatchEvaluator(CodeBlockEvaluator)); return text; } private string CodeBlockEvaluator(Match match) { string codeBlock = match.Groups[1].Value; codeBlock = EncodeCode(Outdent(codeBlock)); codeBlock = _newlinesLeadingTrailing.Replace(codeBlock, ""); return string.Concat("\n\n
    ", codeBlock, "\n
    \n\n"); } private static Regex _codeSpan = new Regex(@" (? /// Turn Markdown `code spans` into HTML code tags /// private string DoCodeSpans(string text) { // * You can use multiple backticks as the delimiters if you want to // include literal backticks in the code span. So, this input: // // Just type ``foo `bar` baz`` at the prompt. // // Will translate to: // //

    Just type foo `bar` baz at the prompt.

    // // There's no arbitrary limit to the number of backticks you // can use as delimters. If you need three consecutive backticks // in your code, use four for delimiters, etc. // // * You can use spaces to get literal backticks at the edges: // // ... type `` `bar` `` ... // // Turns to: // // ... type `bar` ... // return _codeSpan.Replace(text, new MatchEvaluator(CodeSpanEvaluator)); } private string CodeSpanEvaluator(Match match) { string span = match.Groups[2].Value; span = Regex.Replace(span, @"^[ ]*", ""); // leading whitespace span = Regex.Replace(span, @"[ ]*$", ""); // trailing whitespace span = EncodeCode(span); span = SaveFromAutoLinking(span); // to prevent auto-linking. Not necessary in code *blocks*, but in code spans. return string.Concat("", span, ""); } private static Regex _bold = new Regex(@"(\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1", RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.Compiled); private static Regex _strictBold = new Regex(@"(^|[\W_])(?:(?!\1)|(?=^))(\*|_)\2(?=\S)(.*?\S)\2\2(?!\2)(?=[\W_]|$)", RegexOptions.Singleline | RegexOptions.Compiled); private static Regex _italic = new Regex(@"(\*|_) (?=\S) (.+?) (?<=\S) \1", RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.Compiled); private static Regex _strictItalic = new Regex(@"(^|[\W_])(?:(?!\1)|(?=^))(\*|_)(?=\S)((?:(?!\2).)*?\S)\2(?!\2)(?=[\W_]|$)", RegexOptions.Singleline | RegexOptions.Compiled); /// /// Turn Markdown *italics* and **bold** into HTML strong and em tags /// private string DoItalicsAndBold(string text) { // must go first, then if (_strictBoldItalic) { text = _strictBold.Replace(text, "$1$3"); text = _strictItalic.Replace(text, "$1$3"); } else { text = _bold.Replace(text, "$2"); text = _italic.Replace(text, "$2"); } return text; } /// /// Turn markdown line breaks (two space at end of line) into HTML break tags /// private string DoHardBreaks(string text) { if (_autoNewlines) text = Regex.Replace(text, @"\n", string.Format("[ ]? # '>' at the start of a line .+\n # rest of the first line (.+\n)* # subsequent consecutive lines \n* # blanks )+ )", RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline | RegexOptions.Compiled); /// /// Turn Markdown > quoted blocks into HTML blockquote blocks /// private string DoBlockQuotes(string text) { return _blockquote.Replace(text, new MatchEvaluator(BlockQuoteEvaluator)); } private string BlockQuoteEvaluator(Match match) { string bq = match.Groups[1].Value; bq = Regex.Replace(bq, @"^[ ]*>[ ]?", "", RegexOptions.Multiline); // trim one level of quoting bq = Regex.Replace(bq, @"^[ ]+$", "", RegexOptions.Multiline); // trim whitespace-only lines bq = RunBlockGamut(bq); // recurse bq = Regex.Replace(bq, @"^", " ", RegexOptions.Multiline); // These leading spaces screw with
     content, so we need to fix that:
                bq = Regex.Replace(bq, @"(\s*
    .+?
    )", new MatchEvaluator(BlockQuoteEvaluator2), RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline); bq = string.Format("
    \n{0}\n
    ", bq); string key = GetHashKey(bq, isHtmlBlock: true); _htmlBlocks[key] = bq; return "\n\n" + key + "\n\n"; } private string BlockQuoteEvaluator2(Match match) { return Regex.Replace(match.Groups[1].Value, @"^ ", "", RegexOptions.Multiline); } private const string _charInsideUrl = @"[-A-Z0-9+&@#/%?=~_|\[\]\(\)!:,\.;" + "\x1a]"; private const string _charEndingUrl = "[-A-Z0-9+&@#/%=~_|\\[\\])]"; private static Regex _autolinkBare = new Regex(@"(<|="")?\b(https?|ftp)(://" + _charInsideUrl + "*" + _charEndingUrl + ")(?=$|\\W)", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static Regex _endCharRegex = new Regex(_charEndingUrl, RegexOptions.IgnoreCase | RegexOptions.Compiled); private static string handleTrailingParens(Match match) { // The first group is essentially a negative lookbehind -- if there's a < or a =", we don't touch this. // We're not using a *real* lookbehind, because of links with in links, like // With a real lookbehind, the full link would never be matched, and thus the http://www.google.com *would* be matched. // With the simulated lookbehind, the full link *is* matched (just not handled, because of this early return), causing // the google link to not be matched again. if (match.Groups[1].Success) return match.Value; var protocol = match.Groups[2].Value; var link = match.Groups[3].Value; if (!link.EndsWith(")")) return "<" + protocol + link + ">"; var level = 0; foreach (Match c in Regex.Matches(link, "[()]")) { if (c.Value == "(") { if (level <= 0) level = 1; else level++; } else { level--; } } var tail = ""; if (level < 0) { link = Regex.Replace(link, @"\){1," + (-level) + "}$", m => { tail = m.Value; return ""; }); } if (tail.Length > 0) { var lastChar = link[link.Length - 1]; if (!_endCharRegex.IsMatch(lastChar.ToString())) { tail = lastChar + tail; link = link.Substring(0, link.Length - 1); } } return "<" + protocol + link + ">" + tail; } /// /// Turn angle-delimited URLs into HTML anchor tags /// /// /// <http://www.example.com> /// private string DoAutoLinks(string text) { if (_autoHyperlink) { // fixup arbitrary URLs by adding Markdown < > so they get linked as well // note that at this point, all other URL in the text are already hyperlinked as // *except* for the case text = _autolinkBare.Replace(text, handleTrailingParens); } // Hyperlinks: text = Regex.Replace(text, "<((https?|ftp):[^'\">\\s]+)>", new MatchEvaluator(HyperlinkEvaluator)); if (_linkEmails) { // Email addresses: string pattern = @"< (?:mailto:)? ( [-.\w]+ \@ [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ ) >"; text = Regex.Replace(text, pattern, new MatchEvaluator(EmailEvaluator), RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); } return text; } private string HyperlinkEvaluator(Match match) { string link = match.Groups[1].Value; return string.Format("{1}", EscapeBoldItalic(EncodeProblemUrlChars(link)), link); } private string EmailEvaluator(Match match) { string email = Unescape(match.Groups[1].Value); // // Input: an email address, e.g. "foo@example.com" // // Output: the email address as a mailto link, with each character // of the address encoded as either a decimal or hex entity, in // the hopes of foiling most address harvesting spam bots. E.g.: // // foo // @example.com // // Based by a filter by Matthew Wickline, posted to the BBEdit-Talk // mailing list: // email = "mailto:" + email; // leave ':' alone (to spot mailto: later) email = EncodeEmailAddress(email); email = string.Format("{0}", email); // strip the mailto: from the visible part email = Regex.Replace(email, "\">.+?:", "\">"); return email; } private static Regex _outDent = new Regex(@"^[ ]{1," + _tabWidth + @"}", RegexOptions.Multiline | RegexOptions.Compiled); /// /// Remove one level of line-leading spaces /// private string Outdent(string block) { return _outDent.Replace(block, ""); } #region Encoding and Normalization /// /// encodes email address randomly /// roughly 10% raw, 45% hex, 45% dec /// note that @ is always encoded and : never is /// private string EncodeEmailAddress(string addr) { var sb = new StringBuilder(addr.Length * 5); var rand = new Random(); int r; foreach (char c in addr) { r = rand.Next(1, 100); if ((r > 90 || c == ':') && c != '@') sb.Append(c); // m else if (r < 45) sb.AppendFormat("&#x{0:x};", (int)c); // m else sb.AppendFormat("&#{0};", (int)c); // m } return sb.ToString(); } private static Regex _codeEncoder = new Regex(@"&|<|>|\\|\*|_|\{|\}|\[|\]", RegexOptions.Compiled); /// /// Encode/escape certain Markdown characters inside code blocks and spans where they are literals /// private string EncodeCode(string code) { return _codeEncoder.Replace(code, EncodeCodeEvaluator); } private string EncodeCodeEvaluator(Match match) { switch (match.Value) { // Encode all ampersands; HTML entities are not // entities within a Markdown code span. case "&": return "&"; // Do the angle bracket song and dance case "<": return "<"; case ">": return ">"; // escape characters that are magic in Markdown default: return _escapeTable[match.Value]; } } private static Regex _amps = new Regex(@"&(?!((#[0-9]+)|(#[xX][a-fA-F0-9]+)|([a-zA-Z][a-zA-Z0-9]*));)", RegexOptions.ExplicitCapture | RegexOptions.Compiled); private static Regex _angles = new Regex(@"<(?![A-Za-z/?\$!])", RegexOptions.ExplicitCapture | RegexOptions.Compiled); /// /// Encode any ampersands (that aren't part of an HTML entity) and left or right angle brackets /// private string EncodeAmpsAndAngles(string s) { s = _amps.Replace(s, "&"); s = _angles.Replace(s, "<"); return s; } private static Regex _backslashEscapes; /// /// Encodes any escaped characters such as \`, \*, \[ etc /// private string EscapeBackslashes(string s) { return _backslashEscapes.Replace(s, new MatchEvaluator(EscapeBackslashesEvaluator)); } private string EscapeBackslashesEvaluator(Match match) { return _backslashEscapeTable[match.Value]; } private static Regex _unescapes = new Regex("\x1A" + "E\\d+E", RegexOptions.Compiled); /// /// swap back in all the special characters we've hidden /// private string Unescape(string s) { return _unescapes.Replace(s, new MatchEvaluator(UnescapeEvaluator)); } private string UnescapeEvaluator(Match match) { return _invertedEscapeTable[match.Value]; } /// /// escapes Bold [ * ] and Italic [ _ ] characters /// private string EscapeBoldItalic(string s) { s = s.Replace("*", _escapeTable["*"]); s = s.Replace("_", _escapeTable["_"]); return s; } private static string AttributeEncode(string s) { return s.Replace(">", ">").Replace("<", "<").Replace("\"", """); } private static readonly char[] _problemUrlChars = @"""'*()[]$:".ToCharArray(); /// /// hex-encodes some unusual "problem" chars in URLs to avoid URL detection problems /// private string EncodeProblemUrlChars(string url) { if (!_encodeProblemUrlCharacters) return url; var sb = new StringBuilder(url.Length); bool encode; char c; for (int i = 0; i < url.Length; i++) { c = url[i]; encode = Array.IndexOf(_problemUrlChars, c) != -1; if (encode && c == ':' && i < url.Length - 1) encode = !(url[i + 1] == '/') && !(url[i + 1] >= '0' && url[i + 1] <= '9'); if (encode) sb.Append("%" + String.Format("{0:x}", (byte)c)); else sb.Append(c); } return sb.ToString(); } /// /// Within tags -- meaning between < and > -- encode [\ ` * _] so they /// don't conflict with their use in Markdown for code, italics and strong. /// We're replacing each such character with its corresponding hash /// value; this is likely overkill, but it should prevent us from colliding /// with the escape values by accident. /// private string EscapeSpecialCharsWithinTagAttributes(string text) { var tokens = TokenizeHTML(text); // now, rebuild text from the tokens var sb = new StringBuilder(text.Length); foreach (var token in tokens) { string value = token.Value; if (token.Type == TokenType.Tag) { value = value.Replace(@"\", _escapeTable[@"\"]); if (_autoHyperlink && value.StartsWith("(?=.)", _escapeTable[@"`"]); value = EscapeBoldItalic(value); } sb.Append(value); } return sb.ToString(); } /// /// convert all tabs to _tabWidth spaces; /// standardizes line endings from DOS (CR LF) or Mac (CR) to UNIX (LF); /// makes sure text ends with a couple of newlines; /// removes any blank lines (only spaces) in the text /// private string Normalize(string text) { var output = new StringBuilder(text.Length); var line = new StringBuilder(); bool valid = false; for (int i = 0; i < text.Length; i++) { switch (text[i]) { case '\n': if (valid) output.Append(line); output.Append('\n'); line.Length = 0; valid = false; break; case '\r': if ((i < text.Length - 1) && (text[i + 1] != '\n')) { if (valid) output.Append(line); output.Append('\n'); line.Length = 0; valid = false; } break; case '\t': int width = (_tabWidth - line.Length % _tabWidth); for (int k = 0; k < width; k++) line.Append(' '); break; case '\x1A': break; default: if (!valid && text[i] != ' ') valid = true; line.Append(text[i]); break; } } if (valid) output.Append(line); output.Append('\n'); // add two newlines to the end before return return output.Append("\n\n").ToString(); } #endregion /// /// this is to emulate what's evailable in PHP /// private static string RepeatString(string text, int count) { var sb = new StringBuilder(text.Length * count); for (int i = 0; i < count; i++) sb.Append(text); return sb.ToString(); } } } ================================================ FILE: src/Squirrel/MsDeltaCompression.cs ================================================ #nullable enable using System; using System.ComponentModel; namespace Squirrel { internal class MsDeltaCompression { public void CreateDelta(string oldFilePath, string newFilePath, string deltaFilePath) { const string? sourceOptionsName = null; const string? targetOptionsName = null; var globalOptions = new DeltaInput(); var targetFileTime = IntPtr.Zero; if (!NativeMethods.CreateDelta( FileTypeSet.Executables, CreateFlags.IgnoreFileSizeLimit, CreateFlags.None, oldFilePath, newFilePath, sourceOptionsName, targetOptionsName, globalOptions, targetFileTime, HashAlgId.Crc32, deltaFilePath)) { throw new Win32Exception(); } } public void ApplyDelta(string deltaFilePath, string oldFilePath, string newFilePath) { if (!NativeMethods.ApplyDelta(ApplyFlags.AllowLegacy, oldFilePath, deltaFilePath, newFilePath)) throw new Win32Exception(); } } } ================================================ FILE: src/Squirrel/NativeMethods.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Squirrel { public static class NativeMethods { public static int GetParentProcessId() { var pbi = new PROCESS_BASIC_INFORMATION(); //Get a handle to our own process IntPtr hProc = OpenProcess((ProcessAccess)0x001F0FFF, false, Process.GetCurrentProcess().Id); try { int sizeInfoReturned; int queryStatus = NtQueryInformationProcess(hProc, (PROCESSINFOCLASS)0, ref pbi, pbi.Size, out sizeInfoReturned); } finally { if (!hProc.Equals(IntPtr.Zero)) { //Close handle and free allocated memory CloseHandle(hProc); hProc = IntPtr.Zero; } } return (int)pbi.InheritedFromUniqueProcessId; } [DllImport("version.dll", SetLastError = true)] [return:MarshalAs(UnmanagedType.Bool)] internal static extern bool GetFileVersionInfo( string lpszFileName, int dwHandleIgnored, int dwLen, [MarshalAs(UnmanagedType.LPArray)] byte[] lpData); [DllImport("version.dll", SetLastError = true)] internal static extern int GetFileVersionInfoSize( string lpszFileName, IntPtr dwHandleIgnored); [DllImport("version.dll")] [return:MarshalAs(UnmanagedType.Bool)] internal static extern bool VerQueryValue( byte[] pBlock, string pSubBlock, out IntPtr pValue, out int len); [DllImport("psapi.dll", SetLastError=true)] internal static extern bool EnumProcesses( IntPtr pProcessIds, // pointer to allocated DWORD array int cb, out int pBytesReturned); [DllImport("kernel32.dll", SetLastError=true)] internal static extern bool QueryFullProcessImageName( IntPtr hProcess, [In] int justPassZeroHere, [Out] StringBuilder lpImageFileName, [In] [MarshalAs(UnmanagedType.U4)] ref int nSize); [DllImport("kernel32.dll", SetLastError=true)] internal static extern IntPtr OpenProcess( ProcessAccess processAccess, bool bInheritHandle, int processId); [DllImport("kernel32.dll", SetLastError = true)] internal static extern bool CloseHandle(IntPtr hHandle); [DllImport("NTDLL.DLL", SetLastError=true)] internal static extern int NtQueryInformationProcess(IntPtr hProcess, PROCESSINFOCLASS pic, ref PROCESS_BASIC_INFORMATION pbi, int cb, out int pSize); [DllImport("kernel32.dll", SetLastError=true)] internal static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); [DllImport("kernel32.dll", EntryPoint = "GetStdHandle")] internal static extern IntPtr GetStdHandle(StandardHandles nStdHandle); [DllImport("kernel32.dll", EntryPoint = "AllocConsole")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool AllocConsole(); [DllImport("kernel32.dll")] internal static extern bool AttachConsole(int pid); [DllImport("Kernel32.dll", SetLastError=true)] internal static extern IntPtr BeginUpdateResource(string pFileName, bool bDeleteExistingResources); [DllImport("Kernel32.dll", SetLastError=true)] internal static extern bool UpdateResource(IntPtr handle, string pType, IntPtr pName, short language, [MarshalAs(UnmanagedType.LPArray)] byte[] pData, int dwSize); [DllImport("Kernel32.dll", SetLastError=true)] internal static extern bool EndUpdateResource(IntPtr handle, bool discard); #nullable enable /// /// The ApplyDelta function use the specified delta and source files to create a new copy of the target file. /// /// Either DELTA_FLAG_NONE or DELTA_APPLY_FLAG_ALLOW_PA19. /// The name of the source file to which the delta is to be applied. /// The name of the delta to be applied to the source file. /// The name of the target file that is to be created. /// /// Returns TRUE on success or FALSE otherwise. /// /// /// http://msdn.microsoft.com/en-us/library/bb417345.aspx#applydeltaaw /// [DllImport("msdelta.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool ApplyDelta( [MarshalAs(UnmanagedType.I8)] ApplyFlags applyFlags, string sourceName, string deltaName, string targetName); /// /// The CreateDelta function creates a delta from the specified source and target files and write the output delta to the designated file name. /// /// The file type set used for Create. /// The file type set used for Create. /// The file type set used for Create. /// The file type set used for Create. /// The name of the target against which the source is compared. /// Reserved. Pass NULL. /// Reserved. Pass NULL. /// Reserved. Pass a DELTA_INPUT structure with lpStart set to NULL and uSize set to 0. /// The time stamp set on the target file after delta Apply. If NULL, the timestamp of the target file during delta Create will be used. /// ALG_ID of the algorithm to be used to generate the target signature. /// The name of the delta file to be created. /// /// Returns TRUE on success or FALSE otherwise. /// /// /// http://msdn.microsoft.com/en-us/library/bb417345.aspx#createdeltaaw /// [DllImport("msdelta.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CreateDelta( [MarshalAs(UnmanagedType.I8)] FileTypeSet fileTypeSet, [MarshalAs(UnmanagedType.I8)] CreateFlags setFlags, [MarshalAs(UnmanagedType.I8)] CreateFlags resetFlags, string sourceName, string targetName, string? sourceOptionsName, string? targetOptionsName, DeltaInput globalOptions, IntPtr targetFileTime, [MarshalAs(UnmanagedType.U4)] HashAlgId hashAlgId, string deltaName); #nullable restore } [Flags] public enum ProcessAccess : uint { All = 0x001F0FFF, Terminate = 0x00000001, CreateThread = 0x00000002, VirtualMemoryOperation = 0x00000008, VirtualMemoryRead = 0x00000010, VirtualMemoryWrite = 0x00000020, DuplicateHandle = 0x00000040, CreateProcess = 0x000000080, SetQuota = 0x00000100, SetInformation = 0x00000200, QueryInformation = 0x00000400, QueryLimitedInformation = 0x00001000, Synchronize = 0x00100000 } public enum PROCESSINFOCLASS : int { ProcessBasicInformation = 0, // 0, q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATION ProcessQuotaLimits, // qs: QUOTA_LIMITS, QUOTA_LIMITS_EX ProcessIoCounters, // q: IO_COUNTERS ProcessVmCounters, // q: VM_COUNTERS, VM_COUNTERS_EX ProcessTimes, // q: KERNEL_USER_TIMES ProcessBasePriority, // s: KPRIORITY ProcessRaisePriority, // s: ULONG ProcessDebugPort, // q: HANDLE ProcessExceptionPort, // s: HANDLE ProcessAccessToken, // s: PROCESS_ACCESS_TOKEN ProcessLdtInformation, // 10 ProcessLdtSize, ProcessDefaultHardErrorMode, // qs: ULONG ProcessIoPortHandlers, // (kernel-mode only) ProcessPooledUsageAndLimits, // q: POOLED_USAGE_AND_LIMITS ProcessWorkingSetWatch, // q: PROCESS_WS_WATCH_INFORMATION[]; s: void ProcessUserModeIOPL, ProcessEnableAlignmentFaultFixup, // s: BOOLEAN ProcessPriorityClass, // qs: PROCESS_PRIORITY_CLASS ProcessWx86Information, ProcessHandleCount, // 20, q: ULONG, PROCESS_HANDLE_INFORMATION ProcessAffinityMask, // s: KAFFINITY ProcessPriorityBoost, // qs: ULONG ProcessDeviceMap, // qs: PROCESS_DEVICEMAP_INFORMATION, PROCESS_DEVICEMAP_INFORMATION_EX ProcessSessionInformation, // q: PROCESS_SESSION_INFORMATION ProcessForegroundInformation, // s: PROCESS_FOREGROUND_BACKGROUND ProcessWow64Information, // q: ULONG_PTR ProcessImageFileName, // q: UNICODE_STRING ProcessLUIDDeviceMapsEnabled, // q: ULONG ProcessBreakOnTermination, // qs: ULONG ProcessDebugObjectHandle, // 30, q: HANDLE ProcessDebugFlags, // qs: ULONG ProcessHandleTracing, // q: PROCESS_HANDLE_TRACING_QUERY; s: size 0 disables, otherwise enables ProcessIoPriority, // qs: ULONG ProcessExecuteFlags, // qs: ULONG ProcessResourceManagement, ProcessCookie, // q: ULONG ProcessImageInformation, // q: SECTION_IMAGE_INFORMATION ProcessCycleTime, // q: PROCESS_CYCLE_TIME_INFORMATION ProcessPagePriority, // q: ULONG ProcessInstrumentationCallback, // 40 ProcessThreadStackAllocation, // s: PROCESS_STACK_ALLOCATION_INFORMATION, PROCESS_STACK_ALLOCATION_INFORMATION_EX ProcessWorkingSetWatchEx, // q: PROCESS_WS_WATCH_INFORMATION_EX[] ProcessImageFileNameWin32, // q: UNICODE_STRING ProcessImageFileMapping, // q: HANDLE (input) ProcessAffinityUpdateMode, // qs: PROCESS_AFFINITY_UPDATE_MODE ProcessMemoryAllocationMode, // qs: PROCESS_MEMORY_ALLOCATION_MODE ProcessGroupInformation, // q: USHORT[] ProcessTokenVirtualizationEnabled, // s: ULONG ProcessConsoleHostProcess, // q: ULONG_PTR ProcessWindowInformation, // 50, q: PROCESS_WINDOW_INFORMATION ProcessHandleInformation, // q: PROCESS_HANDLE_SNAPSHOT_INFORMATION // since WIN8 ProcessMitigationPolicy, // s: PROCESS_MITIGATION_POLICY_INFORMATION ProcessDynamicFunctionTableInformation, ProcessHandleCheckingMode, ProcessKeepAliveCount, // q: PROCESS_KEEPALIVE_COUNT_INFORMATION ProcessRevokeFileHandles, // s: PROCESS_REVOKE_FILE_HANDLES_INFORMATION MaxProcessInfoClass }; [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct PROCESS_BASIC_INFORMATION { public IntPtr ExitStatus; public IntPtr PebBaseAddress; public IntPtr AffinityMask; public IntPtr BasePriority; public UIntPtr UniqueProcessId; public IntPtr InheritedFromUniqueProcessId; public int Size { get { return (int)Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)); } } } public enum StandardHandles : int { STD_INPUT_HANDLE = -10, STD_OUTPUT_HANDLE = -11, STD_ERROR_HANDLE = -12, } /// /// http://msdn.microsoft.com/en-us/library/bb417345.aspx#deltaflagtypeflags /// internal enum ApplyFlags : long { /// Indicates no special handling. None = 0, /// Allow MSDelta to apply deltas created using PatchAPI. AllowLegacy = 1, } /// /// http://msdn.microsoft.com/en-us/library/bb417345.aspx#filetypesets /// [Flags] internal enum FileTypeSet : long { /// /// File type set that includes I386, IA64 and AMD64 Portable Executable (PE) files. Others are treated as raw. /// Executables = 0x0FL, } /// /// http://msdn.microsoft.com/en-us/library/bb417345.aspx#deltaflagtypeflags /// internal enum CreateFlags : long { /// Indicates no special handling. None = 0, /// Allow the source, target and delta files to exceed the default size limit. IgnoreFileSizeLimit = 1 << 17, } /// /// http://msdn.microsoft.com/en-us/library/bb417345.aspx#deltainputstructure /// [StructLayout(LayoutKind.Sequential)] internal struct DeltaInput { /// Memory address non-editable input buffer. public IntPtr Start; /// Size of the memory buffer in bytes. public IntPtr Size; /// /// Defines whether MSDelta is allowed to edit the input buffer. If you make the input editable, the buffer will /// be zeroed at function return. However this will cause most MSDelta functions to use less memory. /// [MarshalAs(UnmanagedType.Bool)] public bool Editable; } internal enum HashAlgId { /// No signature. None = 0, /// 32-bit CRC defined in msdelta.dll. Crc32 = 32, } } ================================================ FILE: src/Squirrel/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; [assembly: ComVisible(false)] [assembly: InternalsVisibleTo("Squirrel.Tests")] [assembly: InternalsVisibleTo("Update")] [assembly: InternalsVisibleTo("Update-Mono")] [assembly: InternalsVisibleTo("SyncReleases")] ================================================ FILE: src/Squirrel/ReleaseEntry.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using NuGet; using Squirrel.SimpleSplat; using System.Runtime.Serialization; using System.Threading.Tasks; using System.Collections.Concurrent; namespace Squirrel { public interface IReleaseEntry { string SHA1 { get; } string Filename { get; } long Filesize { get; } bool IsDelta { get; } string EntryAsString { get; } SemanticVersion Version { get; } string PackageName { get; } float? StagingPercentage { get; } string GetReleaseNotes(string packageDirectory); Uri GetIconUrl(string packageDirectory); } [DataContract] public class ReleaseEntry : IEnableLogger, IReleaseEntry { [DataMember] public string SHA1 { get; protected set; } [DataMember] public string BaseUrl { get; protected set; } [DataMember] public string Filename { get; protected set; } [DataMember] public string Query { get; protected set; } [DataMember] public long Filesize { get; protected set; } [DataMember] public bool IsDelta { get; protected set; } [DataMember] public float? StagingPercentage { get; protected set; } protected ReleaseEntry(string sha1, string filename, long filesize, bool isDelta, string baseUrl = null, string query = null, float? stagingPercentage = null) { Contract.Requires(sha1 != null && sha1.Length == 40); Contract.Requires(filename != null); Contract.Requires(filename.Contains(Path.DirectorySeparatorChar) == false); Contract.Requires(filesize > 0); SHA1 = sha1; BaseUrl = baseUrl; Filename = filename; Query = query; Filesize = filesize; IsDelta = isDelta; StagingPercentage = stagingPercentage; } [IgnoreDataMember] public string EntryAsString { get { if (StagingPercentage != null) { return String.Format("{0} {1}{2} {3} # {4}", SHA1, BaseUrl, Filename, Filesize, stagingPercentageAsString(StagingPercentage.Value)); } else { return String.Format("{0} {1}{2} {3}", SHA1, BaseUrl, Filename, Filesize); } } } [IgnoreDataMember] public SemanticVersion Version { get { return Filename.ToSemanticVersion(); } } static readonly Regex packageNameRegex = new Regex(@"^([\w-]+)-\d+\..+\.nupkg$"); [IgnoreDataMember] public string PackageName { get { var match = packageNameRegex.Match(Filename); return match.Success ? match.Groups[1].Value : Filename.Substring(0, Filename.IndexOfAny(new[] { '-', '.' })); } } public string GetReleaseNotes(string packageDirectory) { var zp = new ZipPackage(Path.Combine(packageDirectory, Filename)); var t = zp.Id; if (String.IsNullOrWhiteSpace(zp.ReleaseNotes)) { throw new Exception(String.Format("Invalid 'ReleaseNotes' value in nuspec file at '{0}'", Path.Combine(packageDirectory, Filename))); } return zp.ReleaseNotes; } public Uri GetIconUrl(string packageDirectory) { var zp = new ZipPackage(Path.Combine(packageDirectory, Filename)); return zp.IconUrl; } static readonly Regex entryRegex = new Regex(@"^([0-9a-fA-F]{40})\s+(\S+)\s+(\d+)[\r]*$"); static readonly Regex commentRegex = new Regex(@"\s*#.*$"); static readonly Regex stagingRegex = new Regex(@"#\s+(\d{1,3})%$"); public static ReleaseEntry ParseReleaseEntry(string entry) { Contract.Requires(entry != null); float? stagingPercentage = null; var m = stagingRegex.Match(entry); if (m != null && m.Success) { stagingPercentage = Single.Parse(m.Groups[1].Value) / 100.0f; } entry = commentRegex.Replace(entry, ""); if (String.IsNullOrWhiteSpace(entry)) { return null; } m = entryRegex.Match(entry); if (!m.Success) { throw new Exception("Invalid release entry: " + entry); } if (m.Groups.Count != 4) { throw new Exception("Invalid release entry: " + entry); } string filename = m.Groups[2].Value; // Split the base URL and the filename if an URI is provided, // throws if a path is provided string baseUrl = null; string query = null; if(Utility.IsHttpUrl(filename)) { var uri = new Uri(filename); var path = uri.LocalPath; var authority = uri.GetLeftPart(UriPartial.Authority); if (String.IsNullOrEmpty(path) || String.IsNullOrEmpty(authority)) { throw new Exception("Invalid URL"); } var indexOfLastPathSeparator = path.LastIndexOf("/") + 1; baseUrl = authority + path.Substring(0, indexOfLastPathSeparator); filename = path.Substring(indexOfLastPathSeparator); if (!String.IsNullOrEmpty(uri.Query)) { query = uri.Query; } } if (filename.IndexOfAny(Path.GetInvalidFileNameChars()) > -1) { throw new Exception("Filename can either be an absolute HTTP[s] URL, *or* a file name"); } long size = Int64.Parse(m.Groups[3].Value); bool isDelta = filenameIsDeltaFile(filename); return new ReleaseEntry(m.Groups[1].Value, filename, size, isDelta, baseUrl, query, stagingPercentage); } public bool IsStagingMatch(Guid? userId) { // A "Staging match" is when a user falls into the affirmative // bucket - i.e. if the staging is at 10%, this user is the one out // of ten case. if (!StagingPercentage.HasValue) return true; if (!userId.HasValue) return false; uint val = BitConverter.ToUInt32(userId.Value.ToByteArray(), 12); double percentage = ((double)val / (double)UInt32.MaxValue); return percentage < StagingPercentage.Value; } public static IEnumerable ParseReleaseFile(string fileContents) { if (String.IsNullOrEmpty(fileContents)) { return new ReleaseEntry[0]; } fileContents = Utility.RemoveByteOrderMarkerIfPresent(fileContents); var ret = fileContents.Split('\n') .Where(x => !String.IsNullOrWhiteSpace(x)) .Select(ParseReleaseEntry) .Where(x => x != null) .ToArray(); return ret.Any(x => x == null) ? null : ret; } public static IEnumerable ParseReleaseFileAndApplyStaging(string fileContents, Guid? userToken) { if (String.IsNullOrEmpty(fileContents)) { return new ReleaseEntry[0]; } fileContents = Utility.RemoveByteOrderMarkerIfPresent(fileContents); var ret = fileContents.Split('\n') .Where(x => !String.IsNullOrWhiteSpace(x)) .Select(ParseReleaseEntry) .Where(x => x != null && x.IsStagingMatch(userToken)) .ToArray(); return ret.Any(x => x == null) ? null : ret; } public static void WriteReleaseFile(IEnumerable releaseEntries, Stream stream) { Contract.Requires(releaseEntries != null && releaseEntries.Any()); Contract.Requires(stream != null); using (var sw = new StreamWriter(stream, Encoding.UTF8)) { sw.Write(String.Join("\n", releaseEntries .OrderBy(x => x.Version) .ThenByDescending(x => x.IsDelta) .Select(x => x.EntryAsString))); } } public static void WriteReleaseFile(IEnumerable releaseEntries, string path) { Contract.Requires(releaseEntries != null && releaseEntries.Any()); Contract.Requires(!String.IsNullOrEmpty(path)); using (var f = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None)) { WriteReleaseFile(releaseEntries, f); } } public static ReleaseEntry GenerateFromFile(Stream file, string filename, string baseUrl = null) { Contract.Requires(file != null && file.CanRead); Contract.Requires(!String.IsNullOrEmpty(filename)); var hash = Utility.CalculateStreamSHA1(file); return new ReleaseEntry(hash, filename, file.Length, filenameIsDeltaFile(filename), baseUrl); } public static ReleaseEntry GenerateFromFile(string path, string baseUrl = null) { using (var inf = File.OpenRead(path)) { return GenerateFromFile(inf, Path.GetFileName(path), baseUrl); } } public static List BuildReleasesFile(string releasePackagesDir) { var packagesDir = new DirectoryInfo(releasePackagesDir); // Generate release entries for all of the local packages var entriesQueue = new ConcurrentQueue(); Parallel.ForEach(packagesDir.GetFiles("*.nupkg"), x => { using (var file = x.OpenRead()) { entriesQueue.Enqueue(GenerateFromFile(file, x.Name)); } }); // Write the new RELEASES file to a temp file then move it into // place var entries = entriesQueue.ToList(); var tempFile = default(string); Utility.WithTempFile(out tempFile, releasePackagesDir); try { using (var of = File.OpenWrite(tempFile)) { if (entries.Count > 0) WriteReleaseFile(entries, of); } var target = Path.Combine(packagesDir.FullName, "RELEASES"); if (File.Exists(target)) { File.Delete(target); } File.Move(tempFile, target); } finally { if (File.Exists(tempFile)) Utility.DeleteFileHarder(tempFile, true); } return entries; } static string stagingPercentageAsString(float percentage) { return String.Format("{0:F0}%", percentage * 100.0); } static bool filenameIsDeltaFile(string filename) { return filename.EndsWith("-delta.nupkg", StringComparison.InvariantCultureIgnoreCase); } public static ReleasePackage GetPreviousRelease(IEnumerable releaseEntries, IReleasePackage package, string targetDir) { if (releaseEntries == null || !releaseEntries.Any()) return null; return releaseEntries .Where(x => x.IsDelta == false) .Where(x => x.Version < package.ToSemanticVersion()) .OrderByDescending(x => x.Version) .Select(x => new ReleasePackage(Path.Combine(targetDir, x.Filename), true)) .FirstOrDefault(); } } } ================================================ FILE: src/Squirrel/ReleaseExtensions.cs ================================================ using NuGet; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; namespace Squirrel { public static class VersionExtensions { static readonly Regex _suffixRegex = new Regex(@"(-full|-delta)?\.nupkg$", RegexOptions.Compiled); static readonly Regex _versionRegex = new Regex(@"\d+(\.\d+){0,3}(-[A-Za-z][0-9A-Za-z-]*)?$", RegexOptions.Compiled); public static SemanticVersion ToSemanticVersion(this IReleasePackage package) { return package.InputPackageFile.ToSemanticVersion(); } public static SemanticVersion ToSemanticVersion(this string fileName) { var name = _suffixRegex.Replace(fileName, ""); var version = _versionRegex.Match(name).Value; return new SemanticVersion(version); } } } ================================================ FILE: src/Squirrel/ReleasePackage.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel.Design; using System.Diagnostics.Contracts; using System.Globalization; using System.IO; using System.Linq; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Xml; using MarkdownSharp; using NuGet; using Squirrel.SimpleSplat; using System.Threading.Tasks; using SharpCompress.Archives.Zip; using SharpCompress.Readers; namespace Squirrel { internal static class FrameworkTargetVersion { public static FrameworkName Net40 = new FrameworkName(".NETFramework,Version=v4.0"); public static FrameworkName Net45 = new FrameworkName(".NETFramework,Version=v4.5"); } public interface IReleasePackage { string InputPackageFile { get; } string ReleasePackageFile { get; } string SuggestedReleaseFileName { get; } string CreateReleasePackage(string outputFile, string packagesRootDir = null, Func releaseNotesProcessor = null, Action contentsPostProcessHook = null); } public static class VersionComparer { public static bool Matches(IVersionSpec versionSpec, SemanticVersion version) { if (versionSpec == null) return true; // I CAN'T DEAL WITH THIS bool minVersion; if (versionSpec.MinVersion == null) { minVersion = true; // no preconditon? LET'S DO IT } else if (versionSpec.IsMinInclusive) { minVersion = version >= versionSpec.MinVersion; } else { minVersion = version > versionSpec.MinVersion; } bool maxVersion; if (versionSpec.MaxVersion == null) { maxVersion = true; // no preconditon? LET'S DO IT } else if (versionSpec.IsMaxInclusive) { maxVersion = version <= versionSpec.MaxVersion; } else { maxVersion = version < versionSpec.MaxVersion; } return maxVersion && minVersion; } } public class ReleasePackage : IEnableLogger, IReleasePackage { public ReleasePackage(string inputPackageFile, bool isReleasePackage = false) { InputPackageFile = inputPackageFile; if (isReleasePackage) { ReleasePackageFile = inputPackageFile; } } public string InputPackageFile { get; protected set; } public string ReleasePackageFile { get; protected set; } public string SuggestedReleaseFileName { get { var zp = new ZipPackage(InputPackageFile); return String.Format("{0}-{1}-full.nupkg", zp.Id, zp.Version); } } public SemanticVersion Version { get { return InputPackageFile.ToSemanticVersion(); } } public string CreateReleasePackage(string outputFile, string packagesRootDir = null, Func releaseNotesProcessor = null, Action contentsPostProcessHook = null) { Contract.Requires(!String.IsNullOrEmpty(outputFile)); releaseNotesProcessor = releaseNotesProcessor ?? (x => (new Markdown()).Transform(x)); if (ReleasePackageFile != null) { return ReleasePackageFile; } var package = new ZipPackage(InputPackageFile); var dontcare = default(SemanticVersion); // NB: Our test fixtures use packages that aren't SemVer compliant, // we don't really care that they aren't valid if (!ModeDetector.InUnitTestRunner() && !SemanticVersion.TryParseStrict(package.Version.ToString(), out dontcare)) { throw new Exception( String.Format( "Your package version is currently {0}, which is *not* SemVer-compatible, change this to be a SemVer version number", package.Version.ToString())); } // we can tell from here what platform(s) the package targets // but given this is a simple package we only // ever expect one entry here (crash hard otherwise) var frameworks = package.GetSupportedFrameworks(); if (frameworks.Count() > 1) { var platforms = frameworks .Aggregate(new StringBuilder(), (sb, f) => sb.Append(f.ToString() + "; ")); throw new InvalidOperationException(String.Format( "The input package file {0} targets multiple platforms - {1} - and cannot be transformed into a release package.", InputPackageFile, platforms)); } else if (!frameworks.Any()) { throw new InvalidOperationException(String.Format( "The input package file {0} targets no platform and cannot be transformed into a release package.", InputPackageFile)); } var targetFramework = frameworks.Single(); // Recursively walk the dependency tree and extract all of the // dependent packages into the a temporary directory this.Log().Info("Creating release package: {0} => {1}", InputPackageFile, outputFile); var dependencies = findAllDependentPackages( package, new LocalPackageRepository(packagesRootDir), frameworkName: targetFramework) .ToArray(); string tempPath = null; using (Utility.WithTempDirectory(out tempPath, null)) { var tempDir = new DirectoryInfo(tempPath); extractZipWithEscaping(InputPackageFile, tempPath).Wait(); this.Log().Info("Extracting dependent packages: [{0}]", String.Join(",", dependencies.Select(x => x.Id))); extractDependentPackages(dependencies, tempDir, targetFramework); var specPath = tempDir.GetFiles("*.nuspec").First().FullName; this.Log().Info("Removing unnecessary data"); removeDependenciesFromPackageSpec(specPath); if (releaseNotesProcessor != null) { renderReleaseNotesMarkdown(specPath, releaseNotesProcessor); } addDeltaFilesToContentTypes(tempDir.FullName); contentsPostProcessHook?.Invoke(tempPath); Utility.CreateZipFromDirectory(outputFile, tempPath).Wait(); ReleasePackageFile = outputFile; return ReleasePackageFile; } } static Task extractZipWithEscaping(string zipFilePath, string outFolder) { return Task.Run(() => { using (var za = ZipArchive.Open(zipFilePath)) using (var reader = za.ExtractAllEntries()) { while (reader.MoveToNextEntry()) { var parts = reader.Entry.Key.Split('\\', '/').Select(x => Uri.UnescapeDataString(x)); var decoded = String.Join(Path.DirectorySeparatorChar.ToString(), parts); var fullTargetFile = Path.Combine(outFolder, decoded); var fullTargetDir = Path.GetDirectoryName(fullTargetFile); Directory.CreateDirectory(fullTargetDir); Utility.Retry(() => { if (reader.Entry.IsDirectory) { Directory.CreateDirectory(Path.Combine(outFolder, decoded)); } else { reader.WriteEntryToFile(Path.Combine(outFolder, decoded)); } }, 5); } } }); } public static Task ExtractZipForInstall(string zipFilePath, string outFolder, string rootPackageFolder) { return ExtractZipForInstall(zipFilePath, outFolder, rootPackageFolder, x => { }); } public static Task ExtractZipForInstall(string zipFilePath, string outFolder, string rootPackageFolder, Action progress) { var re = new Regex(@"lib[\\\/][^\\\/]*[\\\/]", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); return Task.Run(() => { using (var za = ZipArchive.Open(zipFilePath)) using (var reader = za.ExtractAllEntries()) { var totalItems = za.Entries.Count; var currentItem = 0; while (reader.MoveToNextEntry()) { // Report progress early since we might be need to continue for non-matches currentItem++; var percentage = (currentItem * 100d) / totalItems; progress((int)percentage); var parts = reader.Entry.Key.Split('\\', '/'); var decoded = String.Join(Path.DirectorySeparatorChar.ToString(), parts); if (!re.IsMatch(decoded)) continue; decoded = re.Replace(decoded, "", 1); var fullTargetFile = Path.Combine(outFolder, decoded); var fullTargetDir = Path.GetDirectoryName(fullTargetFile); Directory.CreateDirectory(fullTargetDir); var failureIsOkay = false; if (!reader.Entry.IsDirectory && decoded.Contains("_ExecutionStub.exe")) { // NB: On upgrade, many of these stubs will be in-use, nbd tho. failureIsOkay = true; fullTargetFile = Path.Combine( rootPackageFolder, Path.GetFileName(decoded).Replace("_ExecutionStub.exe", ".exe")); LogHost.Default.Info("Rigging execution stub for {0} to {1}", decoded, fullTargetFile); } try { Utility.Retry(() => { if (reader.Entry.IsDirectory) { Directory.CreateDirectory(fullTargetFile); } else { reader.WriteEntryToFile(fullTargetFile); } }, 5); } catch (Exception e) { if (!failureIsOkay) throw; LogHost.Default.WarnException("Can't write execution stub, probably in use", e); } } } progress(100); }); } void extractDependentPackages(IEnumerable dependencies, DirectoryInfo tempPath, FrameworkName framework) { dependencies.ForEach(pkg => { this.Log().Info("Scanning {0}", pkg.Id); pkg.GetLibFiles().ForEach(file => { var outPath = new FileInfo(Path.Combine(tempPath.FullName, file.Path)); if (!VersionUtility.IsCompatible(framework , new[] { file.TargetFramework })) { this.Log().Info("Ignoring {0} as the target framework is not compatible", outPath); return; } Directory.CreateDirectory(outPath.Directory.FullName); using (var of = File.Create(outPath.FullName)) { this.Log().Info("Writing {0} to {1}", file.Path, outPath); file.GetStream().CopyTo(of); } }); }); } void renderReleaseNotesMarkdown(string specPath, Func releaseNotesProcessor) { var doc = new XmlDocument(); doc.Load(specPath); // XXX: This code looks full tart var metadata = doc.DocumentElement.ChildNodes .OfType() .First(x => x.Name.ToLowerInvariant() == "metadata"); var releaseNotes = metadata.ChildNodes .OfType() .FirstOrDefault(x => x.Name.ToLowerInvariant() == "releasenotes"); if (releaseNotes == null) { this.Log().Info("No release notes found in {0}", specPath); return; } releaseNotes.InnerText = String.Format("", releaseNotesProcessor(releaseNotes.InnerText)); doc.Save(specPath); } void removeDependenciesFromPackageSpec(string specPath) { var xdoc = new XmlDocument(); xdoc.Load(specPath); var metadata = xdoc.DocumentElement.FirstChild; var dependenciesNode = metadata.ChildNodes.OfType().FirstOrDefault(x => x.Name.ToLowerInvariant() == "dependencies"); if (dependenciesNode != null) { metadata.RemoveChild(dependenciesNode); } xdoc.Save(specPath); } internal IEnumerable findAllDependentPackages( IPackage package = null, IPackageRepository packageRepository = null, HashSet packageCache = null, FrameworkName frameworkName = null) { package = package ?? new ZipPackage(InputPackageFile); packageCache = packageCache ?? new HashSet(); var deps = package.DependencySets .Where(x => x.TargetFramework == null || x.TargetFramework == frameworkName) .SelectMany(x => x.Dependencies); return deps.SelectMany(dependency => { var ret = matchPackage(packageRepository, dependency.Id, dependency.VersionSpec); if (ret == null) { var message = String.Format("Couldn't find file for package in {1}: {0}", dependency.Id, packageRepository.Source); this.Log().Error(message); throw new Exception(message); } if (packageCache.Contains(ret.GetFullName())) { return Enumerable.Empty(); } packageCache.Add(ret.GetFullName()); return findAllDependentPackages(ret, packageRepository, packageCache, frameworkName).StartWith(ret).Distinct(y => y.GetFullName()); }).ToArray(); } IPackage matchPackage(IPackageRepository packageRepository, string id, IVersionSpec version) { return packageRepository.FindPackagesById(id).FirstOrDefault(x => VersionComparer.Matches(version, x.Version)); } static internal void addDeltaFilesToContentTypes(string rootDirectory) { var doc = new XmlDocument(); var path = Path.Combine(rootDirectory, "[Content_Types].xml"); doc.Load(path); ContentType.Merge(doc); ContentType.Clean(doc); using (var sw = new StreamWriter(path, false, Encoding.UTF8)) { doc.Save(sw); } } } public class ChecksumFailedException : Exception { public string Filename { get; set; } } } ================================================ FILE: src/Squirrel/ShellFile.cs ================================================ using System; using System.Drawing; using System.Runtime.InteropServices; using System.Text; // All of this code is from http://vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects/Creating_and_Modifying_Shortcuts/article.asp namespace Squirrel.Shell { /// /// Summary description for ShellLink. /// public class ShellLink : IDisposable { [ComImport()] [Guid("0000010C-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IPersist { [PreserveSig] //[helpstring("Returns the class identifier for the component object")] void GetClassID(out Guid pClassID); } [ComImport()] [Guid("0000010B-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IPersistFile { // can't get this to go if I extend IPersist, so put it here: [PreserveSig] void GetClassID(out Guid pClassID); //[helpstring("Checks for changes since last file write")] void IsDirty(); //[helpstring("Opens the specified file and initializes the object from its contents")] void Load( [MarshalAs(UnmanagedType.LPWStr)] string pszFileName, uint dwMode); //[helpstring("Saves the object into the specified file")] void Save( [MarshalAs(UnmanagedType.LPWStr)] string pszFileName, [MarshalAs(UnmanagedType.Bool)] bool fRemember); //[helpstring("Notifies the object that save is completed")] void SaveCompleted( [MarshalAs(UnmanagedType.LPWStr)] string pszFileName); //[helpstring("Gets the current name of the file associated with the object")] void GetCurFile( [MarshalAs(UnmanagedType.LPWStr)] out string ppszFileName); } [StructLayout(LayoutKind.Sequential)] public struct PropVariant { public short variantType; public short Reserved1, Reserved2, Reserved3; public IntPtr pointerValue; public static PropVariant FromString(string str) { var pv = new PropVariant() { variantType = 31, // VT_LPWSTR pointerValue = Marshal.StringToCoTaskMemUni(str), }; return pv; } public static PropVariant FromGuid(Guid guid) { byte[] bytes = guid.ToByteArray(); var pv = new PropVariant() { variantType = 72, // VT_CLSID pointerValue = Marshal.AllocCoTaskMem(bytes.Length), }; Marshal.Copy(bytes, 0, pv.pointerValue, bytes.Length); return pv; } /// /// Called to properly clean up the memory referenced by a PropVariant instance. /// [DllImport("ole32.dll")] private extern static int PropVariantClear(ref PropVariant pvar); /// /// Called to clear the PropVariant's referenced and local memory. /// /// /// You must call Clear to avoid memory leaks. /// public void Clear() { // Can't pass "this" by ref, so make a copy to call PropVariantClear with PropVariant tmp = this; PropVariantClear(ref tmp); // Since we couldn't pass "this" by ref, we need to clear the member fields manually // NOTE: PropVariantClear already freed heap data for us, so we are just setting // our references to null. variantType = (short)VarEnum.VT_EMPTY; Reserved1 = Reserved2 = Reserved3 = 0; pointerValue = IntPtr.Zero; } } [StructLayout(LayoutKind.Sequential)] public struct PROPERTYKEY { public Guid fmtid; public UIntPtr pid; public static PROPERTYKEY PKEY_AppUserModel_ID { get { return new PROPERTYKEY() { fmtid = Guid.ParseExact("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", "B"), pid = new UIntPtr(5), }; } } public static PROPERTYKEY PKEY_AppUserModel_ToastActivatorCLSID { get { return new PROPERTYKEY() { fmtid = Guid.ParseExact("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", "B"), pid = new UIntPtr(26), }; } } } [ComImport] [Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IPropertyStore { [PreserveSig] int GetCount([Out] out uint cProps); [PreserveSig] int GetAt([In] uint iProp, out PROPERTYKEY pkey); [PreserveSig] int GetValue([In] ref PROPERTYKEY key, out PropVariant pv); [PreserveSig] int SetValue([In] ref PROPERTYKEY key, [In] ref PropVariant pv); [PreserveSig] int Commit(); } [ComImport()] [Guid("000214EE-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IShellLinkA { //[helpstring("Retrieves the path and filename of a shell link object")] void GetPath( [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszFile, int cchMaxPath, ref _WIN32_FIND_DATAA pfd, uint fFlags); //[helpstring("Retrieves the list of shell link item identifiers")] void GetIDList(out IntPtr ppidl); //[helpstring("Sets the list of shell link item identifiers")] void SetIDList(IntPtr pidl); //[helpstring("Retrieves the shell link description string")] void GetDescription( [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszFile, int cchMaxName); //[helpstring("Sets the shell link description string")] void SetDescription( [MarshalAs(UnmanagedType.LPStr)] string pszName); //[helpstring("Retrieves the name of the shell link working directory")] void GetWorkingDirectory( [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszDir, int cchMaxPath); //[helpstring("Sets the name of the shell link working directory")] void SetWorkingDirectory( [MarshalAs(UnmanagedType.LPStr)] string pszDir); //[helpstring("Retrieves the shell link command-line arguments")] void GetArguments( [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszArgs, int cchMaxPath); //[helpstring("Sets the shell link command-line arguments")] void SetArguments( [MarshalAs(UnmanagedType.LPStr)] string pszArgs); //[propget, helpstring("Retrieves or sets the shell link hot key")] void GetHotkey(out short pwHotkey); //[propput, helpstring("Retrieves or sets the shell link hot key")] void SetHotkey(short pwHotkey); //[propget, helpstring("Retrieves or sets the shell link show command")] void GetShowCmd(out uint piShowCmd); //[propput, helpstring("Retrieves or sets the shell link show command")] void SetShowCmd(uint piShowCmd); //[helpstring("Retrieves the location (path and index) of the shell link icon")] void GetIconLocation( [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); //[helpstring("Sets the location (path and index) of the shell link icon")] void SetIconLocation( [MarshalAs(UnmanagedType.LPStr)] string pszIconPath, int iIcon); //[helpstring("Sets the shell link relative path")] void SetRelativePath( [MarshalAs(UnmanagedType.LPStr)] string pszPathRel, uint dwReserved); //[helpstring("Resolves a shell link. The system searches for the shell link object and updates the shell link path and its list of identifiers (if necessary)")] void Resolve( IntPtr hWnd, uint fFlags); //[helpstring("Sets the shell link path and filename")] void SetPath( [MarshalAs(UnmanagedType.LPStr)] string pszFile); } [ComImport()] [Guid("000214F9-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IShellLinkW { //[helpstring("Retrieves the path and filename of a shell link object")] void GetPath( [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref _WIN32_FIND_DATAW pfd, uint fFlags); //[helpstring("Retrieves the list of shell link item identifiers")] void GetIDList(out IntPtr ppidl); //[helpstring("Sets the list of shell link item identifiers")] void SetIDList(IntPtr pidl); //[helpstring("Retrieves the shell link description string")] void GetDescription( [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxName); //[helpstring("Sets the shell link description string")] void SetDescription( [MarshalAs(UnmanagedType.LPWStr)] string pszName); //[helpstring("Retrieves the name of the shell link working directory")] void GetWorkingDirectory( [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); //[helpstring("Sets the name of the shell link working directory")] void SetWorkingDirectory( [MarshalAs(UnmanagedType.LPWStr)] string pszDir); //[helpstring("Retrieves the shell link command-line arguments")] void GetArguments( [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); //[helpstring("Sets the shell link command-line arguments")] void SetArguments( [MarshalAs(UnmanagedType.LPWStr)] string pszArgs); //[propget, helpstring("Retrieves or sets the shell link hot key")] void GetHotkey(out short pwHotkey); //[propput, helpstring("Retrieves or sets the shell link hot key")] void SetHotkey(short pwHotkey); //[propget, helpstring("Retrieves or sets the shell link show command")] void GetShowCmd(out uint piShowCmd); //[propput, helpstring("Retrieves or sets the shell link show command")] void SetShowCmd(uint piShowCmd); //[helpstring("Retrieves the location (path and index) of the shell link icon")] void GetIconLocation( [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); //[helpstring("Sets the location (path and index) of the shell link icon")] void SetIconLocation( [MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); //[helpstring("Sets the shell link relative path")] void SetRelativePath( [MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved); //[helpstring("Resolves a shell link. The system searches for the shell link object and updates the shell link path and its list of identifiers (if necessary)")] void Resolve( IntPtr hWnd, uint fFlags); //[helpstring("Sets the shell link path and filename")] void SetPath( [MarshalAs(UnmanagedType.LPWStr)] string pszFile); } [Guid("00021401-0000-0000-C000-000000000046")] [ClassInterface(ClassInterfaceType.None)] [ComImport()] class CShellLink { } enum EShellLinkGP : uint { SLGP_SHORTPATH = 1, SLGP_UNCPRIORITY = 2 } [Flags] enum EShowWindowFlags : uint { SW_HIDE = 0, SW_SHOWNORMAL = 1, SW_NORMAL = 1, SW_SHOWMINIMIZED = 2, SW_SHOWMAXIMIZED = 3, SW_MAXIMIZE = 3, SW_SHOWNOACTIVATE = 4, SW_SHOW = 5, SW_MINIMIZE = 6, SW_SHOWMINNOACTIVE = 7, SW_SHOWNA = 8, SW_RESTORE = 9, SW_SHOWDEFAULT = 10, SW_MAX = 10 } [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0, CharSet = CharSet.Unicode)] struct _WIN32_FIND_DATAW { public uint dwFileAttributes; public _FILETIME ftCreationTime; public _FILETIME ftLastAccessTime; public _FILETIME ftLastWriteTime; public uint nFileSizeHigh; public uint nFileSizeLow; public uint dwReserved0; public uint dwReserved1; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] // MAX_PATH public string cFileName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName; } [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0, CharSet = CharSet.Ansi)] struct _WIN32_FIND_DATAA { public uint dwFileAttributes; public _FILETIME ftCreationTime; public _FILETIME ftLastAccessTime; public _FILETIME ftLastWriteTime; public uint nFileSizeHigh; public uint nFileSizeLow; public uint dwReserved0; public uint dwReserved1; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] // MAX_PATH public string cFileName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName; } [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0)] struct _FILETIME { public uint dwLowDateTime; public uint dwHighDateTime; } class UnManagedMethods { [DllImport("Shell32", CharSet = CharSet.Auto)] internal static extern int ExtractIconEx( [MarshalAs(UnmanagedType.LPTStr)] string lpszFile, int nIconIndex, IntPtr[] phIconLarge, IntPtr[] phIconSmall, int nIcons); [DllImport("user32")] internal static extern int DestroyIcon(IntPtr hIcon); } /// /// Flags determining how the links with missing /// targets are resolved. /// [Flags] public enum EShellLinkResolveFlags : uint { /// /// Allow any match during resolution. Has no effect /// on ME/2000 or above, use the other flags instead. /// SLR_ANY_MATCH = 0x2, /// /// Call the Microsoft Windows Installer. /// SLR_INVOKE_MSI = 0x80, /// /// Disable distributed link tracking. By default, /// distributed link tracking tracks removable media /// across multiple devices based on the volume name. /// It also uses the UNC path to track remote file /// systems whose drive letter has changed. Setting /// SLR_NOLINKINFO disables both types of tracking. /// SLR_NOLINKINFO = 0x40, /// /// Do not display a dialog box if the link cannot be resolved. /// When SLR_NO_UI is set, a time-out value that specifies the /// maximum amount of time to be spent resolving the link can /// be specified in milliseconds. The function returns if the /// link cannot be resolved within the time-out duration. /// If the timeout is not set, the time-out duration will be /// set to the default value of 3,000 milliseconds (3 seconds). /// SLR_NO_UI = 0x1, /// /// Not documented in SDK. Assume same as SLR_NO_UI but /// intended for applications without a hWnd. /// SLR_NO_UI_WITH_MSG_PUMP = 0x101, /// /// Do not update the link information. /// SLR_NOUPDATE = 0x8, /// /// Do not execute the search heuristics. /// SLR_NOSEARCH = 0x10, /// /// Do not use distributed link tracking. /// SLR_NOTRACK = 0x20, /// /// If the link object has changed, update its path and list /// of identifiers. If SLR_UPDATE is set, you do not need to /// call IPersistFile::IsDirty to determine whether or not /// the link object has changed. /// SLR_UPDATE = 0x4 } public enum LinkDisplayMode : uint { edmNormal = EShowWindowFlags.SW_NORMAL, edmMinimized = EShowWindowFlags.SW_SHOWMINNOACTIVE, edmMaximized = EShowWindowFlags.SW_MAXIMIZE } // Use Unicode (W) under NT, otherwise use ANSI IShellLinkW linkW; IShellLinkA linkA; string shortcutFile = ""; /// /// Creates an instance of the Shell Link object. /// public ShellLink() { if (System.Environment.OSVersion.Platform == PlatformID.Win32NT) { linkW = (IShellLinkW)new CShellLink(); } else { linkA = (IShellLinkA)new CShellLink(); } } /// /// Creates an instance of a Shell Link object /// from the specified link file /// /// The Shortcut file to open public ShellLink(string linkFile) : this() { Open(linkFile); } /// /// Call dispose just in case it hasn't happened yet /// ~ShellLink() { Dispose(); } /// /// Dispose the object, releasing the COM ShellLink object /// public void Dispose() { if (linkW != null) { Marshal.ReleaseComObject(linkW); linkW = null; } if (linkA != null) { Marshal.ReleaseComObject(linkA); linkA = null; } } public string ShortCutFile { get { return this.shortcutFile; } set { this.shortcutFile = value; } } /// /// Gets a System.Drawing.Icon containing the icon for this /// ShellLink object. /// public Icon LargeIcon { get { return getIcon(true); } } public Icon SmallIcon { get { return getIcon(false); } } Icon getIcon(bool large) { // Get icon index and path: int iconIndex = 0; StringBuilder iconPath = new StringBuilder(260, 260); if (linkA == null) { linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } else { linkA.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } string iconFile = iconPath.ToString(); // If there are no details set for the icon, then we must use // the shell to get the icon for the target: if (iconFile.Length == 0) { // Use the FileIcon object to get the icon: FileIcon.SHGetFileInfoConstants flags = FileIcon.SHGetFileInfoConstants.SHGFI_ICON | FileIcon.SHGetFileInfoConstants.SHGFI_ATTRIBUTES; if (large) { flags = flags | FileIcon.SHGetFileInfoConstants.SHGFI_LARGEICON; } else { flags = flags | FileIcon.SHGetFileInfoConstants.SHGFI_SMALLICON; } FileIcon fileIcon = new FileIcon(Target, flags); return fileIcon.ShellIcon; } else { // Use ExtractIconEx to get the icon: IntPtr[] hIconEx = new IntPtr[1] { IntPtr.Zero }; int iconCount = 0; if (large) { iconCount = UnManagedMethods.ExtractIconEx( iconFile, iconIndex, hIconEx, null, 1); } else { iconCount = UnManagedMethods.ExtractIconEx( iconFile, iconIndex, null, hIconEx, 1); } // If success then return as a GDI+ object Icon icon = null; if (hIconEx[0] != IntPtr.Zero) { icon = Icon.FromHandle(hIconEx[0]); //UnManagedMethods.DestroyIcon(hIconEx[0]); } return icon; } } /// /// Gets the path to the file containing the icon for this shortcut. /// public string IconPath { get { StringBuilder iconPath = new StringBuilder(260, 260); int iconIndex = 0; if (linkA == null) { linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } else { linkA.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } return iconPath.ToString(); } set { StringBuilder iconPath = new StringBuilder(260, 260); int iconIndex = 0; if (linkA == null) { linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } else { linkA.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } if (linkA == null) { linkW.SetIconLocation(value, iconIndex); } else { linkA.SetIconLocation(value, iconIndex); } } } /// /// Gets the index of this icon within the icon path's resources /// public int IconIndex { get { StringBuilder iconPath = new StringBuilder(260, 260); int iconIndex = 0; if (linkA == null) { linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } else { linkA.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } return iconIndex; } set { StringBuilder iconPath = new StringBuilder(260, 260); int iconIndex = 0; if (linkA == null) { linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } else { linkA.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } if (linkA == null) { linkW.SetIconLocation(iconPath.ToString(), value); } else { linkA.SetIconLocation(iconPath.ToString(), value); } } } /// /// Gets/sets the fully qualified path to the link's target /// public string Target { get { StringBuilder target = new StringBuilder(260, 260); if (linkA == null) { _WIN32_FIND_DATAW fd = new _WIN32_FIND_DATAW(); linkW.GetPath(target, target.Capacity, ref fd, (uint)EShellLinkGP.SLGP_UNCPRIORITY); } else { _WIN32_FIND_DATAA fd = new _WIN32_FIND_DATAA(); linkA.GetPath(target, target.Capacity, ref fd, (uint)EShellLinkGP.SLGP_UNCPRIORITY); } return target.ToString(); } set { if (linkA == null) { linkW.SetPath(value); } else { linkA.SetPath(value); } } } /// /// Gets/sets the Working Directory for the Link /// public string WorkingDirectory { get { StringBuilder path = new StringBuilder(260, 260); if (linkA == null) { linkW.GetWorkingDirectory(path, path.Capacity); } else { linkA.GetWorkingDirectory(path, path.Capacity); } return path.ToString(); } set { if (linkA == null) { linkW.SetWorkingDirectory(value); } else { linkA.SetWorkingDirectory(value); } } } /// /// Gets/sets the description of the link /// public string Description { get { StringBuilder description = new StringBuilder(1024, 1024); if (linkA == null) { linkW.GetDescription(description, description.Capacity); } else { linkA.GetDescription(description, description.Capacity); } return description.ToString(); } set { if (linkA == null) { linkW.SetDescription(value); } else { linkA.SetDescription(value); } } } /// /// Gets/sets any command line arguments associated with the link /// public string Arguments { get { StringBuilder arguments = new StringBuilder(260, 260); if (linkA == null) { linkW.GetArguments(arguments, arguments.Capacity); } else { linkA.GetArguments(arguments, arguments.Capacity); } return arguments.ToString(); } set { if (linkA == null) { linkW.SetArguments(value); } else { linkA.SetArguments(value); } } } /// /// Gets/sets the initial display mode when the shortcut is /// run /// public LinkDisplayMode DisplayMode { get { uint cmd = 0; if (linkA == null) { linkW.GetShowCmd(out cmd); } else { linkA.GetShowCmd(out cmd); } return (LinkDisplayMode)cmd; } set { if (linkA == null) { linkW.SetShowCmd((uint)value); } else { linkA.SetShowCmd((uint)value); } } } /// /// Gets/sets the HotKey to start the shortcut (if any) /// public short HotKey { get { short key = 0; if (linkA == null) { linkW.GetHotkey(out key); } else { linkA.GetHotkey(out key); } return key; } set { if (linkA == null) { linkW.SetHotkey(value); } else { linkA.SetHotkey(value); } } } /// /// Sets the appUserModelId /// public void SetAppUserModelId(string appId) { var propStore = (IPropertyStore)linkW; var pkey = PROPERTYKEY.PKEY_AppUserModel_ID; var str = PropVariant.FromString (appId); propStore.SetValue(ref pkey, ref str); } /// /// Sets the ToastActivatorCLSID /// public void SetToastActivatorCLSID(string clsid) { var guid = Guid.Parse(clsid); SetToastActivatorCLSID(guid); } /// /// Sets the ToastActivatorCLSID /// public void SetToastActivatorCLSID(Guid clsid) { var propStore = (IPropertyStore)linkW; var pkey = PROPERTYKEY.PKEY_AppUserModel_ToastActivatorCLSID; var varGuid = PropVariant.FromGuid(clsid); try { int errCode = propStore.SetValue(ref pkey, ref varGuid); Marshal.ThrowExceptionForHR(errCode); errCode = propStore.Commit(); Marshal.ThrowExceptionForHR(errCode); } finally { varGuid.Clear(); } } /// /// Saves the shortcut to ShortCutFile. /// public void Save() { Save(shortcutFile); } /// /// Saves the shortcut to the specified file /// /// The shortcut file (.lnk) public void Save( string linkFile ) { // Save the object to disk if (linkA == null) { ((IPersistFile)linkW).Save(linkFile, true); shortcutFile = linkFile; } else { ((IPersistFile)linkA).Save(linkFile, true); shortcutFile = linkFile; } } /// /// Loads a shortcut from the specified file /// /// The shortcut file (.lnk) to load public void Open( string linkFile ) { Open(linkFile, IntPtr.Zero, (EShellLinkResolveFlags.SLR_ANY_MATCH | EShellLinkResolveFlags.SLR_NO_UI), 1); } /// /// Loads a shortcut from the specified file, and allows flags controlling /// the UI behaviour if the shortcut's target isn't found to be set. /// /// The shortcut file (.lnk) to load /// The window handle of the application's UI, if any /// Flags controlling resolution behaviour public void Open( string linkFile, IntPtr hWnd, EShellLinkResolveFlags resolveFlags ) { Open(linkFile, hWnd, resolveFlags, 1); } /// /// Loads a shortcut from the specified file, and allows flags controlling /// the UI behaviour if the shortcut's target isn't found to be set. If /// no SLR_NO_UI is specified, you can also specify a timeout. /// /// The shortcut file (.lnk) to load /// The window handle of the application's UI, if any /// Flags controlling resolution behaviour /// Timeout if SLR_NO_UI is specified, in ms. public void Open( string linkFile, IntPtr hWnd, EShellLinkResolveFlags resolveFlags, ushort timeOut ) { uint flags; if ((resolveFlags & EShellLinkResolveFlags.SLR_NO_UI) == EShellLinkResolveFlags.SLR_NO_UI) { flags = (uint)((int)resolveFlags | (timeOut << 16)); } else { flags = (uint)resolveFlags; } if (linkA == null) { ((IPersistFile)linkW).Load(linkFile, 0); //STGM_DIRECT) linkW.Resolve(hWnd, flags); this.shortcutFile = linkFile; } else { ((IPersistFile)linkA).Load(linkFile, 0); //STGM_DIRECT) linkA.Resolve(hWnd, flags); this.shortcutFile = linkFile; } } } /// /// Enables extraction of icons for any file type from /// the Shell. /// public class FileIcon { const int MAX_PATH = 260; [StructLayout(LayoutKind.Sequential)] struct SHFILEINFO { public IntPtr hIcon; public int iIcon; public int dwAttributes; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)] public string szDisplayName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] public string szTypeName; } [DllImport("shell32")] static extern IntPtr SHGetFileInfo( string pszPath, int dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags); [DllImport("user32.dll")] static extern int DestroyIcon(IntPtr hIcon); const int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x100; const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x2000; const int FORMAT_MESSAGE_FROM_HMODULE = 0x800; const int FORMAT_MESSAGE_FROM_STRING = 0x400; const int FORMAT_MESSAGE_FROM_SYSTEM = 0x1000; const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x200; const int FORMAT_MESSAGE_MAX_WIDTH_MASK = 0xFF; [DllImport("kernel32")] static extern int FormatMessage( int dwFlags, IntPtr lpSource, int dwMessageId, int dwLanguageId, string lpBuffer, uint nSize, IntPtr argumentsLong); [DllImport("kernel32")] static extern int GetLastError(); string fileName; string displayName; string typeName; SHGetFileInfoConstants flags; Icon fileIcon; [Flags] public enum SHGetFileInfoConstants : int { SHGFI_ICON = 0x100, // get icon SHGFI_DISPLAYNAME = 0x200, // get display name SHGFI_TYPENAME = 0x400, // get type name SHGFI_ATTRIBUTES = 0x800, // get attributes SHGFI_ICONLOCATION = 0x1000, // get icon location SHGFI_EXETYPE = 0x2000, // return exe type SHGFI_SYSICONINDEX = 0x4000, // get system icon index SHGFI_LINKOVERLAY = 0x8000, // put a link overlay on icon SHGFI_SELECTED = 0x10000, // show icon in selected state SHGFI_ATTR_SPECIFIED = 0x20000, // get only specified attributes SHGFI_LARGEICON = 0x0, // get large icon SHGFI_SMALLICON = 0x1, // get small icon SHGFI_OPENICON = 0x2, // get open icon SHGFI_SHELLICONSIZE = 0x4, // get shell size icon //SHGFI_PIDL = 0x8, // pszPath is a pidl SHGFI_USEFILEATTRIBUTES = 0x10, // use passed dwFileAttribute SHGFI_ADDOVERLAYS = 0x000000020, // apply the appropriate overlays SHGFI_OVERLAYINDEX = 0x000000040 // Get the index of the overlay } /// /// Gets/sets the flags used to extract the icon /// public FileIcon.SHGetFileInfoConstants Flags { get { return flags; } set { flags = value; } } /// /// Gets/sets the filename to get the icon for /// public string FileName { get { return fileName; } set { fileName = value; } } /// /// Gets the icon for the chosen file /// public Icon ShellIcon { get { return fileIcon; } } /// /// Gets the display name for the selected file /// if the SHGFI_DISPLAYNAME flag was set. /// public string DisplayName { get { return displayName; } } /// /// Gets the type name for the selected file /// if the SHGFI_TYPENAME flag was set. /// public string TypeName { get { return typeName; } } /// /// Gets the information for the specified /// file name and flags. /// public void GetInfo() { fileIcon = null; typeName = ""; displayName = ""; SHFILEINFO shfi = new SHFILEINFO(); uint shfiSize = (uint)Marshal.SizeOf(shfi.GetType()); IntPtr ret = SHGetFileInfo( fileName, 0, ref shfi, shfiSize, (uint)(flags)); if (ret != IntPtr.Zero) { if (shfi.hIcon != IntPtr.Zero) { fileIcon = System.Drawing.Icon.FromHandle(shfi.hIcon); // Now owned by the GDI+ object //DestroyIcon(shfi.hIcon); } typeName = shfi.szTypeName; displayName = shfi.szDisplayName; } else { int err = GetLastError(); Console.WriteLine("Error {0}", err); string txtS = new string('\0', 256); int len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, IntPtr.Zero, err, 0, txtS, 256, IntPtr.Zero); Console.WriteLine("Len {0} text {1}", len, txtS); // throw exception } } /// /// Constructs a new, default instance of the FileIcon /// class. Specify the filename and call GetInfo() /// to retrieve an icon. /// public FileIcon() { flags = SHGetFileInfoConstants.SHGFI_ICON | SHGetFileInfoConstants.SHGFI_DISPLAYNAME | SHGetFileInfoConstants.SHGFI_TYPENAME | SHGetFileInfoConstants.SHGFI_ATTRIBUTES | SHGetFileInfoConstants.SHGFI_EXETYPE; } /// /// Constructs a new instance of the FileIcon class /// and retrieves the icon, display name and type name /// for the specified file. /// /// The filename to get the icon, /// display name and type name for public FileIcon(string fileName) : this() { this.fileName = fileName; GetInfo(); } /// /// Constructs a new instance of the FileIcon class /// and retrieves the information specified in the /// flags. /// /// The filename to get information /// for /// The flags to use when extracting the /// icon and other shell information. public FileIcon(string fileName, FileIcon.SHGetFileInfoConstants flags) { this.fileName = fileName; this.flags = flags; GetInfo(); } } } ================================================ FILE: src/Squirrel/SimpleJson/SimpleJson.cs ================================================ //----------------------------------------------------------------------- // // Copyright (c) 2011, The Outercurve Foundation. // // Licensed under the MIT License (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.opensource.org/licenses/mit-license.php // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me) // https://github.com/facebook-csharp-sdk/simple-json //----------------------------------------------------------------------- // VERSION: 0.38.0 // NOTE: uncomment the following line to make SimpleJson class internal. #define SIMPLE_JSON_INTERNAL // NOTE: uncomment the following line to make JsonArray and JsonObject class internal. //#define SIMPLE_JSON_OBJARRAYINTERNAL // NOTE: uncomment the following line to enable dynamic support. #define SIMPLE_JSON_DYNAMIC // NOTE: uncomment the following line to enable DataContract support. #define SIMPLE_JSON_DATACONTRACT // NOTE: uncomment the following line to enable IReadOnlyCollection and IReadOnlyList support. //#define SIMPLE_JSON_READONLY_COLLECTIONS // NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). // define if you are using .net framework <= 3.0 or < WP7.5 //#define SIMPLE_JSON_NO_LINQ_EXPRESSION // NOTE: uncomment the following line if you are compiling under Window Metro style application/library. // usually already defined in properties //#define NETFX_CORE; // If you are targetting WinStore, WP8 and NET4.5+ PCL make sure to #define SIMPLE_JSON_TYPEINFO; // original json parsing code from http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html #if NETFX_CORE #define SIMPLE_JSON_TYPEINFO #endif using System; using System.CodeDom.Compiler; using System.Collections; using System.Collections.Generic; #if !SIMPLE_JSON_NO_LINQ_EXPRESSION using System.Linq.Expressions; #endif using System.ComponentModel; using System.Diagnostics.CodeAnalysis; #if SIMPLE_JSON_DYNAMIC using System.Dynamic; #endif using System.Globalization; using System.Reflection; using System.Runtime.Serialization; using System.Text; using Squirrel.Json.Reflection; // ReSharper disable LoopCanBeConvertedToQuery // ReSharper disable RedundantExplicitArrayCreation // ReSharper disable SuggestUseVarKeywordEvident namespace Squirrel.Json { /// /// Represents the json array. /// [GeneratedCode("simple-json", "1.0.0")] [EditorBrowsable(EditorBrowsableState.Never)] [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] #if SIMPLE_JSON_OBJARRAYINTERNAL internal #else public #endif class JsonArray : List { /// /// Initializes a new instance of the class. /// public JsonArray() { } /// /// Initializes a new instance of the class. /// /// The capacity of the json array. public JsonArray(int capacity) : base(capacity) { } /// /// The json representation of the array. /// /// The json representation of the array. public override string ToString() { return SimpleJson.SerializeObject(this) ?? string.Empty; } } /// /// Represents the json object. /// [GeneratedCode("simple-json", "1.0.0")] [EditorBrowsable(EditorBrowsableState.Never)] [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] #if SIMPLE_JSON_OBJARRAYINTERNAL internal #else public #endif class JsonObject : #if SIMPLE_JSON_DYNAMIC DynamicObject, #endif IDictionary { /// /// The internal member dictionary. /// private readonly Dictionary _members; /// /// Initializes a new instance of . /// public JsonObject() { _members = new Dictionary(); } /// /// Initializes a new instance of . /// /// The implementation to use when comparing keys, or null to use the default for the type of the key. public JsonObject(IEqualityComparer comparer) { _members = new Dictionary(comparer); } /// /// Gets the at the specified index. /// /// public object this[int index] { get { return GetAtIndex(_members, index); } } internal static object GetAtIndex(IDictionary obj, int index) { if (obj == null) throw new ArgumentNullException("obj"); if (index >= obj.Count) throw new ArgumentOutOfRangeException("index"); int i = 0; foreach (KeyValuePair o in obj) if (i++ == index) return o.Value; return null; } /// /// Adds the specified key. /// /// The key. /// The value. public void Add(string key, object value) { _members.Add(key, value); } /// /// Determines whether the specified key contains key. /// /// The key. /// /// true if the specified key contains key; otherwise, false. /// public bool ContainsKey(string key) { return _members.ContainsKey(key); } /// /// Gets the keys. /// /// The keys. public ICollection Keys { get { return _members.Keys; } } /// /// Removes the specified key. /// /// The key. /// public bool Remove(string key) { return _members.Remove(key); } /// /// Tries the get value. /// /// The key. /// The value. /// public bool TryGetValue(string key, out object value) { return _members.TryGetValue(key, out value); } /// /// Gets the values. /// /// The values. public ICollection Values { get { return _members.Values; } } /// /// Gets or sets the with the specified key. /// /// public object this[string key] { get { return _members[key]; } set { _members[key] = value; } } /// /// Adds the specified item. /// /// The item. public void Add(KeyValuePair item) { _members.Add(item.Key, item.Value); } /// /// Clears this instance. /// public void Clear() { _members.Clear(); } /// /// Determines whether [contains] [the specified item]. /// /// The item. /// /// true if [contains] [the specified item]; otherwise, false. /// public bool Contains(KeyValuePair item) { return _members.ContainsKey(item.Key) && _members[item.Key] == item.Value; } /// /// Copies to. /// /// The array. /// Index of the array. public void CopyTo(KeyValuePair[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException("array"); int num = Count; foreach (KeyValuePair kvp in this) { array[arrayIndex++] = kvp; if (--num <= 0) return; } } /// /// Gets the count. /// /// The count. public int Count { get { return _members.Count; } } /// /// Gets a value indicating whether this instance is read only. /// /// /// true if this instance is read only; otherwise, false. /// public bool IsReadOnly { get { return false; } } /// /// Removes the specified item. /// /// The item. /// public bool Remove(KeyValuePair item) { return _members.Remove(item.Key); } /// /// Gets the enumerator. /// /// public IEnumerator> GetEnumerator() { return _members.GetEnumerator(); } /// /// Returns an enumerator that iterates through a collection. /// /// /// An object that can be used to iterate through the collection. /// IEnumerator IEnumerable.GetEnumerator() { return _members.GetEnumerator(); } /// /// Returns a json that represents the current . /// /// /// A json that represents the current . /// public override string ToString() { return SimpleJson.SerializeObject(this); } #if SIMPLE_JSON_DYNAMIC /// /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. /// /// Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the class, binder.Type returns the type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion. /// The result of the type conversion operation. /// /// Alwasy returns true. /// public override bool TryConvert(ConvertBinder binder, out object result) { // if (binder == null) throw new ArgumentNullException("binder"); // Type targetType = binder.Type; if ((targetType == typeof(IEnumerable)) || (targetType == typeof(IEnumerable>)) || (targetType == typeof(IDictionary)) || (targetType == typeof(IDictionary))) { result = this; return true; } return base.TryConvert(binder, out result); } /// /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic. /// /// Provides information about the deletion. /// /// Alwasy returns true. /// public override bool TryDeleteMember(DeleteMemberBinder binder) { // if (binder == null) throw new ArgumentNullException("binder"); // return _members.Remove(binder.Name); } /// /// Provides the implementation for operations that get a value by index. Classes derived from the class can override this method to specify dynamic behavior for indexing operations. /// /// Provides information about the operation. /// The indexes that are used in the operation. For example, for the sampleObject[3] operation in C# (sampleObject(3) in Visual Basic), where sampleObject is derived from the DynamicObject class, is equal to 3. /// The result of the index operation. /// /// Alwasy returns true. /// public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { if (indexes == null) throw new ArgumentNullException("indexes"); if (indexes.Length == 1) { result = ((IDictionary)this)[(string)indexes[0]]; return true; } result = null; return true; } /// /// Provides the implementation for operations that get member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as getting a value for a property. /// /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. /// The result of the get operation. For example, if the method is called for a property, you can assign the property value to . /// /// Alwasy returns true. /// public override bool TryGetMember(GetMemberBinder binder, out object result) { object value; if (_members.TryGetValue(binder.Name, out value)) { result = value; return true; } result = null; return true; } /// /// Provides the implementation for operations that set a value by index. Classes derived from the class can override this method to specify dynamic behavior for operations that access objects by a specified index. /// /// Provides information about the operation. /// The indexes that are used in the operation. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 3. /// The value to set to the object that has the specified index. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 10. /// /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown. /// public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) { if (indexes == null) throw new ArgumentNullException("indexes"); if (indexes.Length == 1) { ((IDictionary)this)[(string)indexes[0]] = value; return true; } return base.TrySetIndex(binder, indexes, value); } /// /// Provides the implementation for operations that set member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as setting a value for a property. /// /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. /// The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, the is "Test". /// /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) /// public override bool TrySetMember(SetMemberBinder binder, object value) { // if (binder == null) throw new ArgumentNullException("binder"); // _members[binder.Name] = value; return true; } /// /// Returns the enumeration of all dynamic member names. /// /// /// A sequence that contains dynamic member names. /// public override IEnumerable GetDynamicMemberNames() { foreach (var key in Keys) yield return key; } #endif } } namespace Squirrel.Json { /// /// This class encodes and decodes JSON strings. /// Spec. details, see http://www.json.org/ /// /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList<object>) and JsonObject(IDictionary<string,object>). /// All numbers are parsed to doubles. /// [GeneratedCode("simple-json", "1.0.0")] #if SIMPLE_JSON_INTERNAL internal #else public #endif static class SimpleJson { private const int TOKEN_NONE = 0; private const int TOKEN_CURLY_OPEN = 1; private const int TOKEN_CURLY_CLOSE = 2; private const int TOKEN_SQUARED_OPEN = 3; private const int TOKEN_SQUARED_CLOSE = 4; private const int TOKEN_COLON = 5; private const int TOKEN_COMMA = 6; private const int TOKEN_STRING = 7; private const int TOKEN_NUMBER = 8; private const int TOKEN_TRUE = 9; private const int TOKEN_FALSE = 10; private const int TOKEN_NULL = 11; private const int BUILDER_CAPACITY = 2000; private static readonly char[] EscapeTable; private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; private static readonly string EscapeCharactersString = new string(EscapeCharacters); static SimpleJson() { EscapeTable = new char[93]; EscapeTable['"'] = '"'; EscapeTable['\\'] = '\\'; EscapeTable['\b'] = 'b'; EscapeTable['\f'] = 'f'; EscapeTable['\n'] = 'n'; EscapeTable['\r'] = 'r'; EscapeTable['\t'] = 't'; } /// /// Parses the string json into a value /// /// A JSON string. /// An IList<object>, a IDictionary<string,object>, a double, a string, null, true, or false public static object DeserializeObject(string json) { object obj; if (TryDeserializeObject(json, out obj)) return obj; throw new SerializationException("Invalid JSON string"); } /// /// Try parsing the json string into a value. /// /// /// A JSON string. /// /// /// The object. /// /// /// Returns true if successfull otherwise false. /// [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] public static bool TryDeserializeObject(string json, out object obj) { bool success = true; if (json != null) { char[] charArray = json.ToCharArray(); int index = 0; obj = ParseValue(charArray, ref index, ref success); } else obj = null; return success; } public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy) { object jsonObject = DeserializeObject(json); return type == null || jsonObject != null && ReflectionUtils.IsAssignableFrom(jsonObject.GetType(), type) ? jsonObject : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type); } public static object DeserializeObject(string json, Type type) { return DeserializeObject(json, type, null); } public static T DeserializeObject(string json, IJsonSerializerStrategy jsonSerializerStrategy) { return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy); } public static T DeserializeObject(string json) { return (T)DeserializeObject(json, typeof(T), null); } /// /// Converts a IDictionary<string,object> / IList<object> object into a JSON string /// /// A IDictionary<string,object> / IList<object> /// Serializer strategy to use /// A JSON encoded string, or null if object 'json' is not serializable public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy) { StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); bool success = SerializeValue(jsonSerializerStrategy, json, builder); return (success ? builder.ToString() : null); } public static string SerializeObject(object json) { return SerializeObject(json, CurrentJsonSerializerStrategy); } public static string EscapeToJavascriptString(string jsonString) { if (string.IsNullOrEmpty(jsonString)) return jsonString; StringBuilder sb = new StringBuilder(); char c; for (int i = 0; i < jsonString.Length; ) { c = jsonString[i++]; if (c == '\\') { int remainingLength = jsonString.Length - i; if (remainingLength >= 2) { char lookahead = jsonString[i]; if (lookahead == '\\') { sb.Append('\\'); ++i; } else if (lookahead == '"') { sb.Append("\""); ++i; } else if (lookahead == 't') { sb.Append('\t'); ++i; } else if (lookahead == 'b') { sb.Append('\b'); ++i; } else if (lookahead == 'n') { sb.Append('\n'); ++i; } else if (lookahead == 'r') { sb.Append('\r'); ++i; } } } else { sb.Append(c); } } return sb.ToString(); } static IDictionary ParseObject(char[] json, ref int index, ref bool success) { IDictionary table = new JsonObject(); int token; // { NextToken(json, ref index); bool done = false; while (!done) { token = LookAhead(json, index); if (token == TOKEN_NONE) { success = false; return null; } else if (token == TOKEN_COMMA) NextToken(json, ref index); else if (token == TOKEN_CURLY_CLOSE) { NextToken(json, ref index); return table; } else { // name string name = ParseString(json, ref index, ref success); if (!success) { success = false; return null; } // : token = NextToken(json, ref index); if (token != TOKEN_COLON) { success = false; return null; } // value object value = ParseValue(json, ref index, ref success); if (!success) { success = false; return null; } table[name] = value; } } return table; } static JsonArray ParseArray(char[] json, ref int index, ref bool success) { JsonArray array = new JsonArray(); // [ NextToken(json, ref index); bool done = false; while (!done) { int token = LookAhead(json, index); if (token == TOKEN_NONE) { success = false; return null; } else if (token == TOKEN_COMMA) NextToken(json, ref index); else if (token == TOKEN_SQUARED_CLOSE) { NextToken(json, ref index); break; } else { object value = ParseValue(json, ref index, ref success); if (!success) return null; array.Add(value); } } return array; } static object ParseValue(char[] json, ref int index, ref bool success) { switch (LookAhead(json, index)) { case TOKEN_STRING: return ParseString(json, ref index, ref success); case TOKEN_NUMBER: return ParseNumber(json, ref index, ref success); case TOKEN_CURLY_OPEN: return ParseObject(json, ref index, ref success); case TOKEN_SQUARED_OPEN: return ParseArray(json, ref index, ref success); case TOKEN_TRUE: NextToken(json, ref index); return true; case TOKEN_FALSE: NextToken(json, ref index); return false; case TOKEN_NULL: NextToken(json, ref index); return null; case TOKEN_NONE: break; } success = false; return null; } static string ParseString(char[] json, ref int index, ref bool success) { StringBuilder s = new StringBuilder(BUILDER_CAPACITY); char c; EatWhitespace(json, ref index); // " c = json[index++]; bool complete = false; while (!complete) { if (index == json.Length) break; c = json[index++]; if (c == '"') { complete = true; break; } else if (c == '\\') { if (index == json.Length) break; c = json[index++]; if (c == '"') s.Append('"'); else if (c == '\\') s.Append('\\'); else if (c == '/') s.Append('/'); else if (c == 'b') s.Append('\b'); else if (c == 'f') s.Append('\f'); else if (c == 'n') s.Append('\n'); else if (c == 'r') s.Append('\r'); else if (c == 't') s.Append('\t'); else if (c == 'u') { int remainingLength = json.Length - index; if (remainingLength >= 4) { // parse the 32 bit hex into an integer codepoint uint codePoint; if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) return ""; // convert the integer codepoint to a unicode char and add to string if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate { index += 4; // skip 4 chars remainingLength = json.Length - index; if (remainingLength >= 6) { uint lowCodePoint; if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint)) { if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate { s.Append((char)codePoint); s.Append((char)lowCodePoint); index += 6; // skip 6 chars continue; } } } success = false; // invalid surrogate pair return ""; } s.Append(ConvertFromUtf32((int)codePoint)); // skip 4 chars index += 4; } else break; } } else s.Append(c); } if (!complete) { success = false; return null; } return s.ToString(); } private static string ConvertFromUtf32(int utf32) { // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm if (utf32 < 0 || utf32 > 0x10FFFF) throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF."); if (0xD800 <= utf32 && utf32 <= 0xDFFF) throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range."); if (utf32 < 0x10000) return new string((char)utf32, 1); utf32 -= 0x10000; return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) }); } static object ParseNumber(char[] json, ref int index, ref bool success) { EatWhitespace(json, ref index); int lastIndex = GetLastIndexOfNumber(json, index); int charLength = (lastIndex - index) + 1; object returnNumber; string str = new string(json, index, charLength); if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) { double number; success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); returnNumber = number; } else { long number; success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); returnNumber = number; } index = lastIndex + 1; return returnNumber; } static int GetLastIndexOfNumber(char[] json, int index) { int lastIndex; for (lastIndex = index; lastIndex < json.Length; lastIndex++) if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break; return lastIndex - 1; } static void EatWhitespace(char[] json, ref int index) { for (; index < json.Length; index++) if (" \t\n\r\b\f".IndexOf(json[index]) == -1) break; } static int LookAhead(char[] json, int index) { int saveIndex = index; return NextToken(json, ref saveIndex); } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] static int NextToken(char[] json, ref int index) { EatWhitespace(json, ref index); if (index == json.Length) return TOKEN_NONE; char c = json[index]; index++; switch (c) { case '{': return TOKEN_CURLY_OPEN; case '}': return TOKEN_CURLY_CLOSE; case '[': return TOKEN_SQUARED_OPEN; case ']': return TOKEN_SQUARED_CLOSE; case ',': return TOKEN_COMMA; case '"': return TOKEN_STRING; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': return TOKEN_NUMBER; case ':': return TOKEN_COLON; } index--; int remainingLength = json.Length - index; // false if (remainingLength >= 5) { if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e') { index += 5; return TOKEN_FALSE; } } // true if (remainingLength >= 4) { if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') { index += 4; return TOKEN_TRUE; } } // null if (remainingLength >= 4) { if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') { index += 4; return TOKEN_NULL; } } return TOKEN_NONE; } static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder) { bool success = true; string stringValue = value as string; if (stringValue != null) success = SerializeString(stringValue, builder); else { IDictionary dict = value as IDictionary; if (dict != null) { success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); } else { IDictionary stringDictionary = value as IDictionary; if (stringDictionary != null) { success = SerializeObject(jsonSerializerStrategy, stringDictionary.Keys, stringDictionary.Values, builder); } else { IEnumerable enumerableValue = value as IEnumerable; if (enumerableValue != null) success = SerializeArray(jsonSerializerStrategy, enumerableValue, builder); else if (IsNumeric(value)) success = SerializeNumber(value, builder); else if (value is bool) builder.Append((bool)value ? "true" : "false"); else if (value == null) builder.Append("null"); else { object serializedObject; success = jsonSerializerStrategy.TrySerializeNonPrimitiveObject(value, out serializedObject); if (success) SerializeValue(jsonSerializerStrategy, serializedObject, builder); } } } } return success; } static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder) { builder.Append("{"); IEnumerator ke = keys.GetEnumerator(); IEnumerator ve = values.GetEnumerator(); bool first = true; while (ke.MoveNext() && ve.MoveNext()) { object key = ke.Current; object value = ve.Current; if (!first) builder.Append(","); string stringKey = key as string; if (stringKey != null) SerializeString(stringKey, builder); else if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; builder.Append(":"); if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; first = false; } builder.Append("}"); return true; } static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder) { builder.Append("["); bool first = true; foreach (object value in anArray) { if (!first) builder.Append(","); if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; first = false; } builder.Append("]"); return true; } static bool SerializeString(string aString, StringBuilder builder) { // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) if (aString.IndexOfAny(EscapeCharacters) == -1) { builder.Append('"'); builder.Append(aString); builder.Append('"'); return true; } builder.Append('"'); int safeCharacterCount = 0; char[] charArray = aString.ToCharArray(); for (int i = 0; i < charArray.Length; i++) { char c = charArray[i]; // Non ascii characters are fine, buffer them up and send them to the builder // in larger chunks if possible. The escape table is a 1:1 translation table // with \0 [default(char)] denoting a safe character. if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) { safeCharacterCount++; } else { if (safeCharacterCount > 0) { builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); safeCharacterCount = 0; } builder.Append('\\'); builder.Append(EscapeTable[c]); } } if (safeCharacterCount > 0) { builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); } builder.Append('"'); return true; } static bool SerializeNumber(object number, StringBuilder builder) { if (number is long) builder.Append(((long)number).ToString(CultureInfo.InvariantCulture)); else if (number is ulong) builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture)); else if (number is int) builder.Append(((int)number).ToString(CultureInfo.InvariantCulture)); else if (number is uint) builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture)); else if (number is decimal) builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture)); else if (number is float) builder.Append(((float)number).ToString(CultureInfo.InvariantCulture)); else builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture)); return true; } /// /// Determines if a given object is numeric in any way /// (can be integer, double, null, etc). /// static bool IsNumeric(object value) { if (value is sbyte) return true; if (value is byte) return true; if (value is short) return true; if (value is ushort) return true; if (value is int) return true; if (value is uint) return true; if (value is long) return true; if (value is ulong) return true; if (value is float) return true; if (value is double) return true; if (value is decimal) return true; return false; } private static IJsonSerializerStrategy _currentJsonSerializerStrategy; public static IJsonSerializerStrategy CurrentJsonSerializerStrategy { get { return _currentJsonSerializerStrategy ?? (_currentJsonSerializerStrategy = #if SIMPLE_JSON_DATACONTRACT DataContractJsonSerializerStrategy #else PocoJsonSerializerStrategy #endif ); } set { _currentJsonSerializerStrategy = value; } } private static PocoJsonSerializerStrategy _pocoJsonSerializerStrategy; [EditorBrowsable(EditorBrowsableState.Advanced)] public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy { get { return _pocoJsonSerializerStrategy ?? (_pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy()); } } #if SIMPLE_JSON_DATACONTRACT private static DataContractJsonSerializerStrategy _dataContractJsonSerializerStrategy; [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)] public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy { get { return _dataContractJsonSerializerStrategy ?? (_dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy()); } } #endif } [GeneratedCode("simple-json", "1.0.0")] #if SIMPLE_JSON_INTERNAL internal #else public #endif interface IJsonSerializerStrategy { [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] bool TrySerializeNonPrimitiveObject(object input, out object output); object DeserializeObject(object value, Type type); } [GeneratedCode("simple-json", "1.0.0")] #if SIMPLE_JSON_INTERNAL internal #else public #endif class PocoJsonSerializerStrategy : IJsonSerializerStrategy { internal IDictionary ConstructorCache; internal IDictionary> GetCache; internal IDictionary>> SetCache; internal static readonly Type[] EmptyTypes = new Type[0]; internal static readonly Type[] ArrayConstructorParameterTypes = new Type[] { typeof(int) }; private static readonly string[] Iso8601Format = new string[] { @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z", @"yyyy-MM-dd\THH:mm:ss\Z", @"yyyy-MM-dd\THH:mm:ssK" }; public PocoJsonSerializerStrategy() { ConstructorCache = new ReflectionUtils.ThreadSafeDictionary(ContructorDelegateFactory); GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); } protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName) { return clrPropertyName; } internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(Type key) { return ReflectionUtils.GetContructor(key, key.IsArray ? ArrayConstructorParameterTypes : EmptyTypes); } internal virtual IDictionary GetterValueFactory(Type type) { IDictionary result = new Dictionary(); foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) { if (propertyInfo.CanRead) { MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); if (getMethod.IsStatic || !getMethod.IsPublic) continue; result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (fieldInfo.IsStatic || !fieldInfo.IsPublic) continue; result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo); } return result; } internal virtual IDictionary> SetterValueFactory(Type type) { IDictionary> result = new Dictionary>(); foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) { if (propertyInfo.CanWrite) { MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); if (setMethod.IsStatic || !setMethod.IsPublic) continue; result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic) continue; result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); } return result; } public virtual bool TrySerializeNonPrimitiveObject(object input, out object output) { return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output); } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] public virtual object DeserializeObject(object value, Type type) { if (type == null) throw new ArgumentNullException("type"); string str = value as string; if (type == typeof (Guid) && string.IsNullOrEmpty(str)) return default(Guid); if (value == null) return null; object obj = null; if (str != null) { if (str.Length != 0) // We know it can't be null now. { if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime))) return DateTime.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset))) return DateTimeOffset.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))) return new Guid(str); if (type == typeof(Uri)) { bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute); Uri result; if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result)) return result; return null; } if (type == typeof(string)) return str; return Convert.ChangeType(str, type, CultureInfo.InvariantCulture); } else { if (type == typeof(Guid)) obj = default(Guid); else if (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) obj = null; else obj = str; } // Empty string case if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) return str; } else if (value is bool) return value; bool valueIsLong = value is long; bool valueIsDouble = value is double; if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double))) return value; if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long))) { obj = type == typeof(int) || type == typeof(long) || type == typeof(double) || type == typeof(float) || type == typeof(bool) || type == typeof(decimal) || type == typeof(byte) || type == typeof(short) ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture) : value; } else { IDictionary objects = value as IDictionary; if (objects != null) { IDictionary jsonObject = objects; if (ReflectionUtils.IsTypeDictionary(type)) { // if dictionary then Type[] types = ReflectionUtils.GetGenericTypeArguments(type); Type keyType = types[0]; Type valueType = types[1]; Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); IDictionary dict = (IDictionary)ConstructorCache[genericType](); foreach (KeyValuePair kvp in jsonObject) dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType)); obj = dict; } else { if (type == typeof(object)) obj = value; else { obj = ConstructorCache[type](); foreach (KeyValuePair> setter in SetCache[type]) { object jsonValue; if (jsonObject.TryGetValue(setter.Key, out jsonValue)) { jsonValue = DeserializeObject(jsonValue, setter.Value.Key); setter.Value.Value(obj, jsonValue); } } } } } else { IList valueAsList = value as IList; if (valueAsList != null) { IList jsonObject = valueAsList; IList list = null; if (type.IsArray) { list = (IList)ConstructorCache[type](jsonObject.Count); int i = 0; foreach (object o in jsonObject) list[i++] = DeserializeObject(o, type.GetElementType()); } else if (ReflectionUtils.IsTypeGenericeCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type)) { Type innerType = ReflectionUtils.GetGenericListElementType(type); list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count); foreach (object o in jsonObject) list.Add(DeserializeObject(o, innerType)); } obj = list; } } return obj; } if (ReflectionUtils.IsNullableType(type)) return ReflectionUtils.ToNullableType(obj, type); return obj; } protected virtual object SerializeEnum(Enum p) { return Convert.ToDouble(p, CultureInfo.InvariantCulture); } [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] protected virtual bool TrySerializeKnownTypes(object input, out object output) { bool returnValue = true; if (input is DateTime) output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); else if (input is DateTimeOffset) output = ((DateTimeOffset)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); else if (input is Guid) output = ((Guid)input).ToString("D"); else if (input is Uri) output = input.ToString(); else { Enum inputEnum = input as Enum; if (inputEnum != null) output = SerializeEnum(inputEnum); else { returnValue = false; output = null; } } return returnValue; } [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] protected virtual bool TrySerializeUnknownTypes(object input, out object output) { if (input == null) throw new ArgumentNullException("input"); output = null; Type type = input.GetType(); if (type.FullName == null) return false; IDictionary obj = new JsonObject(); IDictionary getters = GetCache[type]; foreach (KeyValuePair getter in getters) { if (getter.Value != null) obj.Add(MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input)); } output = obj; return true; } } #if SIMPLE_JSON_DATACONTRACT [GeneratedCode("simple-json", "1.0.0")] #if SIMPLE_JSON_INTERNAL internal #else public #endif class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy { public DataContractJsonSerializerStrategy() { GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); } internal override IDictionary GetterValueFactory(Type type) { bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; if (!hasDataContract) return base.GetterValueFactory(type); string jsonKey; IDictionary result = new Dictionary(); foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) { if (propertyInfo.CanRead) { MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); if (!getMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) result[jsonKey] = ReflectionUtils.GetGetMethod(propertyInfo); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (!fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) result[jsonKey] = ReflectionUtils.GetGetMethod(fieldInfo); } return result; } internal override IDictionary> SetterValueFactory(Type type) { bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; if (!hasDataContract) return base.SetterValueFactory(type); string jsonKey; IDictionary> result = new Dictionary>(); foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) { if (propertyInfo.CanWrite) { MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); if (!setMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) result[jsonKey] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (!fieldInfo.IsInitOnly && !fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) result[jsonKey] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); } // todo implement sorting for DATACONTRACT. return result; } private static bool CanAdd(MemberInfo info, out string jsonKey) { jsonKey = null; if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null) return false; DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute)); if (dataMemberAttribute == null) return false; jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name; return true; } } #endif namespace Reflection { // This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules // that might be in place in the target project. [GeneratedCode("reflection-utils", "1.0.0")] #if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC public #else internal #endif class ReflectionUtils { private static readonly object[] EmptyObjects = new object[] { }; public delegate object GetDelegate(object source); public delegate void SetDelegate(object source, object value); public delegate object ConstructorDelegate(params object[] args); public delegate TValue ThreadSafeDictionaryValueFactory(TKey key); #if SIMPLE_JSON_TYPEINFO public static TypeInfo GetTypeInfo(Type type) { return type.GetTypeInfo(); } #else public static Type GetTypeInfo(Type type) { return type; } #endif public static Attribute GetAttribute(MemberInfo info, Type type) { #if SIMPLE_JSON_TYPEINFO if (info == null || type == null || !info.IsDefined(type)) return null; return info.GetCustomAttribute(type); #else if (info == null || type == null || !Attribute.IsDefined(info, type)) return null; return Attribute.GetCustomAttribute(info, type); #endif } public static Type GetGenericListElementType(Type type) { IEnumerable interfaces; #if SIMPLE_JSON_TYPEINFO interfaces = type.GetTypeInfo().ImplementedInterfaces; #else interfaces = type.GetInterfaces(); #endif foreach (Type implementedInterface in interfaces) { if (IsTypeGeneric(implementedInterface) && implementedInterface.GetGenericTypeDefinition() == typeof (IList<>)) { return GetGenericTypeArguments(implementedInterface)[0]; } } return GetGenericTypeArguments(type)[0]; } public static Attribute GetAttribute(Type objectType, Type attributeType) { #if SIMPLE_JSON_TYPEINFO if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType)) return null; return objectType.GetTypeInfo().GetCustomAttribute(attributeType); #else if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType)) return null; return Attribute.GetCustomAttribute(objectType, attributeType); #endif } public static Type[] GetGenericTypeArguments(Type type) { #if SIMPLE_JSON_TYPEINFO return type.GetTypeInfo().GenericTypeArguments; #else return type.GetGenericArguments(); #endif } public static bool IsTypeGeneric(Type type) { return GetTypeInfo(type).IsGenericType; } public static bool IsTypeGenericeCollectionInterface(Type type) { if (!IsTypeGeneric(type)) return false; Type genericDefinition = type.GetGenericTypeDefinition(); return (genericDefinition == typeof(IList<>) || genericDefinition == typeof(ICollection<>) || genericDefinition == typeof(IEnumerable<>) #if SIMPLE_JSON_READONLY_COLLECTIONS || genericDefinition == typeof(IReadOnlyCollection<>) || genericDefinition == typeof(IReadOnlyList<>) #endif ); } public static bool IsAssignableFrom(Type type1, Type type2) { return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2)); } public static bool IsTypeDictionary(Type type) { #if SIMPLE_JSON_TYPEINFO if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) return true; #else if (typeof(System.Collections.IDictionary).IsAssignableFrom(type)) return true; #endif if (!GetTypeInfo(type).IsGenericType) return false; Type genericDefinition = type.GetGenericTypeDefinition(); return genericDefinition == typeof(IDictionary<,>); } public static bool IsNullableType(Type type) { return GetTypeInfo(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } public static object ToNullableType(object obj, Type nullableType) { return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture); } public static bool IsValueType(Type type) { return GetTypeInfo(type).IsValueType; } public static IEnumerable GetConstructors(Type type) { #if SIMPLE_JSON_TYPEINFO return type.GetTypeInfo().DeclaredConstructors; #else return type.GetConstructors(); #endif } public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType) { IEnumerable constructorInfos = GetConstructors(type); int i; bool matches; foreach (ConstructorInfo constructorInfo in constructorInfos) { ParameterInfo[] parameters = constructorInfo.GetParameters(); if (argsType.Length != parameters.Length) continue; i = 0; matches = true; foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters()) { if (parameterInfo.ParameterType != argsType[i]) { matches = false; break; } } if (matches) return constructorInfo; } return null; } public static IEnumerable GetProperties(Type type) { #if SIMPLE_JSON_TYPEINFO return type.GetRuntimeProperties(); #else return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); #endif } public static IEnumerable GetFields(Type type) { #if SIMPLE_JSON_TYPEINFO return type.GetRuntimeFields(); #else return type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); #endif } public static MethodInfo GetGetterMethodInfo(PropertyInfo propertyInfo) { #if SIMPLE_JSON_TYPEINFO return propertyInfo.GetMethod; #else return propertyInfo.GetGetMethod(true); #endif } public static MethodInfo GetSetterMethodInfo(PropertyInfo propertyInfo) { #if SIMPLE_JSON_TYPEINFO return propertyInfo.SetMethod; #else return propertyInfo.GetSetMethod(true); #endif } public static ConstructorDelegate GetContructor(ConstructorInfo constructorInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetConstructorByReflection(constructorInfo); #else return GetConstructorByExpression(constructorInfo); #endif } public static ConstructorDelegate GetContructor(Type type, params Type[] argsType) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetConstructorByReflection(type, argsType); #else return GetConstructorByExpression(type, argsType); #endif } public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo constructorInfo) { return delegate(object[] args) { return constructorInfo.Invoke(args); }; } public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType) { ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo); } #if !SIMPLE_JSON_NO_LINQ_EXPRESSION public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo constructorInfo) { ParameterInfo[] paramsInfo = constructorInfo.GetParameters(); ParameterExpression param = Expression.Parameter(typeof(object[]), "args"); Expression[] argsExp = new Expression[paramsInfo.Length]; for (int i = 0; i < paramsInfo.Length; i++) { Expression index = Expression.Constant(i); Type paramType = paramsInfo[i].ParameterType; Expression paramAccessorExp = Expression.ArrayIndex(param, index); Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType); argsExp[i] = paramCastExp; } NewExpression newExp = Expression.New(constructorInfo, argsExp); Expression> lambda = Expression.Lambda>(newExp, param); Func compiledLambda = lambda.Compile(); return delegate(object[] args) { return compiledLambda(args); }; } public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType) { ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo); } #endif public static GetDelegate GetGetMethod(PropertyInfo propertyInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetGetMethodByReflection(propertyInfo); #else return GetGetMethodByExpression(propertyInfo); #endif } public static GetDelegate GetGetMethod(FieldInfo fieldInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetGetMethodByReflection(fieldInfo); #else return GetGetMethodByExpression(fieldInfo); #endif } public static GetDelegate GetGetMethodByReflection(PropertyInfo propertyInfo) { MethodInfo methodInfo = GetGetterMethodInfo(propertyInfo); return delegate(object source) { return methodInfo.Invoke(source, EmptyObjects); }; } public static GetDelegate GetGetMethodByReflection(FieldInfo fieldInfo) { return delegate(object source) { return fieldInfo.GetValue(source); }; } #if !SIMPLE_JSON_NO_LINQ_EXPRESSION public static GetDelegate GetGetMethodByExpression(PropertyInfo propertyInfo) { MethodInfo getMethodInfo = GetGetterMethodInfo(propertyInfo); ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); Func compiled = Expression.Lambda>(Expression.TypeAs(Expression.Call(instanceCast, getMethodInfo), typeof(object)), instance).Compile(); return delegate(object source) { return compiled(source); }; } public static GetDelegate GetGetMethodByExpression(FieldInfo fieldInfo) { ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); MemberExpression member = Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo); GetDelegate compiled = Expression.Lambda(Expression.Convert(member, typeof(object)), instance).Compile(); return delegate(object source) { return compiled(source); }; } #endif public static SetDelegate GetSetMethod(PropertyInfo propertyInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetSetMethodByReflection(propertyInfo); #else return GetSetMethodByExpression(propertyInfo); #endif } public static SetDelegate GetSetMethod(FieldInfo fieldInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetSetMethodByReflection(fieldInfo); #else return GetSetMethodByExpression(fieldInfo); #endif } public static SetDelegate GetSetMethodByReflection(PropertyInfo propertyInfo) { MethodInfo methodInfo = GetSetterMethodInfo(propertyInfo); return delegate(object source, object value) { methodInfo.Invoke(source, new object[] { value }); }; } public static SetDelegate GetSetMethodByReflection(FieldInfo fieldInfo) { return delegate(object source, object value) { fieldInfo.SetValue(source, value); }; } #if !SIMPLE_JSON_NO_LINQ_EXPRESSION public static SetDelegate GetSetMethodByExpression(PropertyInfo propertyInfo) { MethodInfo setMethodInfo = GetSetterMethodInfo(propertyInfo); ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); ParameterExpression value = Expression.Parameter(typeof(object), "value"); UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); UnaryExpression valueCast = (!IsValueType(propertyInfo.PropertyType)) ? Expression.TypeAs(value, propertyInfo.PropertyType) : Expression.Convert(value, propertyInfo.PropertyType); Action compiled = Expression.Lambda>(Expression.Call(instanceCast, setMethodInfo, valueCast), new ParameterExpression[] { instance, value }).Compile(); return delegate(object source, object val) { compiled(source, val); }; } public static SetDelegate GetSetMethodByExpression(FieldInfo fieldInfo) { ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); ParameterExpression value = Expression.Parameter(typeof(object), "value"); Action compiled = Expression.Lambda>( Assign(Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo), Expression.Convert(value, fieldInfo.FieldType)), instance, value).Compile(); return delegate(object source, object val) { compiled(source, val); }; } public static BinaryExpression Assign(Expression left, Expression right) { #if SIMPLE_JSON_TYPEINFO return Expression.Assign(left, right); #else MethodInfo assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign"); BinaryExpression assignExpr = Expression.Add(left, right, assign); return assignExpr; #endif } private static class Assigner { public static T Assign(ref T left, T right) { return (left = right); } } #endif public sealed class ThreadSafeDictionary : IDictionary { private readonly object _lock = new object(); private readonly ThreadSafeDictionaryValueFactory _valueFactory; private Dictionary _dictionary; public ThreadSafeDictionary(ThreadSafeDictionaryValueFactory valueFactory) { _valueFactory = valueFactory; } private TValue Get(TKey key) { if (_dictionary == null) return AddValue(key); TValue value; if (!_dictionary.TryGetValue(key, out value)) return AddValue(key); return value; } private TValue AddValue(TKey key) { TValue value = _valueFactory(key); lock (_lock) { if (_dictionary == null) { _dictionary = new Dictionary(); _dictionary[key] = value; } else { TValue val; if (_dictionary.TryGetValue(key, out val)) return val; Dictionary dict = new Dictionary(_dictionary); dict[key] = value; _dictionary = dict; } } return value; } public void Add(TKey key, TValue value) { throw new NotImplementedException(); } public bool ContainsKey(TKey key) { return _dictionary.ContainsKey(key); } public ICollection Keys { get { return _dictionary.Keys; } } public bool Remove(TKey key) { throw new NotImplementedException(); } public bool TryGetValue(TKey key, out TValue value) { value = this[key]; return true; } public ICollection Values { get { return _dictionary.Values; } } public TValue this[TKey key] { get { return Get(key); } set { throw new NotImplementedException(); } } public void Add(KeyValuePair item) { throw new NotImplementedException(); } public void Clear() { throw new NotImplementedException(); } public bool Contains(KeyValuePair item) { throw new NotImplementedException(); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { throw new NotImplementedException(); } public int Count { get { return _dictionary.Count; } } public bool IsReadOnly { get { throw new NotImplementedException(); } } public bool Remove(KeyValuePair item) { throw new NotImplementedException(); } public IEnumerator> GetEnumerator() { return _dictionary.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return _dictionary.GetEnumerator(); } } } } } // ReSharper restore LoopCanBeConvertedToQuery // ReSharper restore RedundantExplicitArrayCreation // ReSharper restore SuggestUseVarKeywordEvident ================================================ FILE: src/Squirrel/SimpleSplat/AssemblyFinder.cs ================================================ using System; using System.Linq; using System.Reflection; namespace Squirrel.SimpleSplat { static class AssemblyFinder { public static T AttemptToLoadType(string fullTypeName) { var thisType = typeof(AssemblyFinder); var toSearch = new[] { thisType.AssemblyQualifiedName.Replace(thisType.FullName + ", ", ""), thisType.AssemblyQualifiedName.Replace(thisType.FullName + ", ", "").Replace(".Portable", ""), }.Select(x => new AssemblyName(x)).ToArray(); foreach (var assembly in toSearch) { var fullName = fullTypeName + ", " + assembly.FullName; var type = Type.GetType(fullName, false); if (type == null) continue; return (T)Activator.CreateInstance(type); } return default(T); } } } ================================================ FILE: src/Squirrel/SimpleSplat/Logging.cs ================================================ using System; using System.ComponentModel; using System.Reflection; using System.Globalization; using System.Text; using System.Threading; using System.Diagnostics; using System.Diagnostics.Contracts; namespace Squirrel.SimpleSplat { /* * Interfaces */ public enum LogLevel { Debug = 1, Info, Warn, Error, Fatal, } public interface ILogger { void Write([Localizable(false)] string message, LogLevel logLevel); LogLevel Level { get; set; } } public interface IFullLogger : ILogger { void Debug(T value); void Debug(IFormatProvider formatProvider, T value); void DebugException([Localizable(false)] string message, Exception exception); void Debug(IFormatProvider formatProvider, [Localizable(false)] string message, params object[] args); void Debug([Localizable(false)] string message); void Debug([Localizable(false)] string message, params object[] args); void Debug(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument argument); void Debug([Localizable(false)] string message, TArgument argument); void Debug(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); void Debug([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); void Debug(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); void Debug([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); void Info(T value); void Info(IFormatProvider formatProvider, T value); void InfoException([Localizable(false)] string message, Exception exception); void Info(IFormatProvider formatProvider, [Localizable(false)] string message, params object[] args); void Info([Localizable(false)] string message); void Info([Localizable(false)] string message, params object[] args); void Info(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument argument); void Info([Localizable(false)] string message, TArgument argument); void Info(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); void Info([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); void Info(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); void Info([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); void Warn(T value); void Warn(IFormatProvider formatProvider, T value); void WarnException([Localizable(false)] string message, Exception exception); void Warn(IFormatProvider formatProvider, [Localizable(false)] string message, params object[] args); void Warn([Localizable(false)] string message); void Warn([Localizable(false)] string message, params object[] args); void Warn(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument argument); void Warn([Localizable(false)] string message, TArgument argument); void Warn(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); void Warn([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); void Warn(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); void Warn([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); void Error(T value); void Error(IFormatProvider formatProvider, T value); void ErrorException([Localizable(false)] string message, Exception exception); void Error(IFormatProvider formatProvider, [Localizable(false)] string message, params object[] args); void Error([Localizable(false)] string message); void Error([Localizable(false)] string message, params object[] args); void Error(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument argument); void Error([Localizable(false)] string message, TArgument argument); void Error(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); void Error([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); void Error(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); void Error([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); void Fatal(T value); void Fatal(IFormatProvider formatProvider, T value); void FatalException([Localizable(false)] string message, Exception exception); void Fatal(IFormatProvider formatProvider, [Localizable(false)] string message, params object[] args); void Fatal([Localizable(false)] string message); void Fatal([Localizable(false)] string message, params object[] args); void Fatal(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument argument); void Fatal([Localizable(false)] string message, TArgument argument); void Fatal(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); void Fatal([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); void Fatal(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); void Fatal([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); } public interface ILogManager { IFullLogger GetLogger(Type type); } public class DefaultLogManager : ILogManager { readonly MemoizingMRUCache loggerCache; public DefaultLogManager(IDependencyResolver dependencyResolver = null) { dependencyResolver = dependencyResolver ?? SquirrelLocator.Current; loggerCache = new MemoizingMRUCache((type, _) => { var ret = dependencyResolver.GetService(); if (ret == null) { throw new Exception("Couldn't find an ILogger. This should never happen, your dependency resolver is probably broken."); } return new WrappingFullLogger(ret, type); }, 64); } static readonly IFullLogger nullLogger = new WrappingFullLogger(new NullLogger(), typeof(MemoizingMRUCache)); public IFullLogger GetLogger(Type type) { if (LogHost.suppressLogging) return nullLogger; if (type == typeof(MemoizingMRUCache)) return nullLogger; lock (loggerCache) { return loggerCache.Get(type); } } } public class FuncLogManager : ILogManager { readonly Func _inner; public FuncLogManager(Func getLogger) { _inner = getLogger; } public IFullLogger GetLogger(Type type) { return _inner(type); } } public static class LogManagerMixin { public static IFullLogger GetLogger(this ILogManager This) { return This.GetLogger(typeof(T)); } } public class NullLogger : ILogger { public void Write(string message, LogLevel logLevel) {} public LogLevel Level { get; set; } } public class DebugLogger : ILogger { public void Write(string message, LogLevel logLevel) { if ((int)logLevel < (int)Level) return; Debug.WriteLine(message); } public LogLevel Level { get; set; } } /* * LogHost / Logging Mixin */ /// /// "Implement" this interface in your class to get access to the Log() /// Mixin, which will give you a Logger that includes the class name in the /// log. /// [System.Runtime.InteropServices.ComVisible(false)] public interface IEnableLogger { } public static class LogHost { static internal bool suppressLogging = false; static readonly IFullLogger nullLogger = new WrappingFullLogger(new NullLogger(), typeof(string)); /// /// Use this logger inside miscellaneous static methods where creating /// a class-specific logger isn't really worth it. /// public static IFullLogger Default { get { if (suppressLogging) return nullLogger; var factory = SquirrelLocator.Current.GetService(); if (factory == null) { throw new Exception("ILogManager is null. This should never happen, your dependency resolver is broken"); } return factory.GetLogger(typeof(LogHost)); } } /// /// Call this method to write log entries on behalf of the current /// class. /// public static IFullLogger Log(this T This) where T : IEnableLogger { if (suppressLogging) return nullLogger; var factory = SquirrelLocator.Current.GetService(); if (factory == null) { throw new Exception("ILogManager is null. This should never happen, your dependency resolver is broken"); } return factory.GetLogger(); } } #region Extremely Dull Code Ahead public class WrappingFullLogger : IFullLogger { readonly ILogger _inner; readonly string prefix; readonly MethodInfo stringFormat; public WrappingFullLogger(ILogger inner, Type callingType) { _inner = inner; prefix = String.Format(CultureInfo.InvariantCulture, "{0}: ", callingType.Name); stringFormat = typeof (String).GetMethod("Format", new[] {typeof (IFormatProvider), typeof (string), typeof (object[])}); Contract.Requires(inner != null); Contract.Requires(stringFormat != null); } string InvokeStringFormat(IFormatProvider formatProvider, string message, object[] args) { var sfArgs = new object[3]; sfArgs[0] = formatProvider; sfArgs[1] = message; sfArgs[2] = args; return (string) stringFormat.Invoke(null, sfArgs); } public void Debug(T value) { _inner.Write(prefix + value, LogLevel.Debug); } public void Debug(IFormatProvider formatProvider, T value) { _inner.Write(String.Format(formatProvider, "{0}{1}", prefix, value), LogLevel.Debug); } public void DebugException(string message, Exception exception) { _inner.Write(String.Format("{0}{1}: {2}", prefix, message, exception), LogLevel.Debug); } public void Debug(IFormatProvider formatProvider, string message, params object[] args) { var result = InvokeStringFormat(formatProvider, message, args); _inner.Write(prefix + result, LogLevel.Debug); } public void Debug(string message) { _inner.Write(prefix + message, LogLevel.Debug); } public void Debug(string message, params object[] args) { var result = InvokeStringFormat(CultureInfo.InvariantCulture, message, args); _inner.Write(prefix + result, LogLevel.Debug); } public void Debug(IFormatProvider formatProvider, string message, TArgument argument) { _inner.Write(prefix + String.Format(formatProvider, message, argument), LogLevel.Debug); } public void Debug(string message, TArgument argument) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument), LogLevel.Debug); } public void Debug(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2) { _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2), LogLevel.Debug); } public void Debug(string message, TArgument1 argument1, TArgument2 argument2) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2), LogLevel.Debug); } public void Debug(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) { _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2, argument3), LogLevel.Debug); } public void Debug(string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2, argument3), LogLevel.Debug); } public void Info(T value) { _inner.Write(prefix + value, LogLevel.Info); } public void Info(IFormatProvider formatProvider, T value) { _inner.Write(String.Format(formatProvider, "{0}{1}", prefix, value), LogLevel.Info); } public void InfoException(string message, Exception exception) { _inner.Write(String.Format("{0}{1}: {2}", prefix, message, exception), LogLevel.Info); } public void Info(IFormatProvider formatProvider, string message, params object[] args) { var result = InvokeStringFormat(formatProvider, message, args); _inner.Write(prefix + result, LogLevel.Info); } public void Info(string message) { _inner.Write(prefix + message, LogLevel.Info); } public void Info(string message, params object[] args) { var result = InvokeStringFormat(CultureInfo.InvariantCulture, message, args); _inner.Write(prefix + result, LogLevel.Info); } public void Info(IFormatProvider formatProvider, string message, TArgument argument) { _inner.Write(prefix + String.Format(formatProvider, message, argument), LogLevel.Info); } public void Info(string message, TArgument argument) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument), LogLevel.Info); } public void Info(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2) { _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2), LogLevel.Info); } public void Info(string message, TArgument1 argument1, TArgument2 argument2) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2), LogLevel.Info); } public void Info(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) { _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2, argument3), LogLevel.Info); } public void Info(string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2, argument3), LogLevel.Info); } public void Warn(T value) { _inner.Write(prefix + value, LogLevel.Warn); } public void Warn(IFormatProvider formatProvider, T value) { _inner.Write(String.Format(formatProvider, "{0}{1}", prefix, value), LogLevel.Warn); } public void WarnException(string message, Exception exception) { _inner.Write(String.Format("{0}{1}: {2}", prefix, message, exception), LogLevel.Warn); } public void Warn(IFormatProvider formatProvider, string message, params object[] args) { var result = InvokeStringFormat(formatProvider, message, args); _inner.Write(prefix + result, LogLevel.Warn); } public void Warn(string message) { _inner.Write(prefix + message, LogLevel.Warn); } public void Warn(string message, params object[] args) { var result = InvokeStringFormat(CultureInfo.InvariantCulture, message, args); _inner.Write(prefix + result, LogLevel.Warn); } public void Warn(IFormatProvider formatProvider, string message, TArgument argument) { _inner.Write(prefix + String.Format(formatProvider, message, argument), LogLevel.Warn); } public void Warn(string message, TArgument argument) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument), LogLevel.Warn); } public void Warn(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2) { _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2), LogLevel.Warn); } public void Warn(string message, TArgument1 argument1, TArgument2 argument2) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2), LogLevel.Warn); } public void Warn(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) { _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2, argument3), LogLevel.Warn); } public void Warn(string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2, argument3), LogLevel.Warn); } public void Error(T value) { _inner.Write(prefix + value, LogLevel.Error); } public void Error(IFormatProvider formatProvider, T value) { _inner.Write(String.Format(formatProvider, "{0}{1}", prefix, value), LogLevel.Error); } public void ErrorException(string message, Exception exception) { _inner.Write(String.Format("{0}{1}: {2}", prefix, message, exception), LogLevel.Error); } public void Error(IFormatProvider formatProvider, string message, params object[] args) { var result = InvokeStringFormat(formatProvider, message, args); _inner.Write(prefix + result, LogLevel.Error); } public void Error(string message) { _inner.Write(prefix + message, LogLevel.Error); } public void Error(string message, params object[] args) { var result = InvokeStringFormat(CultureInfo.InvariantCulture, message, args); _inner.Write(prefix + result, LogLevel.Error); } public void Error(IFormatProvider formatProvider, string message, TArgument argument) { _inner.Write(prefix + String.Format(formatProvider, message, argument), LogLevel.Error); } public void Error(string message, TArgument argument) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument), LogLevel.Error); } public void Error(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2) { _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2), LogLevel.Error); } public void Error(string message, TArgument1 argument1, TArgument2 argument2) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2), LogLevel.Error); } public void Error(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) { _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2, argument3), LogLevel.Error); } public void Error(string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2, argument3), LogLevel.Error); } public void Fatal(T value) { _inner.Write(prefix + value, LogLevel.Fatal); } public void Fatal(IFormatProvider formatProvider, T value) { _inner.Write(String.Format(formatProvider, "{0}{1}", prefix, value), LogLevel.Fatal); } public void FatalException(string message, Exception exception) { _inner.Write(String.Format("{0}{1}: {2}", prefix, message, exception), LogLevel.Fatal); } public void Fatal(IFormatProvider formatProvider, string message, params object[] args) { var result = InvokeStringFormat(formatProvider, message, args); _inner.Write(prefix + result, LogLevel.Fatal); } public void Fatal(string message) { _inner.Write(prefix + message, LogLevel.Fatal); } public void Fatal(string message, params object[] args) { var result = InvokeStringFormat(CultureInfo.InvariantCulture, message, args); _inner.Write(prefix + result, LogLevel.Fatal); } public void Fatal(IFormatProvider formatProvider, string message, TArgument argument) { _inner.Write(prefix + String.Format(formatProvider, message, argument), LogLevel.Fatal); } public void Fatal(string message, TArgument argument) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument), LogLevel.Fatal); } public void Fatal(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2) { _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2), LogLevel.Fatal); } public void Fatal(string message, TArgument1 argument1, TArgument2 argument2) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2), LogLevel.Fatal); } public void Fatal(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) { _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2, argument3), LogLevel.Fatal); } public void Fatal(string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) { _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2, argument3), LogLevel.Fatal); } public void Write([Localizable(false)] string message, LogLevel logLevel) { _inner.Write(message, logLevel); } public LogLevel Level { get { return _inner.Level; } set { _inner.Level = value; } } } #endregion #if PORTABLE || WINDOWS_PHONE || NETFX_CORE [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] public sealed class LocalizableAttribute : Attribute { public LocalizableAttribute(bool isLocalizable) { } } #endif } // vim: tw=120 ts=4 sw=4 et : ================================================ FILE: src/Squirrel/SimpleSplat/MemoizingMRUCache.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Diagnostics.Contracts; namespace Squirrel.SimpleSplat { /// /// This data structure is a representation of a memoizing cache - i.e. a /// class that will evaluate a function, but keep a cache of recently /// evaluated parameters. /// /// Since this is a memoizing cache, it is important that this function be a /// "pure" function in the mathematical sense - that a key *always* maps to /// a corresponding return value. /// /// The type of the parameter to the calculation function. /// The type of the value returned by the calculation /// function. public class MemoizingMRUCache { private readonly Func calculationFunction; private readonly Action releaseFunction; private readonly int maxCacheSize; private LinkedList cacheMRUList; private Dictionary, TVal>> cacheEntries; /// /// Constructor /// /// The function whose results you want to cache, /// which is provided the key value, and an Tag object that is /// user-defined /// The size of the cache to maintain, after which old /// items will start to be thrown out. /// A function to call when a result gets /// evicted from the cache (i.e. because Invalidate was called or the /// cache is full) public MemoizingMRUCache(Func calculationFunc, int maxSize, Action onRelease = null) { Contract.Requires(calculationFunc != null); Contract.Requires(maxSize > 0); calculationFunction = calculationFunc; releaseFunction = onRelease; maxCacheSize = maxSize; InvalidateAll(); } public TVal Get(TParam key) { return Get(key, null); } /// /// Evaluates the function provided, returning the cached value if possible /// /// The value to pass to the calculation function. /// An additional optional user-specific parameter. /// public TVal Get(TParam key, object context = null) { Contract.Requires(key != null); if (cacheEntries.ContainsKey(key)) { var found = cacheEntries[key]; cacheMRUList.Remove(found.Item1); cacheMRUList.AddFirst(found.Item1); return found.Item2; } var result = calculationFunction(key, context); var node = new LinkedListNode(key); cacheMRUList.AddFirst(node); cacheEntries[key] = new Tuple, TVal>(node, result); maintainCache(); return result; } public bool TryGet(TParam key, out TVal result) { Contract.Requires(key != null); Tuple, TVal> output; var ret = cacheEntries.TryGetValue(key, out output); if (ret && output != null) { cacheMRUList.Remove(output.Item1); cacheMRUList.AddFirst(output.Item1); result = output.Item2; } else { result = default(TVal); } return ret; } /// /// Ensure that the next time this key is queried, the calculation /// function will be called. /// public void Invalidate(TParam key) { Contract.Requires(key != null); if (!cacheEntries.ContainsKey(key)) return; var to_remove = cacheEntries[key]; if (releaseFunction != null) releaseFunction(to_remove.Item2); cacheMRUList.Remove(to_remove.Item1); cacheEntries.Remove(key); } /// /// Invalidate all items in the cache /// public void InvalidateAll() { if (releaseFunction == null || cacheEntries == null) { cacheMRUList = new LinkedList(); cacheEntries = new Dictionary, TVal>>(); return; } if (cacheEntries.Count == 0) return; /* We have to remove them one-by-one to call the release function * We ToArray() this so we don't get a "modifying collection while * enumerating" exception. */ foreach (var v in cacheEntries.Keys.ToArray()) { Invalidate(v); } } /// /// Returns all values currently in the cache /// /// public IEnumerable CachedValues() { return cacheEntries.Select(x => x.Value.Item2); } void maintainCache() { while (cacheMRUList.Count > maxCacheSize) { var to_remove = cacheMRUList.Last.Value; if (releaseFunction != null) releaseFunction(cacheEntries[to_remove].Item2); cacheEntries.Remove(cacheMRUList.Last.Value); cacheMRUList.RemoveLast(); } } [ContractInvariantMethod] void Invariants() { Contract.Invariant(cacheEntries.Count == cacheMRUList.Count); Contract.Invariant(cacheEntries.Count <= maxCacheSize); } } } // vim: tw=120 ts=4 sw=4 et : ================================================ FILE: src/Squirrel/SimpleSplat/ModeDetector.cs ================================================ using System; namespace Squirrel.SimpleSplat { public interface IModeDetector { bool? InUnitTestRunner(); bool? InDesignMode(); } public static class ModeDetector { static ModeDetector() { var platModeDetector = AssemblyFinder.AttemptToLoadType("Squirrel.SimpleSplat.PlatformModeDetector"); current = platModeDetector; } static IModeDetector current { get; set; } public static void OverrideModeDetector(IModeDetector modeDetector) { current = modeDetector; cachedInDesignModeResult = null; cachedInUnitTestRunnerResult = null; } static bool? cachedInUnitTestRunnerResult; public static bool InUnitTestRunner() { if (cachedInUnitTestRunnerResult.HasValue) return cachedInUnitTestRunnerResult.Value; if (current != null) { cachedInUnitTestRunnerResult = current.InUnitTestRunner(); if (cachedInUnitTestRunnerResult.HasValue) return cachedInUnitTestRunnerResult.Value; } // We have no sane platform-independent way to detect a unit test // runner :-/ return false; } static bool? cachedInDesignModeResult; public static bool InDesignMode() { if (cachedInDesignModeResult.HasValue) return cachedInDesignModeResult.Value; if (current != null) { cachedInDesignModeResult = current.InDesignMode(); if (cachedInDesignModeResult.HasValue) return cachedInDesignModeResult.Value; } // Check Silverlight / WP8 Design Mode var type = Type.GetType("System.ComponentModel.DesignerProperties, System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", false); if (type != null) { var mInfo = type.GetMethod("GetIsInDesignMode"); var dependencyObject = Type.GetType("System.Windows.Controls.Border, System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", false); if (dependencyObject != null) { cachedInDesignModeResult = (bool)mInfo.Invoke(null, new object[] { Activator.CreateInstance(dependencyObject) }); } } else if((type = Type.GetType("System.ComponentModel.DesignerProperties, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", false)) != null) { // loaded the assembly, could be .net var mInfo = type.GetMethod("GetIsInDesignMode"); Type dependencyObject = Type.GetType("System.Windows.DependencyObject, WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", false); if (dependencyObject != null) { cachedInDesignModeResult = (bool)mInfo.Invoke(null, new object[] { Activator.CreateInstance(dependencyObject) }); } } else if ((type = Type.GetType("Windows.ApplicationModel.DesignMode, Windows, ContentType=WindowsRuntime", false)) != null) { // check WinRT next cachedInDesignModeResult = (bool)type.GetProperty("DesignModeEnabled").GetMethod.Invoke(null, null); } else { cachedInDesignModeResult = false; } return cachedInDesignModeResult.GetValueOrDefault(); } } } ================================================ FILE: src/Squirrel/SimpleSplat/PlatformModeDetector.cs ================================================ using System; using System.IO; using System.Reflection; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; #if SILVERLIGHT using System.Windows; #elif NETFX_CORE using Windows.ApplicationModel; #endif namespace Squirrel.SimpleSplat { public class PlatformModeDetector : IModeDetector { public bool? InUnitTestRunner() { var testAssemblies = new[] { "CSUNIT", "NUNIT", "XUNIT", "MBUNIT", "NBEHAVE", }; try { return searchForAssembly(testAssemblies); } catch (Exception) { return null; } } public bool? InDesignMode() { #if SILVERLIGHT if (Application.Current.RootVisual != null) { return System.ComponentModel.DesignerProperties.GetIsInDesignMode(Application.Current.RootVisual); } return false; #elif NETFX_CORE return DesignMode.DesignModeEnabled; #else var designEnvironments = new[] { "BLEND.EXE", "XDESPROC.EXE", }; var entry = Assembly.GetEntryAssembly(); if (entry != null) { var exeName = (new FileInfo(entry.Location)).Name.ToUpperInvariant(); if (designEnvironments.Any(x => x.Contains(exeName))) { return true; } } return false; #endif } static bool searchForAssembly(IEnumerable assemblyList) { #if SILVERLIGHT return Deployment.Current.Parts.Any(x => assemblyList.Any(name => x.Source.ToUpperInvariant().Contains(name))); #elif NETFX_CORE var depPackages = Package.Current.Dependencies.Select(x => x.Id.FullName); if (depPackages.Any(x => assemblyList.Any(name => x.ToUpperInvariant().Contains(name)))) return true; var fileTask = Task.Factory.StartNew(async () => { var files = await Package.Current.InstalledLocation.GetFilesAsync(); return files.Select(x => x.Path).ToArray(); }, TaskCreationOptions.HideScheduler).Unwrap(); return fileTask.Result.Any(x => assemblyList.Any(name => x.ToUpperInvariant().Contains(name))); #else return AppDomain.CurrentDomain.GetAssemblies() .Any(x => assemblyList.Any(name => x.FullName.ToUpperInvariant().Contains(name))); #endif } } } ================================================ FILE: src/Squirrel/SimpleSplat/ServiceLocation.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading; namespace Squirrel.SimpleSplat { public static class SquirrelLocator { [ThreadStatic] static IDependencyResolver unitTestDependencyResolver; static IDependencyResolver dependencyResolver; static readonly List resolverChanged = new List(); static SquirrelLocator() { var r = new ModernDependencyResolver(); dependencyResolver = r; RegisterResolverCallbackChanged(() => { if (SquirrelLocator.CurrentMutable == null) return; SquirrelLocator.CurrentMutable.InitializeSplat(); }); } /// /// Gets or sets the dependency resolver. This class is used throughout /// libraries for many internal operations as well as for general use /// by applications. If this isn't assigned on startup, a default, highly /// capable implementation will be used, and it is advised for most people /// to simply use the default implementation. /// /// The dependency resolver. public static IDependencyResolver Current { get { return unitTestDependencyResolver ?? dependencyResolver; } set { if (ModeDetector.InUnitTestRunner()) { unitTestDependencyResolver = value; dependencyResolver = dependencyResolver ?? value; } else { dependencyResolver = value; } var currentCallbacks = default(Action[]); lock (resolverChanged) { // NB: Prevent deadlocks should we reenter this setter from // the callbacks currentCallbacks = resolverChanged.ToArray(); } foreach (var block in currentCallbacks) block(); } } /// /// Convenience property to return the DependencyResolver cast to a /// MutableDependencyResolver. The default resolver is also a mutable /// resolver, so this will be non-null. Use this to register new types /// on startup if you are using the default resolver /// public static IMutableDependencyResolver CurrentMutable { get { return Current as IMutableDependencyResolver; } set { Current = value; } } /// /// This method allows libraries to register themselves to be set up /// whenever the dependency resolver changes. Applications should avoid /// this method, it is usually used for libraries that depend on service /// location. /// /// A callback that is invoked when the /// resolver is changed. This callback is also invoked immediately, /// to configure the current resolver. /// When disposed, removes the callback. You probably can /// ignore this. public static IDisposable RegisterResolverCallbackChanged(Action callback) { lock (resolverChanged) { resolverChanged.Add(callback); } // NB: We always immediately invoke the callback to set up the // current resolver with whatever we've got callback(); return new ActionDisposable(() => { lock (resolverChanged) resolverChanged.Remove(callback); }); } } /// /// Represents a dependency resolver, a service to look up global class /// instances or types. /// public interface IDependencyResolver : IDisposable { /// /// Gets an instance of the given . Must return null /// if the service is not available (must not throw). /// /// The object type. /// The requested object, if found; null otherwise. object GetService(Type serviceType, string contract = null); /// /// Gets all instances of the given . Must return an empty /// collection if the service is not available (must not return null or throw). /// /// The object type. /// A sequence of instances of the requested . The sequence /// should be empty (not null) if no objects of the given type are available. IEnumerable GetServices(Type serviceType, string contract = null); } /// /// Represents a dependency resolver where types can be registered after /// setup. /// public interface IMutableDependencyResolver : IDependencyResolver { void Register(Func factory, Type serviceType, string contract = null); /// /// Register a callback to be called when a new service matching the type /// and contract is registered. /// /// When registered, the callback is also called for each currently matching /// service. /// /// When disposed removes the callback /// Service type. /// Contract. /// Callback. IDisposable ServiceRegistrationCallback(Type serviceType, string contract, Action callback); } public static class DependencyResolverMixins { /// /// Gets an instance of the given . Must return null /// if the service is not available (must not throw). /// /// The object type. /// The requested object, if found; null otherwise. public static T GetService(this IDependencyResolver This, string contract = null) { return (T)This.GetService(typeof(T), contract); } /// /// Gets all instances of the given . Must return an empty /// collection if the service is not available (must not return null or throw). /// /// The object type. /// A sequence of instances of the requested . The sequence /// should be empty (not null) if no objects of the given type are available. public static IEnumerable GetServices(this IDependencyResolver This, string contract = null) { return This.GetServices(typeof(T), contract).Cast(); } public static IDisposable ServiceRegistrationCallback(this IMutableDependencyResolver This, Type serviceType, Action callback) { return This.ServiceRegistrationCallback(serviceType, null, callback); } /// /// Override the default Dependency Resolver until the object returned /// is disposed. /// /// The test resolver to use. public static IDisposable WithResolver(this IDependencyResolver resolver) { var origResolver = SquirrelLocator.Current; SquirrelLocator.Current = resolver; return new ActionDisposable(() => SquirrelLocator.Current = origResolver); } public static void RegisterConstant(this IMutableDependencyResolver This, object value, Type serviceType, string contract = null) { This.Register(() => value, serviceType, contract); } public static void RegisterLazySingleton(this IMutableDependencyResolver This, Func valueFactory, Type serviceType, string contract = null) { var val = new Lazy(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication); This.Register(() => val.Value, serviceType, contract); } public static void InitializeSplat(this IMutableDependencyResolver This) { This.Register(() => new DefaultLogManager(), typeof(ILogManager)); This.Register(() => new DebugLogger(), typeof(ILogger)); } } /// /// This class is a dependency resolver written for modern C# 5.0 times. /// It implements all registrations via a Factory method. With the power /// of Closures, you can actually implement most lifetime styles (i.e. /// construct per call, lazy construct, singleton) using this. /// /// Unless you have a very compelling reason not to, this is the only class /// you need in order to do dependency resolution, don't bother with using /// a full IoC container. /// public class ModernDependencyResolver : IMutableDependencyResolver { Dictionary, List>> _registry; Dictionary, List>> _callbackRegistry; public ModernDependencyResolver() : this(null) { } protected ModernDependencyResolver(Dictionary, List>> registry) { _registry = registry != null ? registry.ToDictionary(k => k.Key, v => v.Value.ToList()) : new Dictionary, List>>(); _callbackRegistry = new Dictionary, List>>(); } public void Register(Func factory, Type serviceType, string contract = null) { var pair = Tuple.Create(serviceType, contract ?? string.Empty); if (!_registry.ContainsKey(pair)) { _registry[pair] = new List>(); } _registry[pair].Add(factory); if (_callbackRegistry.ContainsKey(pair)) { List> toRemove = null; foreach (var callback in _callbackRegistry[pair]) { var remove = false; var disp = new ActionDisposable(() => { remove = true; }); callback(disp); if (remove) { if (toRemove == null) { toRemove = new List>(); } toRemove.Add(callback); } } if (toRemove != null) { foreach (var c in toRemove) { _callbackRegistry[pair].Remove(c); } } } } public object GetService(Type serviceType, string contract = null) { var pair = Tuple.Create(serviceType, contract ?? string.Empty); if (!_registry.ContainsKey(pair)) return default(object); var ret = _registry[pair].Last(); return ret(); } public IEnumerable GetServices(Type serviceType, string contract = null) { var pair = Tuple.Create(serviceType, contract ?? string.Empty); if (!_registry.ContainsKey(pair)) return Enumerable.Empty(); return _registry[pair].Select(x => x()).ToList(); } public IDisposable ServiceRegistrationCallback(Type serviceType, string contract, Action callback) { var pair = Tuple.Create(serviceType, contract ?? string.Empty); if (!_callbackRegistry.ContainsKey(pair)) { _callbackRegistry[pair] = new List>(); } _callbackRegistry[pair].Add(callback); var disp = new ActionDisposable(() => { _callbackRegistry[pair].Remove(callback); }); if (_registry.ContainsKey(pair)) { foreach (var s in _registry[pair]) { callback(disp); } } return disp; } public ModernDependencyResolver Duplicate() { return new ModernDependencyResolver(_registry); } public void Dispose() { _registry = null; } } /// /// A simple dependency resolver which takes Funcs for all its actions. /// GetService is always implemented via GetServices().LastOrDefault() /// public class FuncDependencyResolver : IMutableDependencyResolver { readonly Func> innerGetServices; readonly Action, Type, string> innerRegister; readonly Dictionary, List>> _callbackRegistry = new Dictionary, List>>(); IDisposable inner; public FuncDependencyResolver( Func> getAllServices, Action, Type, string> register = null, IDisposable toDispose = null) { innerGetServices = getAllServices; innerRegister = register; inner = toDispose ?? ActionDisposable.Empty; } public object GetService(Type serviceType, string contract = null) { return (GetServices(serviceType, contract) ?? Enumerable.Empty()).LastOrDefault(); } public IEnumerable GetServices(Type serviceType, string contract = null) { return innerGetServices(serviceType, contract); } public void Dispose() { Interlocked.Exchange(ref inner, ActionDisposable.Empty).Dispose(); } public void Register(Func factory, Type serviceType, string contract = null) { if (innerRegister == null) throw new NotImplementedException(); innerRegister(factory, serviceType, contract); var pair = Tuple.Create(serviceType, contract ?? string.Empty); if (_callbackRegistry.ContainsKey(pair)) { List> toRemove = null; foreach (var callback in _callbackRegistry[pair]) { var remove = false; var disp = new ActionDisposable(() => { remove = true; }); callback(disp); if (remove) { if (toRemove == null) { toRemove = new List>(); } toRemove.Add(callback); } } if (toRemove != null) { foreach (var c in toRemove) { _callbackRegistry[pair].Remove(c); } } } } public IDisposable ServiceRegistrationCallback(Type serviceType, string contract, Action callback) { var pair = Tuple.Create(serviceType, contract ?? string.Empty); if (!_callbackRegistry.ContainsKey(pair)) { _callbackRegistry[pair] = new List>(); } _callbackRegistry[pair].Add(callback); return new ActionDisposable(() => { _callbackRegistry[pair].Remove(callback); }); } } sealed class ActionDisposable : IDisposable { Action block; public static IDisposable Empty { get { return new ActionDisposable(() => {}); } } public ActionDisposable(Action block) { this.block = block; } public void Dispose() { Interlocked.Exchange(ref block, () => {})(); } } } ================================================ FILE: src/Squirrel/Squirrel.csproj ================================================  net45;netstandard2.0 9 Squirrel Squirrel true ================================================ FILE: src/Squirrel/SquirrelAwareApp.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Squirrel.SimpleSplat; namespace Squirrel { /// /// SquirrelAwareApp helps you to handle Squirrel app activation events /// correctly. /// public static class SquirrelAwareApp { /// /// Call this method as early as possible in app startup. This method /// will dispatch to your methods to set up your app. Depending on the /// parameter, your app will exit after this method is called, which /// is required by Squirrel. UpdateManager has methods to help you to /// do this, such as CreateShortcutForThisExe. /// /// Called when your app is initially /// installed. Set up app shortcuts here as well as file associations. /// /// Called when your app is updated to a new /// version. /// Called when your app is no longer the /// latest version (i.e. they have installed a new version and your app /// is now the old version) /// Called when your app is uninstalled /// via Programs and Features. Remove all of the things that you created /// in onInitialInstall. /// Called the first time an app is run after /// being installed. Your application will **not** exit after this is /// dispatched, you should use this as a hint (i.e. show a 'Welcome' /// screen, etc etc. /// Use in a unit-test runner to mock the /// arguments. In your app, leave this as null. public static void HandleEvents( Action onInitialInstall = null, Action onAppUpdate = null, Action onAppObsoleted = null, Action onAppUninstall = null, Action onFirstRun = null, string[] arguments = null) { Action defaultBlock = (v => { }); var args = arguments ?? Environment.GetCommandLineArgs().Skip(1).ToArray(); if (args.Length == 0) return; var lookup = new[] { new { Key = "--squirrel-install", Value = onInitialInstall ?? defaultBlock }, new { Key = "--squirrel-updated", Value = onAppUpdate ?? defaultBlock }, new { Key = "--squirrel-obsolete", Value = onAppObsoleted ?? defaultBlock }, new { Key = "--squirrel-uninstall", Value = onAppUninstall ?? defaultBlock }, }.ToDictionary(k => k.Key, v => v.Value); if (args[0] == "--squirrel-firstrun") { (onFirstRun ?? (() => {}))(); return; } if (args.Length != 2) return; if (!lookup.ContainsKey(args[0])) return; var version = args[1].ToSemanticVersion().Version; try { lookup[args[0]](version); if (!ModeDetector.InUnitTestRunner()) Environment.Exit(0); } catch (Exception ex) { LogHost.Default.ErrorException("Failed to handle Squirrel events", ex); if (!ModeDetector.InUnitTestRunner()) Environment.Exit(-1); } } } } ================================================ FILE: src/Squirrel/SquirrelAwareExecutableDetector.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using Mono.Cecil; namespace Squirrel { static class SquirrelAwareExecutableDetector { public static List GetAllSquirrelAwareApps(string directory, int minimumVersion = 1) { var di = new DirectoryInfo(directory); return di.EnumerateFiles() .Where(x => x.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) .Select(x => x.FullName) .Where(x => (GetPESquirrelAwareVersion(x) ?? -1) >= minimumVersion) .ToList(); } public static int? GetPESquirrelAwareVersion(string executable) { if (!File.Exists(executable)) return null; var fullname = Path.GetFullPath(executable); return Utility.Retry(() => GetAssemblySquirrelAwareVersion(fullname) ?? GetVersionBlockSquirrelAwareValue(fullname)); } static int? GetAssemblySquirrelAwareVersion(string executable) { try { using (var assembly = AssemblyDefinition.ReadAssembly(executable)) { if (!assembly.HasCustomAttributes) return null; var attrs = assembly.CustomAttributes; var attribute = attrs.FirstOrDefault(x => { if (x.AttributeType.FullName != typeof(AssemblyMetadataAttribute).FullName) return false; if (x.ConstructorArguments.Count != 2) return false; return x.ConstructorArguments[0].Value.ToString() == "SquirrelAwareVersion"; }); if (attribute == null) return null; int result; if (!Int32.TryParse(attribute.ConstructorArguments[1].Value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture, out result)) { return null; } return result; } } catch (FileLoadException) { return null; } catch (BadImageFormatException) { return null; } } static int? GetVersionBlockSquirrelAwareValue(string executable) { int size = NativeMethods.GetFileVersionInfoSize(executable, IntPtr.Zero); // Nice try, buffer overflow if (size <= 0 || size > 4096) return null; var buf = new byte[size]; if (!NativeMethods.GetFileVersionInfo(executable, 0, size, buf)) return null; const string englishUS = "040904B0"; const string neutral = "000004B0"; var supportedLanguageCodes = new[] {englishUS, neutral}; IntPtr result; int resultSize; if (!supportedLanguageCodes.Any( languageCode => NativeMethods.VerQueryValue( buf, $"\\StringFileInfo\\{languageCode}\\SquirrelAwareVersion", out result, out resultSize ) )) { return null; } // NB: I have **no** idea why, but Atom.exe won't return the version // number "1" despite it being in the resource file and being 100% // identical to the version block that actually works. I've got stuff // to ship, so we're just going to return '1' if we find the name in // the block at all. I hate myself for this. return 1; #if __NOT__DEFINED_EVAR__ int ret; string resultData = Marshal.PtrToStringAnsi(result, resultSize-1 /* Subtract one for null terminator */); if (!Int32.TryParse(resultData, NumberStyles.Integer, CultureInfo.CurrentCulture, out ret)) return null; return ret; #endif } } } ================================================ FILE: src/Squirrel/TaskbarHelper.cs ================================================ using System; using System.IO; using System.Linq; using System.Runtime.InteropServices; namespace Squirrel.Shell { public static class TaskbarHelper { public static bool IsPinnedToTaskbar(string executablePath) { var taskbarPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar"); return Directory .GetFiles(taskbarPath, "*.lnk") .Select(pinnedShortcut => new ShellLink(pinnedShortcut)) .Any(shortcut => String.Equals(shortcut.Target, executablePath, StringComparison.OrdinalIgnoreCase)); } public static void PinToTaskbar(string executablePath) { pinUnpin(executablePath, "pin to taskbar"); if (!IsPinnedToTaskbar(executablePath)) { throw new Exception("Pinning executable to taskbar failed."); } } public static void UnpinFromTaskbar(string executablePath) { pinUnpin(executablePath, "unpin from taskbar"); if (IsPinnedToTaskbar(executablePath)) { throw new Exception("Executable is still pinned to taskbar."); } } static void pinUnpin(string executablePath, string verbToExecute) { if (!File.Exists(executablePath)) { throw new FileNotFoundException(executablePath); } dynamic shellApplication = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application")); try { var path = Path.GetDirectoryName(executablePath); var fileName = Path.GetFileName(executablePath); dynamic directory = shellApplication.NameSpace(path); dynamic link = directory.ParseName(fileName); dynamic verbs = link.Verbs(); for (var i = 0; i < verbs.Count(); i++) { dynamic verb = verbs.Item(i); string verbName = verb.Name.Replace(@"&", String.Empty).ToLower(); if (verbName.Equals(verbToExecute)) { verb.DoIt(); } } } finally { Marshal.ReleaseComObject(shellApplication); } } } } ================================================ FILE: src/Squirrel/TrayHelper.cs ================================================ using Microsoft.Win32; using Squirrel.SimpleSplat; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Squirrel { public class TrayStateChanger : IEnableLogger { public List GetTrayItems() { var instance = new TrayNotify(); try { if (useLegacyInterface()) { return getTrayItemsWin7(instance); } else { return getTrayItems(instance); } } finally { Marshal.ReleaseComObject(instance); } } public void PromoteTrayItem(string exeToPromote) { var instance = new TrayNotify(); try { var items = default(List); var legacy = useLegacyInterface(); if (legacy) { items = getTrayItemsWin7(instance); } else { items = getTrayItems(instance); } exeToPromote = exeToPromote.ToLowerInvariant(); for (int i = 0; i < items.Count; i++) { var item = items[i]; var exeName = item.exe_name.ToLowerInvariant(); if (!exeName.Contains(exeToPromote)) continue; if (item.preference != NOTIFYITEM_PREFERENCE.PREFERENCE_SHOW_WHEN_ACTIVE) continue; item.preference = NOTIFYITEM_PREFERENCE.PREFERENCE_SHOW_ALWAYS; var writable = NOTIFYITEM_Writable.fromNotifyItem(item); if (legacy) { var notifier = (ITrayNotifyWin7)instance; notifier.SetPreference(ref writable); } else { var notifier = (ITrayNotify)instance; notifier.SetPreference(ref writable); } } } catch (Exception ex) { Console.WriteLine("Failed to promote Tray icon: " + ex.ToString()); } finally { Marshal.ReleaseComObject(instance); } } public unsafe void RemoveDeadEntries(List executablesInPackage, string rootAppDirectory, string currentAppVersion) { var iconStreamData = default(byte[]); try { iconStreamData = (byte[])Registry.GetValue("HKEY_CURRENT_USER\\Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\TrayNotify", "IconStreams", new byte[] { 00 }); } catch (Exception ex) { Console.WriteLine("Couldn't load IconStreams key, bailing: " + ex.ToString()); return; } if (iconStreamData == null || iconStreamData.Length < 20) return; var toKeep = new List(); var header = default(IconStreamsHeader); fixed (byte* b = iconStreamData) { header = (IconStreamsHeader)Marshal.PtrToStructure((IntPtr)b, typeof(IconStreamsHeader)); byte* current; if (header.count <= 1) return; for (int i=0; i < header.count; i++) { var offset = Marshal.SizeOf(typeof(IconStreamsHeader)) + (i * Marshal.SizeOf(typeof(IconStreamsItem))); if (offset > iconStreamData.Length) { this.Log().Error("Corrupted IconStreams regkey, bailing"); return; } current = b + offset; var item = (IconStreamsItem)Marshal.PtrToStructure((IntPtr)current, typeof(IconStreamsItem)); try { var path = item.ExePath.ToLowerInvariant(); // Someone completely unrelated? Keep it! if (!executablesInPackage.Any(exe => path.Contains(exe.ToLowerInvariant()))) { goto keepItem; } // Not an installed app? Keep it! if (!path.StartsWith(rootAppDirectory, StringComparison.Ordinal)) { goto keepItem; } // The current version? Keep it! if (path.Contains("app-" + currentAppVersion)) { goto keepItem; } // Don't keep this item, remove it from IconStreams continue; keepItem: var newItem = new byte[Marshal.SizeOf(typeof(IconStreamsItem))]; Array.Copy(iconStreamData, offset, newItem, 0, newItem.Length); toKeep.Add(newItem); } catch (Exception ex) { this.Log().ErrorException("Failed to parse IconStreams regkey", ex); return; } } if (header.count == toKeep.Count) { return; } header.count = (uint)toKeep.Count; Marshal.StructureToPtr(header, (IntPtr)b, false); current = b + Marshal.SizeOf(typeof(IconStreamsHeader)); for(int i = 0; i < toKeep.Count; i++) { Marshal.Copy(toKeep[i], 0, (IntPtr)current, toKeep[i].Length); current += toKeep[i].Length; } } try { var newSize = Marshal.SizeOf(typeof(IconStreamsHeader)) + (toKeep.Count * Marshal.SizeOf(typeof(IconStreamsItem))); var toSave = new byte[newSize]; Array.Copy(iconStreamData, toSave, newSize); Registry.SetValue("HKEY_CURRENT_USER\\Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\TrayNotify", "IconStreams", toSave); } catch (Exception ex) { Console.WriteLine("Failed to write new IconStreams regkey: " + ex.ToString()); } return; } static List getTrayItems(TrayNotify instance) { var notifier = (ITrayNotify)instance; var callback = new NotificationCb(); var handle = default(ulong); notifier.RegisterCallback(callback, out handle); notifier.UnregisterCallback(handle); return callback.items; } static List getTrayItemsWin7(TrayNotify instance) { var notifier = (ITrayNotifyWin7)instance; var callback = new NotificationCb(); notifier.RegisterCallback(callback); notifier.RegisterCallback(null); return callback.items; } class NotificationCb : INotificationCb { public readonly List items = new List(); public void Notify([In] uint nEvent, [In] ref NOTIFYITEM notifyItem) { items.Add(notifyItem); } } static bool useLegacyInterface() { var ver = Environment.OSVersion.Version; if (ver.Major < 6) return true; if (ver.Major > 6) return false; // Windows 6.2 and higher use new interface return ver.Minor <= 1; } } // The known values for NOTIFYITEM's dwPreference member. public enum NOTIFYITEM_PREFERENCE { // In Windows UI: "Only show notifications." PREFERENCE_SHOW_WHEN_ACTIVE = 0, // In Windows UI: "Hide icon and notifications." PREFERENCE_SHOW_NEVER = 1, // In Windows UI: "Show icon and notifications." PREFERENCE_SHOW_ALWAYS = 2 }; // NOTIFYITEM describes an entry in Explorer's registry of status icons. // Explorer keeps entries around for a process even after it exits. public struct NOTIFYITEM { [MarshalAs(UnmanagedType.LPWStr)] public string exe_name; // The file name of the creating executable. [MarshalAs(UnmanagedType.LPWStr)] public string tip; // The last hover-text value associated with this status // item. public IntPtr icon; // The icon associated with this status item. public IntPtr hwnd; // The HWND associated with the status item. public NOTIFYITEM_PREFERENCE preference; // Determines the behavior of the icon with respect to // the taskbar public uint id; // The ID specified by the application. (hWnd, uID) is // unique. public Guid guid; // The GUID specified by the application, alternative to // uID. }; public struct NOTIFYITEM_Writable { public IntPtr exe_name; // The file name of the creating executable. public IntPtr tip; // The last hover-text value associated with this status // item. public IntPtr icon; // The icon associated with this status item. public IntPtr hwnd; // The HWND associated with the status item. public NOTIFYITEM_PREFERENCE preference; // Determines the behavior of the icon with respect to // the taskbar public uint id; // The ID specified by the application. (hWnd, uID) is // unique. public Guid guid; // The GUID specified by the application, alternative to // uID. public static NOTIFYITEM_Writable fromNotifyItem(NOTIFYITEM item) { return new NOTIFYITEM_Writable { exe_name = Marshal.StringToCoTaskMemAuto(item.exe_name), tip = Marshal.StringToCoTaskMemAuto(item.tip), icon = item.icon, hwnd = item.hwnd, preference = item.preference, id = item.id, guid = item.guid }; } }; [ComImport] [Guid("D782CCBA-AFB0-43F1-94DB-FDA3779EACCB")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface INotificationCb { void Notify([In]uint nEvent, [In] ref NOTIFYITEM notifyItem); } [ComImport] [Guid("FB852B2C-6BAD-4605-9551-F15F87830935")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface ITrayNotifyWin7 { void RegisterCallback([MarshalAs(UnmanagedType.Interface)]INotificationCb callback); void SetPreference([In] ref NOTIFYITEM_Writable notifyItem); void EnableAutoTray([In] bool enabled); } [ComImport] [Guid("D133CE13-3537-48BA-93A7-AFCD5D2053B4")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface ITrayNotify { void RegisterCallback([MarshalAs(UnmanagedType.Interface)]INotificationCb callback, [Out] out ulong handle); void UnregisterCallback([In] ulong handle); void SetPreference([In] ref NOTIFYITEM_Writable notifyItem); void EnableAutoTray([In] bool enabled); void DoAction([In] bool enabled); } [ComImport, Guid("25DEAD04-1EAC-4911-9E3A-AD0A4AB560FD")] class TrayNotify { } public struct IconStreamsHeader { public uint cbSize; public uint unknown1; public uint unknown2; public uint count; public uint unknown3; } public struct IconStreamsItem { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 528)] public byte[] exe_path; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1112)] public byte[] dontcare; public unsafe string ExePath { get { byte[] exeCopy = new byte[exe_path.Length]; // https://raw.githubusercontent.com/lestert2005/SystemTrayModder/b1061f3758f8ff9c43d77157c7a62c7e5cc6885d/source/Program.cs for (int i=0; i < exe_path.Length; i++) { var b = exe_path[i]; if (b > 64 && b < 91) { exeCopy[i] = (byte)((b - 64 + 13) % 26 + 64); continue; } if (b > 96 && b < 123) { exeCopy[i] = (byte)((b - 96 + 13) % 26 + 96); continue; } exeCopy[i] = b; } fixed (byte* b = exeCopy) { return Marshal.PtrToStringUni((IntPtr)b); } } } } } ================================================ FILE: src/Squirrel/UpdateInfo.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Runtime.Serialization; using Squirrel.SimpleSplat; namespace Squirrel { [DataContract] public class UpdateInfo : IEnableLogger { [DataMember] public ReleaseEntry CurrentlyInstalledVersion { get; protected set; } [DataMember] public ReleaseEntry FutureReleaseEntry { get; protected set; } [DataMember] public List ReleasesToApply { get; protected set; } [IgnoreDataMember] public bool IsBootstrapping { get { return CurrentlyInstalledVersion == null; } } [IgnoreDataMember] public string PackageDirectory { get; protected set; } protected UpdateInfo(ReleaseEntry currentlyInstalledVersion, IEnumerable releasesToApply, string packageDirectory) { // NB: When bootstrapping, CurrentlyInstalledVersion is null! CurrentlyInstalledVersion = currentlyInstalledVersion; ReleasesToApply = (releasesToApply ?? Enumerable.Empty()).ToList(); FutureReleaseEntry = ReleasesToApply.Any() ? ReleasesToApply.MaxBy(x => x.Version).FirstOrDefault() : CurrentlyInstalledVersion; this.PackageDirectory = packageDirectory; } public Dictionary FetchReleaseNotes() { return ReleasesToApply .SelectMany(x => { try { var releaseNotes = x.GetReleaseNotes(PackageDirectory); return EnumerableExtensions.Return(Tuple.Create(x, releaseNotes)); } catch (Exception ex) { this.Log().WarnException("Couldn't get release notes for:" + x.Filename, ex); return Enumerable.Empty>(); } }) .ToDictionary(k => k.Item1, v => v.Item2); } public static UpdateInfo Create(ReleaseEntry currentVersion, IEnumerable availableReleases, string packageDirectory) { Contract.Requires(availableReleases != null); Contract.Requires(!String.IsNullOrEmpty(packageDirectory)); var latestFull = availableReleases.MaxBy(x => x.Version).FirstOrDefault(x => !x.IsDelta); if (latestFull == null) { throw new Exception("There should always be at least one full release"); } if (currentVersion == null) { return new UpdateInfo(null, new[] { latestFull }, packageDirectory); } if (currentVersion.Version >= latestFull.Version) { return new UpdateInfo(currentVersion, Enumerable.Empty(), packageDirectory); } var newerThanUs = availableReleases .Where(x => x.Version > currentVersion.Version) .OrderBy(v => v.Version); var deltasSize = newerThanUs.Where(x => x.IsDelta).Sum(x => x.Filesize); return (deltasSize < latestFull.Filesize && deltasSize > 0) ? new UpdateInfo(currentVersion, newerThanUs.Where(x => x.IsDelta).ToArray(), packageDirectory) : new UpdateInfo(currentVersion, new[] { latestFull }, packageDirectory); } } } ================================================ FILE: src/Squirrel/UpdateManager.ApplyReleases.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using NuGet; using Squirrel.SimpleSplat; using System.Threading; using Squirrel.Shell; using Microsoft.Win32; namespace Squirrel { public sealed partial class UpdateManager { internal class ApplyReleasesImpl : IEnableLogger { readonly string rootAppDirectory; public ApplyReleasesImpl(string rootAppDirectory) { this.rootAppDirectory = rootAppDirectory; } public async Task ApplyReleases(UpdateInfo updateInfo, bool silentInstall, bool attemptingFullInstall, Action progress = null) { progress = progress ?? (_ => { }); progress(0); // Progress range: 00 -> 40 var release = await createFullPackagesFromDeltas(updateInfo.ReleasesToApply, updateInfo.CurrentlyInstalledVersion, new ApplyReleasesProgress(updateInfo.ReleasesToApply.Count, x => progress(CalculateProgress(x, 0, 40)))); progress(40); if (release == null) { if (attemptingFullInstall) { this.Log().Info("No release to install, running the app"); await invokePostInstall(updateInfo.CurrentlyInstalledVersion.Version, false, true, silentInstall); } progress(100); return getDirectoryForRelease(updateInfo.CurrentlyInstalledVersion.Version).FullName; } // Progress range: 40 -> 80 var ret = await this.ErrorIfThrows(() => installPackageToAppDir(updateInfo, release, x => progress(CalculateProgress(x, 40, 80))), "Failed to install package to app dir"); progress(80); var currentReleases = await this.ErrorIfThrows(() => updateLocalReleasesFile(), "Failed to update local releases file"); progress(85); var newVersion = currentReleases.MaxBy(x => x.Version).First().Version; executeSelfUpdate(newVersion); progress(90); await this.ErrorIfThrows(() => invokePostInstall(newVersion, attemptingFullInstall, false, silentInstall), "Failed to invoke post-install"); progress(95); this.Log().Info("Starting fixPinnedExecutables"); this.ErrorIfThrows(() => fixPinnedExecutables(updateInfo.FutureReleaseEntry.Version)); progress(96); this.Log().Info("Fixing up tray icons"); var trayFixer = new TrayStateChanger(); var appDir = new DirectoryInfo(Utility.AppDirForRelease(rootAppDirectory, updateInfo.FutureReleaseEntry)); var allExes = appDir.GetFiles("*.exe").Select(x => x.Name).ToList(); this.ErrorIfThrows(() => trayFixer.RemoveDeadEntries(allExes, rootAppDirectory, updateInfo.FutureReleaseEntry.Version.ToString())); progress(97); unshimOurselves(); progress(98); try { var currentVersion = updateInfo.CurrentlyInstalledVersion != null ? updateInfo.CurrentlyInstalledVersion.Version : null; await cleanDeadVersions(currentVersion, newVersion); } catch (Exception ex) { this.Log().WarnException("Failed to clean dead versions, continuing anyways", ex); } progress(100); return ret; } public async Task FullUninstall() { var currentRelease = getReleases().MaxBy(x => x.Name.ToSemanticVersion()).FirstOrDefault(); this.Log().Info("Starting full uninstall"); if (currentRelease.Exists) { var version = currentRelease.Name.ToSemanticVersion(); try { var squirrelAwareApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(currentRelease.FullName); if (isAppFolderDead(currentRelease.FullName)) throw new Exception("App folder is dead, but we're trying to uninstall it?"); var allApps = currentRelease.EnumerateFiles() .Where(x => x.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) .Where(x => !x.Name.StartsWith("squirrel.", StringComparison.OrdinalIgnoreCase) && !x.Name.StartsWith("update.", StringComparison.OrdinalIgnoreCase)) .ToList(); if (squirrelAwareApps.Count > 0) { await squirrelAwareApps.ForEachAsync(async exe => { using (var cts = new CancellationTokenSource()) { cts.CancelAfter(10 * 1000); try { await Utility.InvokeProcessAsync(exe, String.Format("--squirrel-uninstall {0}", version), cts.Token); } catch (Exception ex) { this.Log().ErrorException("Failed to run cleanup hook, continuing: " + exe, ex); } } }, 1 /*at a time*/); } else { allApps.ForEach(x => RemoveShortcutsForExecutable(x.Name, ShortcutLocation.StartMenu | ShortcutLocation.Desktop)); } } catch (Exception ex) { this.Log().WarnException("Failed to run pre-uninstall hooks, uninstalling anyways", ex); } } try { this.ErrorIfThrows(() => fixPinnedExecutables(new SemanticVersion(255, 255, 255, 255), true)); } catch { } await this.ErrorIfThrows(() => Utility.DeleteDirectoryOrJustGiveUp(rootAppDirectory), "Failed to delete app directory: " + rootAppDirectory); // NB: We drop this file here so that --checkInstall will ignore // this folder - if we don't do this, users who "accidentally" run as // administrator will find the app reinstalling itself on every // reboot if (!Directory.Exists(rootAppDirectory)) { Directory.CreateDirectory(rootAppDirectory); } File.WriteAllText(Path.Combine(rootAppDirectory, ".dead"), " "); } public Dictionary GetShortcutsForExecutable(string exeName, ShortcutLocation locations, string programArguments) { this.Log().Info("About to create shortcuts for {0}, rootAppDir {1}", exeName, rootAppDirectory); var releases = Utility.LoadLocalReleases(Utility.LocalReleaseFileForAppDir(rootAppDirectory)); var thisRelease = Utility.FindCurrentVersion(releases); var zf = new ZipPackage(Path.Combine( Utility.PackageDirectoryForAppDir(rootAppDirectory), thisRelease.Filename)); var exePath = Path.Combine(Utility.AppDirForRelease(rootAppDirectory, thisRelease), exeName); var fileVerInfo = FileVersionInfo.GetVersionInfo(exePath); var ret = new Dictionary(); foreach (var f in (ShortcutLocation[]) Enum.GetValues(typeof(ShortcutLocation))) { if (!locations.HasFlag(f)) continue; var file = linkTargetForVersionInfo(f, zf, fileVerInfo); var appUserModelId = String.Format("com.squirrel.{0}.{1}", zf.Id.Replace(" ", ""), exeName.Replace(".exe", "").Replace(" ", "")); var toastActivatorCLSDID = Utility.CreateGuidFromHash(appUserModelId).ToString(); this.Log().Info("Creating shortcut for {0} => {1}", exeName, file); this.Log().Info("appUserModelId: {0} | toastActivatorCLSID: {1}", appUserModelId, toastActivatorCLSDID); var target = Path.Combine(rootAppDirectory, exeName); var sl = new ShellLink { Target = target, IconPath = target, IconIndex = 0, WorkingDirectory = Path.GetDirectoryName(exePath), Description = zf.Description, }; if (!String.IsNullOrWhiteSpace(programArguments)) { sl.Arguments += String.Format(" -a \"{0}\"", programArguments); } sl.SetAppUserModelId(appUserModelId); sl.SetToastActivatorCLSID(toastActivatorCLSDID); ret.Add(f, sl); } return ret; } public void CreateShortcutsForExecutable(string exeName, ShortcutLocation locations, bool updateOnly, string programArguments, string icon) { this.Log().Info("About to create shortcuts for {0}, rootAppDir {1}", exeName, rootAppDirectory); var releases = Utility.LoadLocalReleases(Utility.LocalReleaseFileForAppDir(rootAppDirectory)); var thisRelease = Utility.FindCurrentVersion(releases); var zf = new ZipPackage(Path.Combine( Utility.PackageDirectoryForAppDir(rootAppDirectory), thisRelease.Filename)); var exePath = Path.Combine(Utility.AppDirForRelease(rootAppDirectory, thisRelease), exeName); var fileVerInfo = FileVersionInfo.GetVersionInfo(exePath); foreach (var f in (ShortcutLocation[]) Enum.GetValues(typeof(ShortcutLocation))) { if (!locations.HasFlag(f)) continue; var file = linkTargetForVersionInfo(f, zf, fileVerInfo); var fileExists = File.Exists(file); // NB: If we've already installed the app, but the shortcut // is no longer there, we have to assume that the user didn't // want it there and explicitly deleted it, so we shouldn't // annoy them by recreating it. if (!fileExists && updateOnly) { this.Log().Warn("Wanted to update shortcut {0} but it appears user deleted it", file); continue; } this.Log().Info("Creating shortcut for {0} => {1}", exeName, file); ShellLink sl; this.ErrorIfThrows(() => Utility.Retry(() => { File.Delete(file); var target = Path.Combine(rootAppDirectory, exeName); sl = new ShellLink { Target = target, IconPath = icon ?? target, IconIndex = 0, WorkingDirectory = Path.GetDirectoryName(exePath), Description = zf.Description, }; if (!String.IsNullOrWhiteSpace(programArguments)) { sl.Arguments += String.Format(" -a \"{0}\"", programArguments); } var appUserModelId = String.Format("com.squirrel.{0}.{1}", zf.Id.Replace(" ", ""), exeName.Replace(".exe", "").Replace(" ", "")); var toastActivatorCLSID = Utility.CreateGuidFromHash(appUserModelId).ToString(); sl.SetAppUserModelId(appUserModelId); sl.SetToastActivatorCLSID(toastActivatorCLSID); this.Log().Info("About to save shortcut: {0} (target {1}, workingDir {2}, args {3}, toastActivatorCSLID {4})", file, sl.Target, sl.WorkingDirectory, sl.Arguments, toastActivatorCLSID); if (ModeDetector.InUnitTestRunner() == false) sl.Save(file); }, 4), "Can't write shortcut: " + file); } fixPinnedExecutables(zf.Version); } public void RemoveShortcutsForExecutable(string exeName, ShortcutLocation locations) { var releases = Utility.LoadLocalReleases(Utility.LocalReleaseFileForAppDir(rootAppDirectory)); var thisRelease = Utility.FindCurrentVersion(releases); var zf = new ZipPackage(Path.Combine( Utility.PackageDirectoryForAppDir(rootAppDirectory), thisRelease.Filename)); var fileVerInfo = FileVersionInfo.GetVersionInfo( Path.Combine(Utility.AppDirForRelease(rootAppDirectory, thisRelease), exeName)); foreach (var f in (ShortcutLocation[]) Enum.GetValues(typeof(ShortcutLocation))) { if (!locations.HasFlag(f)) continue; var file = linkTargetForVersionInfo(f, zf, fileVerInfo); this.Log().Info("Removing shortcut for {0} => {1}", exeName, file); this.ErrorIfThrows(() => { if (File.Exists(file)) File.Delete(file); }, "Couldn't delete shortcut: " + file); } fixPinnedExecutables(zf.Version); } Task installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release, Action progressCallback) { return Task.Run(async () => { var target = getDirectoryForRelease(release.Version); // NB: This might happen if we got killed partially through applying the release if (target.Exists) { this.Log().Warn("Found partially applied release folder, killing it: " + target.FullName); await Utility.DeleteDirectory(target.FullName); } target.Create(); // Create the .not-finished file before extraction is started var notFinishedFilePath = Path.Combine(target.FullName, ".not-finished"); File.WriteAllText(notFinishedFilePath, ""); this.Log().Info("Writing files to app directory: {0}", target.FullName); await ReleasePackage.ExtractZipForInstall( Path.Combine(updateInfo.PackageDirectory, release.Filename), target.FullName, rootAppDirectory, progressCallback); // Delete the .not-finished file after extraction is completed this.ErrorIfThrows(() => { File.Delete(notFinishedFilePath); }, "Couldn't delete file: " + notFinishedFilePath); return target.FullName; }); } async Task createFullPackagesFromDeltas(IEnumerable releasesToApply, ReleaseEntry currentVersion, ApplyReleasesProgress progress) { Contract.Requires(releasesToApply != null); progress = progress ?? new ApplyReleasesProgress(releasesToApply.Count(), x => { }); // If there are no remote releases at all, bail if (!releasesToApply.Any()) { return null; } // If there are no deltas in our list, we're already done if (releasesToApply.All(x => !x.IsDelta)) { return releasesToApply.MaxBy(x => x.Version).FirstOrDefault(); } if (!releasesToApply.All(x => x.IsDelta)) { throw new Exception("Cannot apply combinations of delta and full packages"); } // Progress calculation is "complex" here. We need to known how many releases, and then give each release a similar amount of // progress. For example, when applying 5 releases: // // release 1: 00 => 20 // release 2: 20 => 40 // release 3: 40 => 60 // release 4: 60 => 80 // release 5: 80 => 100 // // Smash together our base full package and the nearest delta var ret = await Task.Run(() => { var basePkg = new ReleasePackage(Path.Combine(rootAppDirectory, "packages", currentVersion.Filename)); var deltaPkg = new ReleasePackage(Path.Combine(rootAppDirectory, "packages", releasesToApply.First().Filename)); var deltaBuilder = new DeltaPackageBuilder(Directory.GetParent(this.rootAppDirectory).FullName); return deltaBuilder.ApplyDeltaPackage(basePkg, deltaPkg, Regex.Replace(deltaPkg.InputPackageFile, @"-delta.nupkg$", ".nupkg", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant), x => progress.ReportReleaseProgress(x)); }); progress.FinishRelease(); if (releasesToApply.Count() == 1) { return ReleaseEntry.GenerateFromFile(ret.InputPackageFile); } var fi = new FileInfo(ret.InputPackageFile); var entry = ReleaseEntry.GenerateFromFile(fi.OpenRead(), fi.Name); // Recursively combine the rest of them return await createFullPackagesFromDeltas(releasesToApply.Skip(1), entry, progress); } void executeSelfUpdate(SemanticVersion currentVersion) { var targetDir = getDirectoryForRelease(currentVersion); var newSquirrel = Path.Combine(targetDir.FullName, "Squirrel.exe"); if (!File.Exists(newSquirrel)) { return; } // If we're running in the context of Update.exe, we can't // update ourselves. Instead, ask the new Update.exe to do it // once we exit var us = Assembly.GetEntryAssembly(); if (us != null && Path.GetFileName(us.Location).Equals("update.exe", StringComparison.OrdinalIgnoreCase)) { var appName = targetDir.Parent.Name; Process.Start(newSquirrel, "--updateSelf=" + us.Location); return; } // If we're *not* Update.exe, this is easy, it's just a file copy Utility.Retry(() => File.Copy(newSquirrel, Path.Combine(targetDir.Parent.FullName, "Update.exe"), true)); } async Task invokePostInstall(SemanticVersion currentVersion, bool isInitialInstall, bool firstRunOnly, bool silentInstall) { var targetDir = getDirectoryForRelease(currentVersion); var args = isInitialInstall ? String.Format("--squirrel-install {0}", currentVersion) : String.Format("--squirrel-updated {0}", currentVersion); var squirrelApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(targetDir.FullName); this.Log().Info("Squirrel Enabled Apps: [{0}]", String.Join(",", squirrelApps)); // For each app, run the install command in-order and wait if (!firstRunOnly) await squirrelApps.ForEachAsync(async exe => { using (var cts = new CancellationTokenSource()) { cts.CancelAfter(15 * 1000); try { await Utility.InvokeProcessAsync(exe, args, cts.Token); } catch (Exception ex) { this.Log().ErrorException("Couldn't run Squirrel hook, continuing: " + exe, ex); } } }, 1 /* at a time */); // If this is the first run, we run the apps with first-run and // *don't* wait for them, since they're probably the main EXE if (squirrelApps.Count == 0) { this.Log().Warn("No apps are marked as Squirrel-aware! Going to run them all"); squirrelApps = targetDir.EnumerateFiles() .Where(x => x.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) .Where(x => !x.Name.StartsWith("squirrel.", StringComparison.OrdinalIgnoreCase)) .Select(x => x.FullName) .ToList(); // Create shortcuts for apps automatically if they didn't // create any Squirrel-aware apps squirrelApps.ForEach(x => CreateShortcutsForExecutable(Path.GetFileName(x), ShortcutLocation.Desktop | ShortcutLocation.StartMenu, isInitialInstall == false, null, null)); } if (!isInitialInstall || silentInstall) return; var firstRunParam = isInitialInstall ? "--squirrel-firstrun" : ""; squirrelApps .Select(exe => new ProcessStartInfo(exe, firstRunParam) { WorkingDirectory = Path.GetDirectoryName(exe) }) .ForEach(info => Process.Start(info)); } void fixPinnedExecutables(SemanticVersion newCurrentVersion, bool removeAll = false) { if (Environment.OSVersion.Version < new Version(6, 1)) { this.Log().Warn("fixPinnedExecutables: Found OS Version '{0}', exiting...", Environment.OSVersion.VersionString); return; } var newCurrentFolder = "app-" + newCurrentVersion; var newAppPath = Path.Combine(rootAppDirectory, newCurrentFolder); var taskbarPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar"); if (!Directory.Exists(taskbarPath)) { this.Log().Info("fixPinnedExecutables: PinnedExecutables directory doesn't exitsts, skiping..."); return; } var resolveLink = new Func(file => { try { this.Log().Debug("Examining Pin: " + file); return new ShellLink(file.FullName); } catch (Exception ex) { var message = String.Format("File '{0}' could not be converted into a valid ShellLink", file.FullName); this.Log().WarnException(message, ex); return null; } }); var shellLinks = (new DirectoryInfo(taskbarPath)).GetFiles("*.lnk").Select(resolveLink).ToArray(); foreach (var shortcut in shellLinks) { try { if (shortcut == null) continue; if (String.IsNullOrWhiteSpace(shortcut.Target)) continue; if (!shortcut.Target.StartsWith(rootAppDirectory, StringComparison.OrdinalIgnoreCase)) continue; if (removeAll) { Utility.DeleteFileHarder(shortcut.ShortCutFile); } else { updateLink(shortcut, newAppPath); } } catch (Exception ex) { var message = String.Format("fixPinnedExecutables: shortcut failed: {0}", shortcut.Target); this.Log().ErrorException(message, ex); } } } void updateLink(ShellLink shortcut, string newAppPath) { this.Log().Info("Processing shortcut '{0}'", shortcut.ShortCutFile); var target = Environment.ExpandEnvironmentVariables(shortcut.Target); var targetIsUpdateDotExe = target.EndsWith("update.exe", StringComparison.OrdinalIgnoreCase); this.Log().Info("Old shortcut target: '{0}'", target); // NB: In 1.5.0 we accidentally fixed the target of pinned shortcuts but left the arguments, // so if we find a shortcut with --processStart in the args, we're gonna stomp it even though // what we _should_ do is stomp it only if the target is Update.exe if (shortcut.Arguments.Contains("--processStart")) { shortcut.Arguments = ""; } if (!targetIsUpdateDotExe) { target = Path.Combine(rootAppDirectory, Path.GetFileName(shortcut.Target)); } else { target = Path.Combine(rootAppDirectory, Path.GetFileName(shortcut.IconPath)); } this.Log().Info("New shortcut target: '{0}'", target); shortcut.WorkingDirectory = newAppPath; shortcut.Target = target; this.Log().Info("Old iconPath is: '{0}'", shortcut.IconPath); shortcut.IconPath = target; shortcut.IconIndex = 0; this.ErrorIfThrows(() => Utility.Retry(() => shortcut.Save(), 2), "Couldn't write shortcut " + shortcut.ShortCutFile); this.Log().Info("Finished shortcut successfully"); } internal void unshimOurselves() { new[] { RegistryView.Registry32, RegistryView.Registry64 }.ForEach(view => { var baseKey = default(RegistryKey); var regKey = default(RegistryKey); try { baseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, view); regKey = baseKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"); if (regKey == null) return; var toDelete = regKey.GetValueNames() .Where(x => x.StartsWith(rootAppDirectory, StringComparison.OrdinalIgnoreCase)) .ToList(); toDelete.ForEach(x => this.Log().LogIfThrows(LogLevel.Warn, "Failed to delete key: " + x, () => regKey.DeleteValue(x))); } catch (Exception e) { this.Log().WarnException("Couldn't rewrite shim RegKey, most likely no apps are shimmed", e); } finally { if (regKey != null) regKey.Dispose(); if (baseKey != null) baseKey.Dispose(); } }); } // NB: Once we uninstall the old version of the app, we try to schedule // it to be deleted at next reboot. Unfortunately, depending on whether // the user has admin permissions, this can fail. So as a failsafe, // before we try to apply any update, we assume previous versions in the // directory are "dead" (i.e. already uninstalled, but not deleted), and // we blow them away. This is to make sure that we don't attempt to run // an uninstaller on an already-uninstalled version. async Task cleanDeadVersions(SemanticVersion currentVersion, SemanticVersion newVersion, bool forceUninstall = false) { if (newVersion == null) return; var di = new DirectoryInfo(rootAppDirectory); if (!di.Exists) return; this.Log().Info("cleanDeadVersions: checking for version {0}", newVersion); string currentVersionFolder = null; if (currentVersion != null) { currentVersionFolder = getDirectoryForRelease(currentVersion).Name; this.Log().Info("cleanDeadVersions: exclude current version folder {0}", currentVersionFolder); } string newVersionFolder = null; if (newVersion != null) { newVersionFolder = getDirectoryForRelease(newVersion).Name; this.Log().Info("cleanDeadVersions: exclude new version folder {0}", newVersionFolder); } // NB: If we try to access a directory that has already been // scheduled for deletion by MoveFileEx it throws what seems like // NT's only error code, ERROR_ACCESS_DENIED. Squelch errors that // come from here. var toCleanup = di.GetDirectories() .Where(x => x.Name.ToLowerInvariant().Contains("app-")) .Where(x => x.Name != newVersionFolder && x.Name != currentVersionFolder) .Where(x => !isAppFolderDead(x.FullName)); if (forceUninstall == false) { await toCleanup.ForEachAsync(async x => { var squirrelApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(x.FullName); var args = String.Format("--squirrel-obsolete {0}", x.Name.Replace("app-", "")); if (squirrelApps.Count > 0) { // For each app, run the install command in-order and wait await squirrelApps.ForEachAsync(async exe => { using (var cts = new CancellationTokenSource()) { cts.CancelAfter(10 * 1000); try { await Utility.InvokeProcessAsync(exe, args, cts.Token); } catch (Exception ex) { this.Log().ErrorException("Coudln't run Squirrel hook, continuing: " + exe, ex); } } }, 1 /* at a time */); } }); } // Include dead folders in folders to :fire: toCleanup = di.GetDirectories() .Where(x => x.Name.ToLowerInvariant().Contains("app-")) .Where(x => x.Name != newVersionFolder && x.Name != currentVersionFolder); // Get the current process list in an attempt to not burn // directories which have running processes var runningProcesses = UnsafeUtility.EnumerateProcesses(); // Finally, clean up the app-X.Y.Z directories await toCleanup.ForEachAsync(async x => { try { if (runningProcesses.All(p => p.Item1 == null || !p.Item1.StartsWith(x.FullName, StringComparison.OrdinalIgnoreCase))) { await Utility.DeleteDirectoryOrJustGiveUp(x.FullName); } if (Directory.Exists(x.FullName)) { // NB: If we cannot clean up a directory, we need to make // sure that anyone finding it later won't attempt to run // Squirrel events on it. We'll mark it with a .dead file markAppFolderAsDead(x.FullName); } } catch (UnauthorizedAccessException ex) { this.Log().WarnException("Couldn't delete directory: " + x.FullName, ex); // NB: Same deal as above markAppFolderAsDead(x.FullName); } }); // Clean up the packages directory too var releasesFile = Utility.LocalReleaseFileForAppDir(rootAppDirectory); var entries = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasesFile, Encoding.UTF8)); var pkgDir = Utility.PackageDirectoryForAppDir(rootAppDirectory); var releaseEntry = default(ReleaseEntry); foreach (var entry in entries) { if (entry.Version == newVersion) { releaseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(pkgDir, entry.Filename)); continue; } File.Delete(Path.Combine(pkgDir, entry.Filename)); } ReleaseEntry.WriteReleaseFile(new[] { releaseEntry }, releasesFile); } static void markAppFolderAsDead(string appFolderPath) { File.WriteAllText(Path.Combine(appFolderPath, ".dead"), ""); } static bool isAppFolderDead(string appFolderPath) { return File.Exists(Path.Combine(appFolderPath, ".dead")); } internal async Task> updateLocalReleasesFile() { return await Task.Run(() => ReleaseEntry.BuildReleasesFile(Utility.PackageDirectoryForAppDir(rootAppDirectory))); } IEnumerable getReleases() { var rootDirectory = new DirectoryInfo(rootAppDirectory); if (!rootDirectory.Exists) return Enumerable.Empty(); return rootDirectory.GetDirectories() .Where(x => x.Name.StartsWith("app-", StringComparison.InvariantCultureIgnoreCase)); } DirectoryInfo getDirectoryForRelease(SemanticVersion releaseVersion) { return new DirectoryInfo(Path.Combine(rootAppDirectory, "app-" + releaseVersion)); } string linkTargetForVersionInfo(ShortcutLocation location, IPackage package, FileVersionInfo versionInfo) { var possibleProductNames = new[] { versionInfo.ProductName, package.Title, versionInfo.FileDescription, Path.GetFileNameWithoutExtension(versionInfo.FileName) }; var possibleCompanyNames = new[] { versionInfo.CompanyName, package.Authors.FirstOrDefault() ?? package.Id, }; var prodName = possibleCompanyNames.First(x => !String.IsNullOrWhiteSpace(x)); var pkgName = possibleProductNames.First(x => !String.IsNullOrWhiteSpace(x)); return getLinkTarget(location, pkgName, prodName); } string getLinkTarget(ShortcutLocation location, string title, string applicationName, bool createDirectoryIfNecessary = true) { var dir = default(string); switch (location) { case ShortcutLocation.Desktop: dir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); break; case ShortcutLocation.StartMenu: dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Programs", applicationName); break; case ShortcutLocation.Startup: dir = Environment.GetFolderPath (Environment.SpecialFolder.Startup); break; case ShortcutLocation.AppRoot: dir = rootAppDirectory; break; } if (createDirectoryIfNecessary && !Directory.Exists(dir)) { Directory.CreateDirectory(dir); } return Path.Combine(dir, title + ".lnk"); } } } } ================================================ FILE: src/Squirrel/UpdateManager.CheckForUpdates.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using Squirrel.SimpleSplat; namespace Squirrel { public sealed partial class UpdateManager { internal class CheckForUpdateImpl : IEnableLogger { readonly string rootAppDirectory; public CheckForUpdateImpl(string rootAppDirectory) { this.rootAppDirectory = rootAppDirectory; } public async Task CheckForUpdate( UpdaterIntention intention, string localReleaseFile, string updateUrlOrPath, bool ignoreDeltaUpdates = false, Action progress = null, IFileDownloader urlDownloader = null) { progress = progress ?? (_ => { }); var localReleases = Enumerable.Empty(); var stagingId = intention == UpdaterIntention.Install ? null : getOrCreateStagedUserId(); bool shouldInitialize = intention == UpdaterIntention.Install; if (intention != UpdaterIntention.Install) { try { localReleases = Utility.LoadLocalReleases(localReleaseFile); } catch (Exception ex) { // Something has gone pear-shaped, let's start from scratch this.Log().WarnException("Failed to load local releases, starting from scratch", ex); shouldInitialize = true; } } if (shouldInitialize) await initializeClientAppDirectory(); string releaseFile; var latestLocalRelease = localReleases.Count() > 0 ? localReleases.MaxBy(x => x.Version).First() : default(ReleaseEntry); // Fetch the remote RELEASES file, whether it's a local dir or an // HTTP URL if (Utility.IsHttpUrl(updateUrlOrPath)) { if (updateUrlOrPath.EndsWith("/")) { updateUrlOrPath = updateUrlOrPath.Substring(0, updateUrlOrPath.Length - 1); } this.Log().Info("Downloading RELEASES file from {0}", updateUrlOrPath); int retries = 3; retry: try { var uri = Utility.AppendPathToUri(new Uri(updateUrlOrPath), "RELEASES"); if (latestLocalRelease != null) { uri = Utility.AddQueryParamsToUri(uri, new Dictionary { { "id", latestLocalRelease.PackageName }, { "localVersion", latestLocalRelease.Version.ToString() }, { "arch", Environment.Is64BitOperatingSystem ? "amd64" : "x86" } }); } var data = await urlDownloader.DownloadUrl(uri.ToString()); releaseFile = Encoding.UTF8.GetString(data); } catch (WebException ex) { this.Log().InfoException("Download resulted in WebException (returning blank release list)", ex); if (retries <= 0) throw; retries--; goto retry; } progress(33); } else { this.Log().Info("Reading RELEASES file from {0}", updateUrlOrPath); if (!Directory.Exists(updateUrlOrPath)) { var message = String.Format( "The directory {0} does not exist, something is probably broken with your application", updateUrlOrPath); throw new Exception(message); } var fi = new FileInfo(Path.Combine(updateUrlOrPath, "RELEASES")); if (!fi.Exists) { var message = String.Format( "The file {0} does not exist, something is probably broken with your application", fi.FullName); this.Log().Warn(message); var packages = (new DirectoryInfo(updateUrlOrPath)).GetFiles("*.nupkg"); if (packages.Length == 0) { throw new Exception(message); } // NB: Create a new RELEASES file since we've got a directory of packages ReleaseEntry.WriteReleaseFile( packages.Select(x => ReleaseEntry.GenerateFromFile(x.FullName)), fi.FullName); } releaseFile = File.ReadAllText(fi.FullName, Encoding.UTF8); progress(33); } var ret = default(UpdateInfo); var remoteReleases = ReleaseEntry.ParseReleaseFileAndApplyStaging(releaseFile, stagingId); progress(66); if (!remoteReleases.Any()) { throw new Exception("Remote release File is empty or corrupted"); } ret = determineUpdateInfo(intention, localReleases, remoteReleases, ignoreDeltaUpdates); progress(100); return ret; } async Task initializeClientAppDirectory() { // On bootstrap, we won't have any of our directories, create them var pkgDir = Path.Combine(rootAppDirectory, "packages"); if (Directory.Exists(pkgDir)) { await Utility.DeleteDirectory(pkgDir); } Directory.CreateDirectory(pkgDir); } UpdateInfo determineUpdateInfo(UpdaterIntention intention, IEnumerable localReleases, IEnumerable remoteReleases, bool ignoreDeltaUpdates) { var packageDirectory = Utility.PackageDirectoryForAppDir(rootAppDirectory); localReleases = localReleases ?? Enumerable.Empty(); if (remoteReleases == null) { this.Log().Warn("Release information couldn't be determined due to remote corrupt RELEASES file"); throw new Exception("Corrupt remote RELEASES file"); } var latestFullRelease = Utility.FindCurrentVersion(remoteReleases); var currentRelease = Utility.FindCurrentVersion(localReleases); if (latestFullRelease == currentRelease) { this.Log().Info("No updates, remote and local are the same"); var info = UpdateInfo.Create(currentRelease, new[] {latestFullRelease}, packageDirectory); return info; } if (ignoreDeltaUpdates) { remoteReleases = remoteReleases.Where(x => !x.IsDelta); } if (!localReleases.Any()) { if (intention == UpdaterIntention.Install) { this.Log().Info("First run, starting from scratch"); } else { this.Log().Warn("No local releases found, starting from scratch"); } return UpdateInfo.Create(null, new[] {latestFullRelease}, packageDirectory); } if (localReleases.Max(x => x.Version) > remoteReleases.Max(x => x.Version)) { this.Log().Warn("hwhat, local version is greater than remote version"); return UpdateInfo.Create(Utility.FindCurrentVersion(localReleases), new[] {latestFullRelease}, packageDirectory); } return UpdateInfo.Create(currentRelease, remoteReleases, packageDirectory); } internal Guid? getOrCreateStagedUserId() { var stagedUserIdFile = Path.Combine(rootAppDirectory, "packages", ".betaId"); var ret = default(Guid); try { if (!Guid.TryParse(File.ReadAllText(stagedUserIdFile, Encoding.UTF8), out ret)) { throw new Exception("File was read but contents were invalid"); } this.Log().Info("Using existing staging user ID: {0}", ret.ToString()); return ret; } catch (Exception ex) { this.Log().DebugException("Couldn't read staging user ID, creating a blank one", ex); } var prng = new Random(); var buf = new byte[4096]; prng.NextBytes(buf); ret = Utility.CreateGuidFromHash(buf); try { File.WriteAllText(stagedUserIdFile, ret.ToString(), Encoding.UTF8); this.Log().Info("Generated new staging user ID: {0}", ret.ToString()); return ret; } catch (Exception ex) { this.Log().WarnException("Couldn't write out staging user ID, this user probably shouldn't get beta anything", ex); return null; } } } } } ================================================ FILE: src/Squirrel/UpdateManager.DownloadReleases.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using Squirrel.SimpleSplat; namespace Squirrel { public sealed partial class UpdateManager { internal class DownloadReleasesImpl : IEnableLogger { readonly string rootAppDirectory; public DownloadReleasesImpl(string rootAppDirectory) { this.rootAppDirectory = rootAppDirectory; } public async Task DownloadReleases(string updateUrlOrPath, IEnumerable releasesToDownload, Action progress = null, IFileDownloader urlDownloader = null) { progress = progress ?? (_ => { }); urlDownloader = urlDownloader ?? new FileDownloader(); var packagesDirectory = Path.Combine(rootAppDirectory, "packages"); double current = 0; double toIncrement = 100.0 / releasesToDownload.Count(); if (Utility.IsHttpUrl(updateUrlOrPath)) { // From Internet await releasesToDownload.ForEachAsync(async x => { var targetFile = Path.Combine(packagesDirectory, x.Filename); double component = 0; await downloadRelease(updateUrlOrPath, x, urlDownloader, targetFile, p => { lock (progress) { current -= component; component = toIncrement / 100.0 * p; progress((int)Math.Round(current += component)); } }); checksumPackage(x); }); } else { // From Disk await releasesToDownload.ForEachAsync(x => { var targetFile = Path.Combine(packagesDirectory, x.Filename); File.Copy( Path.Combine(updateUrlOrPath, x.Filename), targetFile, true); lock (progress) progress((int)Math.Round(current += toIncrement)); checksumPackage(x); }); } } bool isReleaseExplicitlyHttp(ReleaseEntry x) { return x.BaseUrl != null && Uri.IsWellFormedUriString(x.BaseUrl, UriKind.Absolute); } Task downloadRelease(string updateBaseUrl, ReleaseEntry releaseEntry, IFileDownloader urlDownloader, string targetFile, Action progress) { var baseUri = Utility.EnsureTrailingSlash(new Uri(updateBaseUrl)); var releaseEntryUrl = releaseEntry.BaseUrl + releaseEntry.Filename; if (!String.IsNullOrEmpty(releaseEntry.Query)) { releaseEntryUrl += releaseEntry.Query; } var sourceFileUrl = new Uri(baseUri, releaseEntryUrl).AbsoluteUri; File.Delete(targetFile); return urlDownloader.DownloadFile(sourceFileUrl, targetFile, progress); } Task checksumAllPackages(IEnumerable releasesDownloaded) { return releasesDownloaded.ForEachAsync(x => checksumPackage(x)); } void checksumPackage(ReleaseEntry downloadedRelease) { var targetPackage = new FileInfo( Path.Combine(rootAppDirectory, "packages", downloadedRelease.Filename)); if (!targetPackage.Exists) { this.Log().Error("File {0} should exist but doesn't", targetPackage.FullName); throw new Exception("Checksummed file doesn't exist: " + targetPackage.FullName); } if (targetPackage.Length != downloadedRelease.Filesize) { this.Log().Error("File Length should be {0}, is {1}", downloadedRelease.Filesize, targetPackage.Length); targetPackage.Delete(); throw new Exception("Checksummed file size doesn't match: " + targetPackage.FullName); } using (var file = targetPackage.OpenRead()) { var hash = Utility.CalculateStreamSHA1(file); if (!hash.Equals(downloadedRelease.SHA1,StringComparison.OrdinalIgnoreCase)) { this.Log().Error("File SHA1 should be {0}, is {1}", downloadedRelease.SHA1, hash); targetPackage.Delete(); throw new Exception("Checksum doesn't match: " + targetPackage.FullName); } } } } } } ================================================ FILE: src/Squirrel/UpdateManager.Factory.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; using Squirrel.Json; namespace Squirrel { public sealed partial class UpdateManager { [DataContract] public class Release { [DataMember(Name = "prerelease")] public bool Prerelease { get; set; } [DataMember(Name = "published_at")] public DateTime PublishedAt { get; set; } [DataMember(Name = "html_url")] public string HtmlUrl { get; set; } } public static async Task GitHubUpdateManager( string repoUrl, string applicationName = null, string rootDirectory = null, IFileDownloader urlDownloader = null, bool prerelease = false, string accessToken = null) { var repoUri = new Uri(repoUrl); var userAgent = new ProductInfoHeaderValue("Squirrel", Assembly.GetExecutingAssembly().GetName().Version.ToString()); if (repoUri.Segments.Length != 3) { throw new Exception("Repo URL must be to the root URL of the repo e.g. https://github.com/myuser/myrepo"); } var releasesApiBuilder = new StringBuilder("repos") .Append(repoUri.AbsolutePath) .Append("/releases"); if (!string.IsNullOrWhiteSpace(accessToken)) releasesApiBuilder.Append("?access_token=").Append(accessToken); Uri baseAddress; if (repoUri.Host.EndsWith("github.com", StringComparison.OrdinalIgnoreCase)) { baseAddress = new Uri("https://api.github.com/"); } else { // if it's not github.com, it's probably an Enterprise server // now the problem with Enterprise is that the API doesn't come prefixed // it comes suffixed // so the API path of http://internal.github.server.local API location is // http://interal.github.server.local/api/v3. baseAddress = new Uri(string.Format("{0}{1}{2}/api/v3/", repoUri.Scheme, Uri.SchemeDelimiter, repoUri.Host)); } // above ^^ notice the end slashes for the baseAddress, explained here: http://stackoverflow.com/a/23438417/162694 using (var client = new HttpClient() { BaseAddress = baseAddress }) { client.DefaultRequestHeaders.UserAgent.Add(userAgent); var response = await client.GetAsync(releasesApiBuilder.ToString()); response.EnsureSuccessStatusCode(); var releases = SimpleJson.DeserializeObject>(await response.Content.ReadAsStringAsync()); var latestRelease = releases .Where(x => prerelease || !x.Prerelease) .OrderByDescending(x => x.PublishedAt) .First(); var latestReleaseUrl = latestRelease.HtmlUrl.Replace("/tag/", "/download/"); return new UpdateManager(latestReleaseUrl, applicationName, rootDirectory, urlDownloader); } } } } ================================================ FILE: src/Squirrel/UpdateManager.InstallHelpers.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using Microsoft.Win32; using NuGet; using System.Reflection; using Squirrel.SimpleSplat; namespace Squirrel { public sealed partial class UpdateManager { internal class InstallHelperImpl : IEnableLogger { readonly string applicationName; readonly string rootAppDirectory; public InstallHelperImpl(string applicationName, string rootAppDirectory) { this.applicationName = applicationName; this.rootAppDirectory = rootAppDirectory; } const string currentVersionRegSubKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall"; const string uninstallRegSubKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall"; public async Task CreateUninstallerRegistryEntry(string uninstallCmd, string quietSwitch) { var releaseContent = File.ReadAllText(Path.Combine(rootAppDirectory, "packages", "RELEASES"), Encoding.UTF8); var releases = ReleaseEntry.ParseReleaseFile(releaseContent); var latest = releases.Where(x => !x.IsDelta).OrderByDescending(x => x.Version).First(); // Download the icon and PNG => ICO it. If this doesn't work, who cares var pkgPath = Path.Combine(rootAppDirectory, "packages", latest.Filename); var zp = new ZipPackage(pkgPath); var targetPng = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".png"); var targetIco = Path.Combine(rootAppDirectory, "app.ico"); // NB: Sometimes the Uninstall key doesn't exist using (var parentKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default) .CreateSubKey("Uninstall", RegistryKeyPermissionCheck.ReadWriteSubTree)) { ; } var key = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default) .CreateSubKey(uninstallRegSubKey + "\\" + applicationName, RegistryKeyPermissionCheck.ReadWriteSubTree); if (zp.IconUrl != null && !File.Exists(targetIco)) { try { using (var wc = Utility.CreateWebClient()) { await wc.DownloadFileTaskAsync(zp.IconUrl, targetPng); using (var fs = new FileStream(targetIco, FileMode.Create)) { if (zp.IconUrl.AbsolutePath.EndsWith("ico")) { var bytes = File.ReadAllBytes(targetPng); fs.Write(bytes, 0, bytes.Length); } else { using (var bmp = (Bitmap)Image.FromFile(targetPng)) using (var ico = Icon.FromHandle(bmp.GetHicon())) { ico.Save(fs); } } key.SetValue("DisplayIcon", targetIco, RegistryValueKind.String); } } } catch(Exception ex) { this.Log().InfoException("Couldn't write uninstall icon, don't care", ex); } finally { File.Delete(targetPng); } } var stringsToWrite = new[] { new { Key = "DisplayName", Value = zp.Title ?? zp.Description ?? zp.Summary }, new { Key = "DisplayVersion", Value = zp.Version.ToString() }, new { Key = "InstallDate", Value = DateTime.Now.ToString("yyyyMMdd", CultureInfo.InvariantCulture) }, new { Key = "InstallLocation", Value = rootAppDirectory }, new { Key = "Publisher", Value = String.Join(",", zp.Authors) }, new { Key = "QuietUninstallString", Value = String.Format("{0} {1}", uninstallCmd, quietSwitch) }, new { Key = "UninstallString", Value = uninstallCmd }, new { Key = "URLUpdateInfo", Value = zp.ProjectUrl != null ? zp.ProjectUrl.ToString() : "", } }; var dwordsToWrite = new[] { new { Key = "EstimatedSize", Value = (int)((new FileInfo(pkgPath)).Length / 1024) }, new { Key = "NoModify", Value = 1 }, new { Key = "NoRepair", Value = 1 }, new { Key = "Language", Value = 0x0409 }, }; foreach (var kvp in stringsToWrite) { key.SetValue(kvp.Key, kvp.Value, RegistryValueKind.String); } foreach (var kvp in dwordsToWrite) { key.SetValue(kvp.Key, kvp.Value, RegistryValueKind.DWord); } return key; } public void KillAllProcessesBelongingToPackage() { var ourExe = Assembly.GetEntryAssembly(); var ourExePath = ourExe != null ? ourExe.Location : null; UnsafeUtility.EnumerateProcesses() .Where(x => { // Processes we can't query will have an empty process name, we can't kill them // anyways if (String.IsNullOrWhiteSpace(x.Item1)) return false; // Files that aren't in our root app directory are untouched if (!x.Item1.StartsWith(rootAppDirectory, StringComparison.OrdinalIgnoreCase)) return false; // Never kill our own EXE if (ourExePath != null && x.Item1.Equals(ourExePath, StringComparison.OrdinalIgnoreCase)) return false; var name = Path.GetFileName(x.Item1).ToLowerInvariant(); if (name == "squirrel.exe" || name == "update.exe") return false; return true; }) .ForEach(x => { try { this.WarnIfThrows(() => Process.GetProcessById(x.Item2).Kill()); } catch {} }); } public Task CreateUninstallerRegistryEntry() { var updateDotExe = Path.Combine(rootAppDirectory, "Update.exe"); return CreateUninstallerRegistryEntry(String.Format("\"{0}\" --uninstall", updateDotExe), "-s"); } public void RemoveUninstallerRegistryEntry() { var key = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default) .OpenSubKey(uninstallRegSubKey, true); key.DeleteSubKeyTree(applicationName); } } } } ================================================ FILE: src/Squirrel/UpdateManager.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Security.AccessControl; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Microsoft.Win32; using NuGet; using Squirrel.SimpleSplat; using Squirrel.Shell; namespace Squirrel { public sealed partial class UpdateManager : IUpdateManager, IEnableLogger { readonly string rootAppDirectory; readonly string applicationName; readonly IFileDownloader urlDownloader; readonly string updateUrlOrPath; IDisposable updateLock; public UpdateManager(string urlOrPath, string applicationName = null, string rootDirectory = null, IFileDownloader urlDownloader = null) { Contract.Requires(!String.IsNullOrEmpty(urlOrPath)); Contract.Requires(!String.IsNullOrEmpty(applicationName)); updateUrlOrPath = urlOrPath; this.applicationName = applicationName ?? UpdateManager.getApplicationName(); this.urlDownloader = urlDownloader ?? new FileDownloader(); if (rootDirectory != null) { this.rootAppDirectory = Path.Combine(rootDirectory, this.applicationName); return; } this.rootAppDirectory = Path.Combine(rootDirectory ?? GetLocalAppDataDirectory(), this.applicationName); } public async Task CheckForUpdate(bool ignoreDeltaUpdates = false, Action progress = null, UpdaterIntention intention = UpdaterIntention.Update) { var checkForUpdate = new CheckForUpdateImpl(rootAppDirectory); await acquireUpdateLock(); return await checkForUpdate.CheckForUpdate(intention, Utility.LocalReleaseFileForAppDir(rootAppDirectory), updateUrlOrPath, ignoreDeltaUpdates, progress, urlDownloader); } public async Task DownloadReleases(IEnumerable releasesToDownload, Action progress = null) { var downloadReleases = new DownloadReleasesImpl(rootAppDirectory); await acquireUpdateLock(); await downloadReleases.DownloadReleases(updateUrlOrPath, releasesToDownload, progress, urlDownloader); } public async Task ApplyReleases(UpdateInfo updateInfo, Action progress = null) { var applyReleases = new ApplyReleasesImpl(rootAppDirectory); await acquireUpdateLock(); return await applyReleases.ApplyReleases(updateInfo, false, false, progress); } public async Task FullInstall(bool silentInstall = false, Action progress = null) { var updateInfo = await CheckForUpdate(intention: UpdaterIntention.Install); await DownloadReleases(updateInfo.ReleasesToApply); var applyReleases = new ApplyReleasesImpl(rootAppDirectory); await acquireUpdateLock(); await applyReleases.ApplyReleases(updateInfo, silentInstall, true, progress); } public async Task FullUninstall() { var applyReleases = new ApplyReleasesImpl(rootAppDirectory); await acquireUpdateLock(); this.KillAllExecutablesBelongingToPackage(); await applyReleases.FullUninstall(); } public Task CreateUninstallerRegistryEntry(string uninstallCmd, string quietSwitch) { var installHelpers = new InstallHelperImpl(applicationName, rootAppDirectory); return installHelpers.CreateUninstallerRegistryEntry(uninstallCmd, quietSwitch); } public Task CreateUninstallerRegistryEntry() { var installHelpers = new InstallHelperImpl(applicationName, rootAppDirectory); return installHelpers.CreateUninstallerRegistryEntry(); } public void RemoveUninstallerRegistryEntry() { var installHelpers = new InstallHelperImpl(applicationName, rootAppDirectory); installHelpers.RemoveUninstallerRegistryEntry(); } public void CreateShortcutsForExecutable(string exeName, ShortcutLocation locations, bool updateOnly, string programArguments = null, string icon = null) { var installHelpers = new ApplyReleasesImpl(rootAppDirectory); installHelpers.CreateShortcutsForExecutable(exeName, locations, updateOnly, programArguments, icon); } public Dictionary GetShortcutsForExecutable(string exeName, ShortcutLocation locations, string programArguments = null) { var installHelpers = new ApplyReleasesImpl(rootAppDirectory); return installHelpers.GetShortcutsForExecutable(exeName, locations, programArguments); } public void RemoveShortcutsForExecutable(string exeName, ShortcutLocation locations) { var installHelpers = new ApplyReleasesImpl(rootAppDirectory); installHelpers.RemoveShortcutsForExecutable(exeName, locations); } public SemanticVersion CurrentlyInstalledVersion(string executable = null) { executable = executable ?? Path.GetDirectoryName(typeof(UpdateManager).Assembly.Location); if (!executable.StartsWith(rootAppDirectory, StringComparison.OrdinalIgnoreCase)) { return null; } var appDirName = executable.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) .FirstOrDefault(x => x.StartsWith("app-", StringComparison.OrdinalIgnoreCase)); if (appDirName == null) return null; return appDirName.ToSemanticVersion(); } public void KillAllExecutablesBelongingToPackage() { var installHelpers = new InstallHelperImpl(applicationName, rootAppDirectory); installHelpers.KillAllProcessesBelongingToPackage(); } public string ApplicationName { get { return applicationName; } } public string RootAppDirectory { get { return rootAppDirectory; } } public bool IsInstalledApp { get { return Assembly.GetExecutingAssembly().Location.StartsWith(RootAppDirectory, StringComparison.OrdinalIgnoreCase); } } public void Dispose() { var disp = Interlocked.Exchange(ref updateLock, null); if (disp != null) { disp.Dispose(); } } static bool exiting = false; public static void RestartApp(string exeToStart = null, string arguments = null) { // NB: Here's how this method works: // // 1. We're going to pass the *name* of our EXE and the params to // Update.exe // 2. Update.exe is going to grab our PID (via getting its parent), // then wait for us to exit. // 3. We exit cleanly, dropping any single-instance mutexes or // whatever. // 4. Update.exe unblocks, then we launch the app again, possibly // launching a different version than we started with (this is why // we take the app's *name* rather than a full path) exeToStart = exeToStart ?? Path.GetFileName(Assembly.GetEntryAssembly().Location); var argsArg = arguments != null ? String.Format("-a \"{0}\"", arguments) : ""; exiting = true; Process.Start(getUpdateExe(), String.Format("--processStartAndWait \"{0}\" {1}", exeToStart, argsArg)); // NB: We have to give update.exe some time to grab our PID, but // we can't use WaitForInputIdle because we probably don't have // whatever WaitForInputIdle considers a message loop. Thread.Sleep(500); Environment.Exit(0); } public static async Task RestartAppWhenExited(string exeToStart = null, string arguments = null) { // NB: Here's how this method works: // // 1. We're going to pass the *name* of our EXE and the params to // Update.exe // 2. Update.exe is going to grab our PID (via getting its parent), // then wait for us to exit. // 3. Return control and new Process back to caller and allow them to Exit as desired. // 4. After our process exits, Update.exe unblocks, then we launch the app again, possibly // launching a different version than we started with (this is why // we take the app's *name* rather than a full path) exeToStart = exeToStart ?? Path.GetFileName(Assembly.GetEntryAssembly().Location); var argsArg = arguments != null ? String.Format("-a \"{0}\"", arguments) : ""; exiting = true; var updateProcess = Process.Start(getUpdateExe(), String.Format("--processStartAndWait {0} {1}", exeToStart, argsArg)); await Task.Delay(500); return updateProcess; } public static string GetLocalAppDataDirectory(string assemblyLocation = null) { // Try to divine our our own install location via reading tea leaves // // * We're Update.exe, running in the app's install folder // * We're Update.exe, running on initial install from SquirrelTemp // * We're a C# EXE with Squirrel linked in var assembly = Assembly.GetEntryAssembly(); if (assemblyLocation == null && assembly == null) { // dunno lol return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); } assemblyLocation = assemblyLocation ?? assembly.Location; if (Path.GetFileName(assemblyLocation).Equals("update.exe", StringComparison.OrdinalIgnoreCase)) { // NB: Both the "SquirrelTemp" case and the "App's folder" case // mean that the root app dir is one up var oneFolderUpFromAppFolder = Path.Combine(Path.GetDirectoryName(assemblyLocation), ".."); return Path.GetFullPath(oneFolderUpFromAppFolder); } var twoFoldersUpFromAppFolder = Path.Combine(Path.GetDirectoryName(assemblyLocation), "..\\.."); return Path.GetFullPath(twoFoldersUpFromAppFolder); } ~UpdateManager() { if (updateLock != null && !exiting) { throw new Exception("You must dispose UpdateManager!"); } } Task acquireUpdateLock() { if (updateLock != null) return Task.FromResult(updateLock); return Task.Run(() => { var key = Utility.CalculateStreamSHA1(new MemoryStream(Encoding.UTF8.GetBytes(rootAppDirectory))); IDisposable theLock; try { theLock = ModeDetector.InUnitTestRunner() ? Disposable.Create(() => {}) : new SingleGlobalInstance(key, TimeSpan.FromMilliseconds(2000)); } catch (TimeoutException) { throw new TimeoutException("Couldn't acquire update lock, another instance may be running updates"); } var ret = Disposable.Create(() => { theLock.Dispose(); updateLock = null; }); updateLock = ret; return ret; }); } /// /// Calculates the total percentage of a specific step that should report within a specific range. /// /// If a step needs to report between 50 -> 75 %, this method should be used as CalculateProgress(percentage, 50, 75). /// /// The percentage of the current step, a value between 0 and 100. /// The start percentage of the range the current step represents. /// The end percentage of the range the current step represents. /// The calculated percentage that can be reported about the total progress. internal static int CalculateProgress(int percentageOfCurrentStep, int stepStartPercentage, int stepEndPercentage) { // Ensure we are between 0 and 100 percentageOfCurrentStep = Math.Max(Math.Min(percentageOfCurrentStep, 100), 0); var range = stepEndPercentage - stepStartPercentage; var singleValue = range / 100d; var totalPercentage = (singleValue * percentageOfCurrentStep) + stepStartPercentage; return (int)totalPercentage; } static string getApplicationName() { var fi = new FileInfo(getUpdateExe()); return fi.Directory.Name; } static string getUpdateExe() { var assembly = Assembly.GetEntryAssembly(); // Are we update.exe? if (assembly != null && Path.GetFileName(assembly.Location).Equals("update.exe", StringComparison.OrdinalIgnoreCase) && assembly.Location.IndexOf("app-", StringComparison.OrdinalIgnoreCase) == -1 && assembly.Location.IndexOf("SquirrelTemp", StringComparison.OrdinalIgnoreCase) == -1) { return Path.GetFullPath(assembly.Location); } assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly(); var updateDotExe = Path.Combine(Path.GetDirectoryName(assembly.Location), "..\\Update.exe"); var target = new FileInfo(updateDotExe); if (!target.Exists) throw new Exception("Update.exe not found, not a Squirrel-installed app?"); return target.FullName; } } } ================================================ FILE: src/Squirrel/Utility.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Security.Cryptography; using System.Security.Principal; using System.Threading; using Squirrel.SimpleSplat; using System.Text; using System.Threading.Tasks; using System.Collections.Concurrent; using System.Diagnostics; using System.Net; using NuGet; namespace Squirrel { static class Utility { public static string RemoveByteOrderMarkerIfPresent(string content) { return string.IsNullOrEmpty(content) ? string.Empty : RemoveByteOrderMarkerIfPresent(Encoding.UTF8.GetBytes(content)); } public static string RemoveByteOrderMarkerIfPresent(byte[] content) { byte[] output = { }; if (content == null) { goto done; } Func matches = (bom, src) => { if (src.Length < bom.Length) return false; return !bom.Where((chr, index) => src[index] != chr).Any(); }; var utf32Be = new byte[] { 0x00, 0x00, 0xFE, 0xFF }; var utf32Le = new byte[] { 0xFF, 0xFE, 0x00, 0x00 }; var utf16Be = new byte[] { 0xFE, 0xFF }; var utf16Le = new byte[] { 0xFF, 0xFE }; var utf8 = new byte[] { 0xEF, 0xBB, 0xBF }; if (matches(utf32Be, content)) { output = new byte[content.Length - utf32Be.Length]; } else if (matches(utf32Le, content)) { output = new byte[content.Length - utf32Le.Length]; } else if (matches(utf16Be, content)) { output = new byte[content.Length - utf16Be.Length]; } else if (matches(utf16Le, content)) { output = new byte[content.Length - utf16Le.Length]; } else if (matches(utf8, content)) { output = new byte[content.Length - utf8.Length]; } else { output = content; } done: if (output.Length > 0) { Buffer.BlockCopy(content, content.Length - output.Length, output, 0, output.Length); } return Encoding.UTF8.GetString(output); } public static IEnumerable GetAllFilesRecursively(this DirectoryInfo rootPath) { Contract.Requires(rootPath != null); return rootPath.EnumerateFiles("*", SearchOption.AllDirectories); } public static IEnumerable GetAllFilePathsRecursively(string rootPath) { Contract.Requires(rootPath != null); return Directory.EnumerateFiles(rootPath, "*", SearchOption.AllDirectories); } public static string CalculateFileSHA1(string filePath) { Contract.Requires(filePath != null); using (var stream = File.OpenRead(filePath)) { return CalculateStreamSHA1(stream); } } public static string CalculateStreamSHA1(Stream file) { Contract.Requires(file != null && file.CanRead); using (var sha1 = SHA1.Create()) { return BitConverter.ToString(sha1.ComputeHash(file)).Replace("-", String.Empty); } } public static WebClient CreateWebClient() { // enable TLS support // TLS 1.0 and 1.1 are enabled for backward compatibility and should be disabled in the future // for security reasons ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; // disable SSLv3 support for security reasons ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Ssl3; var ret = new WebClient(); var wp = WebRequest.DefaultWebProxy; if (wp != null) { wp.Credentials = CredentialCache.DefaultCredentials; ret.Proxy = wp; } return ret; } public static async Task CopyToAsync(string from, string to) { Contract.Requires(!String.IsNullOrEmpty(from) && File.Exists(from)); Contract.Requires(!String.IsNullOrEmpty(to)); if (!File.Exists(from)) { Log().Warn("The file {0} does not exist", from); // TODO: should we fail this operation? return; } // XXX: SafeCopy await Task.Run(() => File.Copy(from, to, true)); } public static void Retry(this Action block, int retries = 2) { Contract.Requires(retries > 0); Func thunk = () => { block(); return null; }; thunk.Retry(retries); } public static T Retry(this Func block, int retries = 2) { Contract.Requires(retries > 0); while (true) { try { T ret = block(); return ret; } catch (Exception) { if (retries == 0) { throw; } retries--; Thread.Sleep(250); } } } public static Task> InvokeProcessAsync(string fileName, string arguments, CancellationToken ct, string workingDirectory = "") { var psi = new ProcessStartInfo(fileName, arguments); if (Environment.OSVersion.Platform != PlatformID.Win32NT && fileName.EndsWith (".exe", StringComparison.OrdinalIgnoreCase)) { psi = new ProcessStartInfo("wine", fileName + " " + arguments); } psi.UseShellExecute = false; psi.WindowStyle = ProcessWindowStyle.Hidden; psi.ErrorDialog = false; psi.CreateNoWindow = true; psi.RedirectStandardOutput = true; psi.RedirectStandardError = true; psi.WorkingDirectory = workingDirectory; return InvokeProcessAsync(psi, ct); } public static async Task> InvokeProcessAsync(ProcessStartInfo psi, CancellationToken ct) { var pi = Process.Start(psi); await Task.Run(() => { while (!ct.IsCancellationRequested) { if (pi.WaitForExit(2000)) return; } if (ct.IsCancellationRequested) { pi.Kill(); ct.ThrowIfCancellationRequested(); } }); string textResult = await pi.StandardOutput.ReadToEndAsync(); if (String.IsNullOrWhiteSpace(textResult) || pi.ExitCode != 0) { textResult = (textResult ?? "") + "\n" + await pi.StandardError.ReadToEndAsync(); if (String.IsNullOrWhiteSpace(textResult)) { textResult = String.Empty; } } return Tuple.Create(pi.ExitCode, textResult.Trim()); } public static Task ForEachAsync(this IEnumerable source, Action body, int degreeOfParallelism = 4) { return ForEachAsync(source, x => Task.Run(() => body(x)), degreeOfParallelism); } public static Task ForEachAsync(this IEnumerable source, Func body, int degreeOfParallelism = 4) { return Task.WhenAll( from partition in Partitioner.Create(source).GetPartitions(degreeOfParallelism) select Task.Run(async () => { using (partition) while (partition.MoveNext()) await body(partition.Current); })); } static Lazy directoryChars = new Lazy(() => { return "abcdefghijklmnopqrstuvwxyz" + Enumerable.Range(0x03B0, 0x03FF - 0x03B0) // Greek and Coptic .Concat(Enumerable.Range(0x0400, 0x04FF - 0x0400)) // Cyrillic .Aggregate(new StringBuilder(), (acc, x) => { acc.Append(Char.ConvertFromUtf32(x)); return acc; }) .ToString(); }); internal static string tempNameForIndex(int index, string prefix) { if (index < directoryChars.Value.Length) { return prefix + directoryChars.Value[index]; } return prefix + directoryChars.Value[index % directoryChars.Value.Length] + tempNameForIndex(index / directoryChars.Value.Length, ""); } public static DirectoryInfo GetTempDirectory(string localAppDirectory) { var tempDir = Environment.GetEnvironmentVariable("SQUIRREL_TEMP"); tempDir = tempDir ?? Path.Combine(localAppDirectory ?? Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "SquirrelTemp"); var di = new DirectoryInfo(tempDir); if (!di.Exists) di.Create(); return di; } public static IDisposable WithTempDirectory(out string path, string localAppDirectory = null) { var di = GetTempDirectory(localAppDirectory); var tempDir = default(DirectoryInfo); var names = Enumerable.Range(0, 1<<20).Select(x => tempNameForIndex(x, "temp")); foreach (var name in names) { var target = Path.Combine(di.FullName, name); if (!File.Exists(target) && !Directory.Exists(target)) { Directory.CreateDirectory(target); tempDir = new DirectoryInfo(target); break; } } path = tempDir.FullName; return Disposable.Create(() => Task.Run(async () => await DeleteDirectory(tempDir.FullName)).Wait()); } public static IDisposable WithTempFile(out string path, string localAppDirectory = null) { var di = GetTempDirectory(localAppDirectory); var names = Enumerable.Range(0, 1<<20).Select(x => tempNameForIndex(x, "temp")); path = ""; foreach (var name in names) { path = Path.Combine(di.FullName, name); if (!File.Exists(path) && !Directory.Exists(path)) { break; } } var thePath = path; return Disposable.Create(() => File.Delete(thePath)); } public static async Task DeleteDirectory(string directoryPath) { Contract.Requires(!String.IsNullOrEmpty(directoryPath)); Log().Debug("Starting to delete folder: {0}", directoryPath); if (!Directory.Exists(directoryPath)) { Log().Warn("DeleteDirectory: does not exist - {0}", directoryPath); return; } // From http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true/329502#329502 var files = new string[0]; try { files = Directory.GetFiles(directoryPath); } catch (UnauthorizedAccessException ex) { var message = String.Format("The files inside {0} could not be read", directoryPath); Log().Warn(message, ex); } var dirs = new string[0]; try { dirs = Directory.GetDirectories(directoryPath); } catch (UnauthorizedAccessException ex) { var message = String.Format("The directories inside {0} could not be read", directoryPath); Log().Warn(message, ex); } var fileOperations = files.ForEachAsync(file => { File.SetAttributes(file, FileAttributes.Normal); File.Delete(file); }); var directoryOperations = dirs.ForEachAsync(async dir => await DeleteDirectory(dir)); await Task.WhenAll(fileOperations, directoryOperations); Log().Debug("Now deleting folder: {0}", directoryPath); File.SetAttributes(directoryPath, FileAttributes.Normal); try { Directory.Delete(directoryPath, false); } catch (Exception ex) { var message = String.Format("DeleteDirectory: could not delete - {0}", directoryPath); Log().ErrorException(message, ex); } } public static string FindHelperExecutable(string toFind, IEnumerable additionalDirs = null) { additionalDirs = additionalDirs ?? Enumerable.Empty(); var dirs = (new[] { Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) }) .Concat(additionalDirs ?? Enumerable.Empty()); var exe = @".\" + toFind; return dirs .Select(x => Path.Combine(x, toFind)) .FirstOrDefault(x => File.Exists(x)) ?? exe; } static string find7Zip() { if (ModeDetector.InUnitTestRunner()) { var vendorDir = Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", "")), "..", "..", "..", "..", "vendor", "7zip" ); return FindHelperExecutable("7z.exe", new[] { vendorDir }); } else { return FindHelperExecutable("7z.exe"); } } public static async Task ExtractZipToDirectory(string zipFilePath, string outFolder) { var sevenZip = find7Zip(); var result = default(Tuple); try { var cmd = sevenZip; var args = String.Format("x \"{0}\" -tzip -mmt on -aoa -y -o\"{1}\" *", zipFilePath, outFolder); if (Environment.OSVersion.Platform != PlatformID.Win32NT) { cmd = "wine"; args = sevenZip + " " + args; } result = await Utility.InvokeProcessAsync(cmd, args, CancellationToken.None); if (result.Item1 != 0) throw new Exception(result.Item2); } catch (Exception ex) { Log().Error($"Failed to extract file {zipFilePath} to {outFolder}\n{ex.Message}"); throw; } } public static async Task CreateZipFromDirectory(string zipFilePath, string inFolder) { var sevenZip = find7Zip(); var result = default(Tuple); try { var cmd = sevenZip; var args = String.Format("a \"{0}\" -tzip -aoa -y -mmt on *", zipFilePath); if (Environment.OSVersion.Platform != PlatformID.Win32NT) { cmd = "wine"; args = sevenZip + " " + args; } result = await Utility.InvokeProcessAsync(cmd, args, CancellationToken.None, inFolder); if (result.Item1 != 0) throw new Exception(result.Item2); } catch (Exception ex) { Log().Error($"Failed to extract file {zipFilePath} to {inFolder}\n{ex.Message}"); throw; } } public static string AppDirForRelease(string rootAppDirectory, ReleaseEntry entry) { return Path.Combine(rootAppDirectory, "app-" + entry.Version.ToString()); } public static string AppDirForVersion(string rootAppDirectory, SemanticVersion version) { return Path.Combine(rootAppDirectory, "app-" + version.ToString()); } public static string PackageDirectoryForAppDir(string rootAppDirectory) { return Path.Combine(rootAppDirectory, "packages"); } public static string LocalReleaseFileForAppDir(string rootAppDirectory) { return Path.Combine(PackageDirectoryForAppDir(rootAppDirectory), "RELEASES"); } public static IEnumerable LoadLocalReleases(string localReleaseFile) { var file = File.OpenRead(localReleaseFile); // NB: sr disposes file using (var sr = new StreamReader(file, Encoding.UTF8)) { return ReleaseEntry.ParseReleaseFile(sr.ReadToEnd()); } } public static ReleaseEntry FindCurrentVersion(IEnumerable localReleases) { if (!localReleases.Any()) { return null; } return localReleases.OrderByDescending(x => x.Version).FirstOrDefault(x => !x.IsDelta); } static TAcc scan(this IEnumerable This, TAcc initialValue, Func accFunc) { TAcc acc = initialValue; foreach (var x in This) { acc = accFunc(acc, x); } return acc; } public static bool IsHttpUrl(string urlOrPath) { var uri = default(Uri); if (!Uri.TryCreate(urlOrPath, UriKind.Absolute, out uri)) { return false; } return uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps; } public static Uri AppendPathToUri(Uri uri, string path) { var builder = new UriBuilder(uri); if (!builder.Path.EndsWith("/")) { builder.Path += "/"; } builder.Path += path; return builder.Uri; } public static Uri EnsureTrailingSlash(Uri uri) { return AppendPathToUri(uri, ""); } public static Uri AddQueryParamsToUri(Uri uri, IEnumerable> newQuery) { var query = System.Web.HttpUtility.ParseQueryString(uri.Query); foreach (var entry in newQuery) { query[entry.Key] = entry.Value; } var builder = new UriBuilder(uri); builder.Query = query.ToString(); return builder.Uri; } public static void DeleteFileHarder(string path, bool ignoreIfFails = false) { try { Retry(() => File.Delete(path), 2); } catch (Exception ex) { if (ignoreIfFails) return; LogHost.Default.ErrorException("Really couldn't delete file: " + path, ex); throw; } } public static async Task DeleteDirectoryOrJustGiveUp(string dir) { try { await Utility.DeleteDirectory(dir); } catch { var message = String.Format("Uninstall failed to delete dir '{0}'", dir); } } // http://stackoverflow.com/questions/3111669/how-can-i-determine-the-subsystem-used-by-a-given-net-assembly public static bool ExecutableUsesWin32Subsystem(string peImage) { using (var s = new FileStream(peImage, FileMode.Open, FileAccess.Read)) { var rawPeSignatureOffset = new byte[4]; s.Seek(0x3c, SeekOrigin.Begin); s.Read(rawPeSignatureOffset, 0, 4); int peSignatureOffset = rawPeSignatureOffset[0]; peSignatureOffset |= rawPeSignatureOffset[1] << 8; peSignatureOffset |= rawPeSignatureOffset[2] << 16; peSignatureOffset |= rawPeSignatureOffset[3] << 24; var coffHeader = new byte[24]; s.Seek(peSignatureOffset, SeekOrigin.Begin); s.Read(coffHeader, 0, 24); byte[] signature = { (byte)'P', (byte)'E', (byte)'\0', (byte)'\0' }; for (int index = 0; index < 4; index++) { if (coffHeader[index] != signature[index]) throw new Exception("File is not a PE image"); } var subsystemBytes = new byte[2]; s.Seek(68, SeekOrigin.Current); s.Read(subsystemBytes, 0, 2); int subSystem = subsystemBytes[0] | subsystemBytes[1] << 8; return subSystem == 2; /*IMAGE_SUBSYSTEM_WINDOWS_GUI*/ } } readonly static string[] peExtensions = new[] { ".exe", ".dll", ".node" }; public static bool FileIsLikelyPEImage(string name) { var ext = Path.GetExtension(name); return peExtensions.Any(x => ext.Equals(x, StringComparison.OrdinalIgnoreCase)); } public static bool IsFileTopLevelInPackage(string fullName, string pkgPath) { var fn = fullName.ToLowerInvariant(); var pkg = pkgPath.ToLowerInvariant(); var relativePath = fn.Replace(pkg, ""); // NB: We want to match things like `/lib/net45/foo.exe` but not `/lib/net45/bar/foo.exe` return relativePath.Split(Path.DirectorySeparatorChar).Length == 4; } public static void LogIfThrows(this IFullLogger This, LogLevel level, string message, Action block) { try { block(); } catch (Exception ex) { switch (level) { case LogLevel.Debug: This.DebugException(message ?? "", ex); break; case LogLevel.Info: This.InfoException(message ?? "", ex); break; case LogLevel.Warn: This.WarnException(message ?? "", ex); break; case LogLevel.Error: This.ErrorException(message ?? "", ex); break; } throw; } } public static async Task LogIfThrows(this IFullLogger This, LogLevel level, string message, Func block) { try { await block(); } catch (Exception ex) { switch (level) { case LogLevel.Debug: This.DebugException(message ?? "", ex); break; case LogLevel.Info: This.InfoException(message ?? "", ex); break; case LogLevel.Warn: This.WarnException(message ?? "", ex); break; case LogLevel.Error: This.ErrorException(message ?? "", ex); break; } throw; } } public static async Task LogIfThrows(this IFullLogger This, LogLevel level, string message, Func> block) { try { return await block(); } catch (Exception ex) { switch (level) { case LogLevel.Debug: This.DebugException(message ?? "", ex); break; case LogLevel.Info: This.InfoException(message ?? "", ex); break; case LogLevel.Warn: This.WarnException(message ?? "", ex); break; case LogLevel.Error: This.ErrorException(message ?? "", ex); break; } throw; } } public static void WarnIfThrows(this IEnableLogger This, Action block, string message = null) { This.Log().LogIfThrows(LogLevel.Warn, message, block); } public static Task WarnIfThrows(this IEnableLogger This, Func block, string message = null) { return This.Log().LogIfThrows(LogLevel.Warn, message, block); } public static Task WarnIfThrows(this IEnableLogger This, Func> block, string message = null) { return This.Log().LogIfThrows(LogLevel.Warn, message, block); } public static void ErrorIfThrows(this IEnableLogger This, Action block, string message = null) { This.Log().LogIfThrows(LogLevel.Error, message, block); } public static Task ErrorIfThrows(this IEnableLogger This, Func block, string message = null) { return This.Log().LogIfThrows(LogLevel.Error, message, block); } public static Task ErrorIfThrows(this IEnableLogger This, Func> block, string message = null) { return This.Log().LogIfThrows(LogLevel.Error, message, block); } static IFullLogger logger; static IFullLogger Log() { return logger ?? (logger = SquirrelLocator.CurrentMutable.GetService().GetLogger(typeof(Utility))); } [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags); [Flags] enum MoveFileFlags { MOVEFILE_REPLACE_EXISTING = 0x00000001, MOVEFILE_COPY_ALLOWED = 0x00000002, MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004, MOVEFILE_WRITE_THROUGH = 0x00000008, MOVEFILE_CREATE_HARDLINK = 0x00000010, MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020 } public static Guid CreateGuidFromHash(string text) { return CreateGuidFromHash(text, Utility.IsoOidNamespace); } public static Guid CreateGuidFromHash(byte[] data) { return CreateGuidFromHash(data, Utility.IsoOidNamespace); } public static Guid CreateGuidFromHash(string text, Guid namespaceId) { return CreateGuidFromHash(Encoding.UTF8.GetBytes(text), namespaceId); } public static Guid CreateGuidFromHash(byte[] nameBytes, Guid namespaceId) { // convert the namespace UUID to network order (step 3) byte[] namespaceBytes = namespaceId.ToByteArray(); SwapByteOrder(namespaceBytes); // comput the hash of the name space ID concatenated with the // name (step 4) byte[] hash; using (var algorithm = SHA1.Create()) { algorithm.TransformBlock(namespaceBytes, 0, namespaceBytes.Length, null, 0); algorithm.TransformFinalBlock(nameBytes, 0, nameBytes.Length); hash = algorithm.Hash; } // most bytes from the hash are copied straight to the bytes of // the new GUID (steps 5-7, 9, 11-12) var newGuid = new byte[16]; Array.Copy(hash, 0, newGuid, 0, 16); // set the four most significant bits (bits 12 through 15) of // the time_hi_and_version field to the appropriate 4-bit // version number from Section 4.1.3 (step 8) newGuid[6] = (byte)((newGuid[6] & 0x0F) | (5 << 4)); // set the two most significant bits (bits 6 and 7) of the // clock_seq_hi_and_reserved to zero and one, respectively // (step 10) newGuid[8] = (byte)((newGuid[8] & 0x3F) | 0x80); // convert the resulting UUID to local byte order (step 13) SwapByteOrder(newGuid); return new Guid(newGuid); } /// /// The namespace for fully-qualified domain names (from RFC 4122, Appendix C). /// public static readonly Guid DnsNamespace = new Guid("6ba7b810-9dad-11d1-80b4-00c04fd430c8"); /// /// The namespace for URLs (from RFC 4122, Appendix C). /// public static readonly Guid UrlNamespace = new Guid("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); /// /// The namespace for ISO OIDs (from RFC 4122, Appendix C). /// public static readonly Guid IsoOidNamespace = new Guid("6ba7b812-9dad-11d1-80b4-00c04fd430c8"); // Converts a GUID (expressed as a byte array) to/from network order (MSB-first). static void SwapByteOrder(byte[] guid) { SwapBytes(guid, 0, 3); SwapBytes(guid, 1, 2); SwapBytes(guid, 4, 5); SwapBytes(guid, 6, 7); } static void SwapBytes(byte[] guid, int left, int right) { byte temp = guid[left]; guid[left] = guid[right]; guid[right] = temp; } } static unsafe class UnsafeUtility { public static List> EnumerateProcesses() { int bytesReturned = 0; var pids = new int[16384]; fixed(int* p = pids) { if (!NativeMethods.EnumProcesses((IntPtr)p, sizeof(int) * pids.Length, out bytesReturned)) { throw new Win32Exception("Failed to enumerate processes"); } if (bytesReturned < 1) throw new Exception("Failed to enumerate processes"); } return Enumerable.Range(0, bytesReturned / sizeof(int)) .Where(i => pids[i] > 0) .Select(i => { try { var hProcess = NativeMethods.OpenProcess(ProcessAccess.QueryLimitedInformation, false, pids[i]); if (hProcess == IntPtr.Zero) throw new Win32Exception(); var sb = new StringBuilder(256); var capacity = sb.Capacity; if (!NativeMethods.QueryFullProcessImageName(hProcess, 0, sb, ref capacity)) { throw new Win32Exception(); } NativeMethods.CloseHandle(hProcess); return Tuple.Create(sb.ToString(), pids[i]); } catch (Exception) { return Tuple.Create(default(string), pids[i]); } }) .ToList(); } } sealed class SingleGlobalInstance : IDisposable, IEnableLogger { IDisposable handle = null; public SingleGlobalInstance(string key, TimeSpan timeOut) { if (ModeDetector.InUnitTestRunner()) { return; } var path = Path.Combine(Path.GetTempPath(), ".squirrel-lock-" + key); var st = new Stopwatch(); st.Start(); var fh = default(FileStream); while (st.Elapsed < timeOut) { try { fh = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Delete); fh.Write(new byte[] { 0xba, 0xad, 0xf0, 0x0d, }, 0, 4); break; } catch (Exception ex) { this.Log().WarnException("Failed to grab lockfile, will retry: " + path, ex); Thread.Sleep(250); } } st.Stop(); if (fh == null) { throw new Exception("Couldn't acquire lock, is another instance running"); } handle = Disposable.Create(() => { fh.Dispose(); File.Delete(path); }); } public void Dispose() { if (ModeDetector.InUnitTestRunner()) { return; } var disp = Interlocked.Exchange(ref handle, null); if (disp != null) disp.Dispose(); } ~SingleGlobalInstance() { if (handle == null) return; throw new AbandonedMutexException("Leaked a Mutex!"); } } static class Disposable { public static IDisposable Create(Action action) { return new AnonDisposable(action); } class AnonDisposable : IDisposable { static readonly Action dummyBlock = (() => { }); Action block; public AnonDisposable(Action b) { block = b; } public void Dispose() { Interlocked.Exchange(ref block, dummyBlock)(); } } } } ================================================ FILE: src/Squirrel.nuspec ================================================  2.0.1 GitHub Anaïs Betts https://github.com/squirrel/Squirrel.Windows/blob/master/COPYING https://github.com/squirrel/Squirrel.Windows https://raw.githubusercontent.com/Squirrel/Squirrel.Windows/master/docs/artwork/Squirrel-Logo-Square.png squirrel.windows Squirrel for Windows false An installation and update framework for Windows applications Copyright GitHub© 2017 ================================================ FILE: src/StubExecutable/LICENSE.md ================================================ This executable uses the semver library from https://github.com/zmarko/semver, under the following license: The MIT License (MIT) Copyright (c) 2015 Marko Živanovc Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: src/StubExecutable/Resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by StubExecutable.rc // #define IDS_APP_TITLE 103 #define IDR_MAINFRAME 128 #define IDD_STUBEXECUTABLE_DIALOG 102 #define IDD_ABOUTBOX 103 #define IDM_ABOUT 104 #define IDM_EXIT 105 #define IDI_STUBEXECUTABLE 107 #define IDI_SMALL 108 #define IDC_STUBEXECUTABLE 109 #define IDC_MYICON 2 #ifndef IDC_STATIC #define IDC_STATIC -1 #endif // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 130 #define _APS_NEXT_RESOURCE_VALUE 129 #define _APS_NEXT_COMMAND_VALUE 32771 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 110 #endif #endif ================================================ FILE: src/StubExecutable/Semver200_comparator.cpp ================================================ /* The MIT License (MIT) Copyright (c) 2015 Marko Zivanovic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "stdafx.h" #include #include #include #include "semver200.h" using namespace std; namespace version { namespace { // Compare normal version identifiers. int compare_normal(const Version_data& l, const Version_data& r) { if (l.major > r.major) return 1; if (l.major < r.major) return -1; if (l.minor > r.minor) return 1; if (l.minor < r.minor) return -1; if (l.patch > r.patch) return 1; if (l.patch < r.patch) return -1; return 0; } // Compare alphanumeric prerelease identifiers. inline int cmp_alnum_prerel_ids(const string& l, const string& r) { // If both versions have a prerelease section with the same prefix // and end with digits, compare based on the digits' numeric order auto index_l = l.find_last_not_of("0123456789"); auto index_r = r.find_last_not_of("0123456789"); if (index_l != string::npos && index_r != string::npos) { string name_l = l.substr(0, index_l + 1); string name_r = r.substr(0, index_r + 1); if (name_l == name_r) { int il = stoi(l.substr(index_l + 1)); int ir = stoi(r.substr(index_r + 1)); return il > ir ? 1 : -1; } } auto cmp = l.compare(r); if (cmp == 0) { return cmp; } else { return cmp > 0 ? 1 : -1; } } // Compare numeric prerelease identifiers. inline int cmp_num_prerel_ids(const string& l, const string& r) { long long li = stoll(l); long long ri = stoll(r); if (li == ri) return 0; return li > ri ? 1 : -1; } using Prerel_type_pair = pair; using Prerel_id_comparator = function; const map comparators = { { { Id_type::alnum, Id_type::alnum }, cmp_alnum_prerel_ids }, { { Id_type::alnum, Id_type::num }, [](const string&, const string&) {return 1;} }, { { Id_type::num, Id_type::alnum }, [](const string&, const string&) {return -1;} }, { { Id_type::num, Id_type::num }, cmp_num_prerel_ids } }; // Compare prerelease identifiers based on their types. inline int compare_prerel_identifiers(const Prerelease_identifier& l, const Prerelease_identifier& r) { auto cmp = comparators.at({ l.second, r.second }); return cmp(l.first, r.first); } inline int cmp_rel_prerel(const Prerelease_identifiers& l, const Prerelease_identifiers& r) { if (l.empty() && !r.empty()) return 1; if (r.empty() && !l.empty()) return -1; return 0; } } int Semver200_comparator::compare(const Version_data& l, const Version_data& r) const { // Compare normal version components. int cmp = compare_normal(l, r); if (cmp != 0) return cmp; // Compare if one version is release and the other prerelease - release is always higher. cmp = cmp_rel_prerel(l.prerelease_ids, r.prerelease_ids); if (cmp != 0) return cmp; // Compare prerelease by looking at each identifier: numeric ones are compared as numbers, // alphanum as ASCII strings. auto shorter = min(l.prerelease_ids.size(), r.prerelease_ids.size()); for (size_t i = 0; i < shorter; i++) { cmp = compare_prerel_identifiers(l.prerelease_ids[i], r.prerelease_ids[i]); if (cmp != 0) return cmp; } // Prerelease identifiers are the same, to the length of the shorter version string; // if they are the same length, then versions are equal, otherwise, longer one wins. if (l.prerelease_ids.size() == r.prerelease_ids.size()) return 0; return l.prerelease_ids.size() > r.prerelease_ids.size() ? 1 : -1; } } ================================================ FILE: src/StubExecutable/Semver200_parser.cpp ================================================ /* The MIT License (MIT) Copyright (c) 2015 Marko Zivanovic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "stdafx.h" #include #include #include "semver200.h" #ifdef _MSC_VER // disable symbol name too long warning #pragma warning(disable:4503) #endif using namespace std; namespace version { namespace { enum class Parser_state { major, minor, patch, prerelease, build }; using Validator = function; using State_transition_hook = function; /// State transition is described by a character that triggers it, a state to transition to and /// optional hook to be invoked on transition. using Transition = tuple; using Transitions = vector; using State = tuple; using State_machine = map; // Ranges of characters allowed in prerelease and build identifiers. const vector> allowed_prerel_id_chars = { { '0', '9' },{ 'A','Z' },{ 'a','z' },{ '-','-' } }; inline Transition mkx(const char c, Parser_state p, State_transition_hook pth) { return make_tuple(c, p, pth); } /// Advance parser state machine by a single step. /** Perform single step of parser state machine: if character matches one from transition tables - trigger transition to next state; otherwise, validate if current token is in legal state (throw Parse_error if not) and then add character to current token; State transition includes preparing various vars for next state and invoking state transition hook (if specified) which is where whole tokens are validated. */ inline void process_char(const char c, Parser_state& cstate, Parser_state& pstate, const Transitions& transitions, string& target, Validator validate) { for (const auto& transition : transitions) { if (c == get<0>(transition)) { if (get<2>(transition)) get<2>(transition)(target); pstate = cstate; cstate = get<1>(transition); return; } } validate(target, c); target.push_back(c); } /// Validate normal (major, minor, patch) version components. inline void normal_version_validator(const string& tgt, const char c) { if (c < '0' || c > '9') throw Parse_error("invalid character encountered: " + string(1, c)); if (tgt.compare(0, 1, "0") == 0) throw Parse_error("leading 0 not allowed"); } /// Validate that prerelease and build version identifiers are comprised of allowed chars only. inline void prerelease_version_validator(const string&, const char c) { bool res = false; for (const auto& r : allowed_prerel_id_chars) { res |= (c >= r.first && c <= r.second); } if (!res) throw Parse_error("invalid character encountered: " + string(1, c)); } inline bool is_identifier_numeric(const string& id) { return id.find_first_not_of("0123456789") == string::npos; } inline bool check_for_leading_0(const string& str) { return str.length() > 1 && str[0] == '0'; } /// Validate every individual prerelease identifier, determine it's type and add it to collection. void prerelease_hook_impl(string& id, Prerelease_identifiers& prerelease) { if (id.empty()) throw Parse_error("version identifier cannot be empty"); Id_type t = Id_type::alnum; if (is_identifier_numeric(id)) { t = Id_type::num; if (check_for_leading_0(id)) { throw Parse_error("numeric identifiers cannot have leading 0"); } } prerelease.push_back(Prerelease_identifier(id, t)); id.clear(); } /// Validate every individual build identifier and add it to collection. void build_hook_impl(string& id, Parser_state& pstate, Build_identifiers& build, std::string& prerelease_id, Prerelease_identifiers& prerelease) { // process last token left from parsing prerelease data if (pstate == Parser_state::prerelease) prerelease_hook_impl(prerelease_id, prerelease); if (id.empty()) throw Parse_error("version identifier cannot be empty"); build.push_back(id); id.clear(); } } /// Parse semver 2.0.0-compatible string to Version_data structure. /** Version text parser is implemented as a state machine. In each step one successive character from version string is consumed and is either added to current token or triggers state transition. Hooks can be injected into state transitions for validation/customization purposes. */ Version_data Semver200_parser::parse(const string& s) const { string major; string minor; string patch; string prerelease_id; string build_id; Prerelease_identifiers prerelease; Build_identifiers build; Parser_state cstate{ Parser_state::major }; Parser_state pstate; auto prerelease_hook = [&](string& id) { prerelease_hook_impl(id, prerelease); }; auto build_hook = [&](string& id) { build_hook_impl(id, pstate, build, prerelease_id, prerelease); }; // State transition tables auto major_trans = { mkx('.', Parser_state::minor, {}) }; auto minor_trans = { mkx('.', Parser_state::patch, {}) }; auto patch_trans = { mkx('-', Parser_state::prerelease, {}), mkx('+', Parser_state::build, {}) }; auto prerelease_trans = { // When identifier separator (.) is found, stay in the same state but invoke hook // in order to process each individual identifier separately. mkx('.', Parser_state::prerelease, prerelease_hook), mkx('+', Parser_state::build, {}) }; auto build_trans = { // Same stay-in-the-same-state-but-invoke-hook trick from above. mkx('.', Parser_state::build, build_hook) }; State_machine state_machine = { {Parser_state::major, State{major_trans, major, normal_version_validator}}, {Parser_state::minor, State{minor_trans, minor, normal_version_validator}}, {Parser_state::patch, State{patch_trans, patch, normal_version_validator}}, {Parser_state::prerelease, State{prerelease_trans, prerelease_id, prerelease_version_validator}}, {Parser_state::build, State{build_trans, build_id, prerelease_version_validator}} }; // Main loop. for (const auto& c : s) { auto state = state_machine.at(cstate); process_char(c, cstate, pstate, get<0>(state), get<1>(state), get<2>(state)); } // Trigger appropriate hooks in order to process last token, because no state transition was // triggered for it. if (cstate == Parser_state::prerelease) { prerelease_hook(prerelease_id); } else if (cstate == Parser_state::build) { build_hook(build_id); } try { return Version_data{ stoi(major), stoi(minor), stoi(patch), prerelease, build }; } catch (invalid_argument& ex) { throw Parse_error(ex.what()); } } } ================================================ FILE: src/StubExecutable/StubExecutable.cpp ================================================ // StubExecutable.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "StubExecutable.h" #include "semver200.h" using namespace std; bool FileExists(const std::wstring& filePath) { DWORD fileAttributes = GetFileAttributes(filePath.c_str()); return (fileAttributes != INVALID_FILE_ATTRIBUTES) && !(fileAttributes & FILE_ATTRIBUTE_DIRECTORY); } wchar_t* FindRootAppDir() { wchar_t* ourDirectory = new wchar_t[MAX_PATH]; GetModuleFileName(GetModuleHandle(NULL), ourDirectory, MAX_PATH); wchar_t* lastSlash = wcsrchr(ourDirectory, L'\\'); if (!lastSlash) { delete[] ourDirectory; return NULL; } // Null-terminate the string at the slash so now it's a directory *lastSlash = 0x0; return ourDirectory; } wchar_t* FindOwnExecutableName() { wchar_t* ourDirectory = new wchar_t[MAX_PATH]; GetModuleFileName(GetModuleHandle(NULL), ourDirectory, MAX_PATH); wchar_t* lastSlash = wcsrchr(ourDirectory, L'\\'); if (!lastSlash) { delete[] ourDirectory; return NULL; } wchar_t* ret = _wcsdup(lastSlash + 1); delete[] ourDirectory; return ret; } std::wstring FindLatestAppDir() { std::wstring ourDir; ourDir.assign(FindRootAppDir()); ourDir += L"\\app-*"; WIN32_FIND_DATA fileInfo = { 0 }; HANDLE hFile = FindFirstFile(ourDir.c_str(), &fileInfo); if (hFile == INVALID_HANDLE_VALUE) { return NULL; } version::Semver200_version acc("0.0.0"); std::wstring acc_s; do { std::wstring appVer = fileInfo.cFileName; appVer = appVer.substr(4); // Skip 'app-' if (!(fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { continue; } std::string s(appVer.begin(), appVer.end()); version::Semver200_version thisVer(s); // Skip the directory which contains a .not-finished file std::wstring appFolder = fileInfo.cFileName; std::wstring dirPath = ourDir.substr(0, ourDir.size() - 5) + appFolder; if (FileExists(dirPath + L"\\.not-finished")) { continue; } if (thisVer > acc) { acc = thisVer; acc_s = appVer; } } while (FindNextFile(hFile, &fileInfo)); if (acc == version::Semver200_version("0.0.0")) { return NULL; } ourDir.assign(FindRootAppDir()); std::wstringstream ret; ret << ourDir << L"\\app-" << acc_s; FindClose(hFile); return ret.str(); } int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { std::wstring appName; appName.assign(FindOwnExecutableName()); std::wstring workingDir(FindLatestAppDir()); std::wstring fullPath(workingDir + L"\\" + appName); STARTUPINFO si = { 0 }; PROCESS_INFORMATION pi = { 0 }; si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = nCmdShow; std::wstring cmdLine(L"\""); cmdLine += fullPath; cmdLine += L"\" "; cmdLine += lpCmdLine; wchar_t* lpCommandLine = _wcsdup(cmdLine.c_str()); wchar_t* lpCurrentDirectory = _wcsdup(workingDir.c_str()); if (!CreateProcess(NULL, lpCommandLine, NULL, NULL, true, 0, NULL, lpCurrentDirectory, &si, &pi)) { return -1; } AllowSetForegroundWindow(pi.dwProcessId); WaitForInputIdle(pi.hProcess, 5 * 1000); return 0; } ================================================ FILE: src/StubExecutable/StubExecutable.h ================================================ #pragma once #include "resource.h" ================================================ FILE: src/StubExecutable/StubExecutable.vcxproj ================================================  Debug Win32 Release Win32 Debug x64 Release x64 {C028DB2A-E7C5-4232-8C22-D5FBA2176136} Application Win32Proj StubExecutable v142 Unicode 10.0 true false true true true false false Use Level3 Disabled WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true Use Level3 Disabled _DEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true Level3 Use MinSpace true true WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) Size MultiThreaded Windows true true true Level3 Use MaxSpeed true true NDEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true true true Create ================================================ FILE: src/StubExecutable/StubExecutable.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Resource Files Resource Files Resource Files ================================================ FILE: src/StubExecutable/semver200.h ================================================ /* The MIT License (MIT) Copyright (c) 2015 Marko Zivanovic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "version.h" namespace version { /// Parse string into Version_data structure according to semantic versioning 2.0.0 rules. struct Semver200_parser { Version_data parse(const std::string&) const; }; /// Compare Version_data to another using semantic versioning 2.0.0 rules. struct Semver200_comparator { int compare(const Version_data&, const Version_data&) const; }; /// Concrete version class that binds all semver 2.0.0 functionality together. class Semver200_version : public Basic_version { public: Semver200_version() : Basic_version{ Semver200_parser(), Semver200_comparator() } {} Semver200_version(const std::string& v) : Basic_version{ v, Semver200_parser(), Semver200_comparator() } {} }; } ================================================ FILE: src/StubExecutable/stdafx.cpp ================================================ // stdafx.cpp : source file that includes just the standard includes // StubExecutable.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" // TODO: reference any additional headers you need in STDAFX.H // and not in this file ================================================ FILE: src/StubExecutable/stdafx.h ================================================ // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #pragma once #include "targetver.h" #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files: #include // C RunTime Header Files #include #include #include #include #include #include // TODO: reference additional headers your program requires here ================================================ FILE: src/StubExecutable/targetver.h ================================================ #pragma once // Including SDKDDKVer.h defines the highest available Windows platform. // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. #include ================================================ FILE: src/StubExecutable/version.h ================================================ /* The MIT License (MIT) Copyright (c) 2015 Marko Zivanovic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include namespace version { /// Any error in parsing or validation of version string will result in Parse_error exception being thrown. class Parse_error : public std::runtime_error { using std::runtime_error::runtime_error; }; /// Type of prerelease identifier: alphanumeric or numeric. /** Type of identifier affects comparison: alphanumeric identifiers are compared as ASCII strings, while numeric identifiers are compared as numbers. */ enum class Id_type { alnum, ///< Identifier is alphanumerical num ///< Identifier is numeric }; /// Container for prerelease identifier value and it's type. /** Prerelease version string consist of an optional series of dot-separated identifiers. These identifiers can be either numerical or alphanumerical. This structure describes one such identifier. */ using Prerelease_identifier = std::pair; /// Container for all prerelease identifiers for a given version string. using Prerelease_identifiers = std::vector; /// Build identifier is arbitrary string with no special meaning with regards to version precedence. using Build_identifier = std::string; /// Container for all build identifiers of a given version string. using Build_identifiers = std::vector; /// Description of version broken into parts, as per semantic versioning specification. struct Version_data { int major; ///< Major version, change only on incompatible API modifications. int minor; ///< Minor version, change on backwards-compatible API modifications. int patch; ///< Patch version, change only on bugfixes. /// Optional series of prerelease identifiers. Prerelease_identifiers prerelease_ids; /// Optional series of build identifiers. Build_identifiers build_ids; }; // Forward declaration required for operators' template declarations. template class Basic_version; /// Test if left-hand version operand is of lower precedence than the right-hand version. template bool operator<(const Basic_version&, const Basic_version&); /// Test if left-hand version operand if of equal precedence as the right-hand version. template bool operator==(const Basic_version&, const Basic_version&); /// Output version object to stream using standard semver format (X.Y.Z-PR+B). template std::ostream& operator<<(std::ostream&, const Basic_version&); /// Test if left-hand version and right-hand version are of different precedence. template bool operator!=(const Basic_version&, const Basic_version&); /// Test if left-hand version operand is of higher precedence than the right-hand version. template bool operator>(const Basic_version&, const Basic_version&); /// Test if left-hand version operand is of higher or equal precedence as the right-hand version. template bool operator>=(const Basic_version&, const Basic_version&); /// Test if left-hand version operand is of lower or equal precedence as the right-hand version. template bool operator<=(const Basic_version&, const Basic_version&); /// Base class for various version parsing and precedence ordering schemes. /** Basic_version class describes general version object without prescribing parsing, validation and comparison rules. These rules are implemented by supplied Parser and Comparator objects. */ template class Basic_version { public: /// Construct Basic_version object using Parser object to parse default ("0.0.0") version string and Comparator for comparison. Basic_version(Parser, Comparator); /// Construct Basic_version object using Parser to parse supplied version string and Comparator for comparison. Basic_version(const std::string&, Parser, Comparator); /// Construct Basic_version by copying data from another one. Basic_version(const Basic_version&); /// Copy version data from another Basic_version to this one. Basic_version& operator=(const Basic_version&); int major() const; ///< Get major version. int minor() const; ///< Get minor version. int patch() const; ///< Get patch version. const std::string prerelease() const; ///< Get prerelease version string. const std::string build() const; ///< Get build version string. friend bool operator< <>(const Basic_version&, const Basic_version&); friend bool operator== <>(const Basic_version&, const Basic_version&); friend std::ostream& operator<< <>(std::ostream&s, const Basic_version&); private: Parser parser_; Comparator comparator_; Version_data ver_; }; } #include "version.inl" ================================================ FILE: src/StubExecutable/version.inl ================================================ /* The MIT License (MIT) Copyright (c) 2015 Marko Zivanovic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "version.h" namespace version { namespace { /// Utility function to splice all vector elements to output stream, using designated separator /// between elements and function object for getting values from vector elements. template std::ostream& splice(std::ostream& os, const std::vector& vec, const std::string& sep, F read) { if (!vec.empty()) { for (auto it = vec.cbegin(); it < vec.cend() - 1; ++it) { os << read(*it) << sep; } os << read(*vec.crbegin()); } return os; } } template Basic_version::Basic_version(Parser p, Comparator c) : parser_(p), comparator_(c), ver_(parser_.parse("0.0.0")) {} template Basic_version::Basic_version(const std::string& v, Parser p, Comparator c) : parser_(p), comparator_(c), ver_(parser_.parse(v)) {} template Basic_version::Basic_version(const Basic_version&) = default; template Basic_version& Basic_version::operator=( const Basic_version&) = default; template int Basic_version::major() const { return ver_.major; } template int Basic_version::minor() const { return ver_.minor; } template int Basic_version::patch() const { return ver_.patch; } template const std::string Basic_version::prerelease() const { std::stringstream ss; splice(ss, ver_.prerelease_ids, ".", [](const auto& id) { return id.first;}); return ss.str(); } template const std::string Basic_version::build() const { std::stringstream ss; splice(ss, ver_.build_ids, ".", [](const auto& id) { return id;}); return ss.str(); } template bool operator<(const Basic_version& l, const Basic_version& r) { return l.comparator_.compare(l.ver_, r.ver_) == -1; } template bool operator==(const Basic_version& l, const Basic_version& r) { return l.comparator_.compare(l.ver_, r.ver_) == 0; } template std::ostream& operator<<(std::ostream& os, const Basic_version& v) { os << v.ver_.major << "." << v.ver_.minor << "." << v.ver_.patch; std::string prl = v.prerelease(); if (!prl.empty()) { os << "-" << prl; } std::string bld = v.build(); if (!bld.empty()) { os << "+" << bld; } return os; } template inline bool operator!=(const Basic_version& l, const Basic_version& r) { return !(l == r); } template inline bool operator>(const Basic_version& l, const Basic_version& r) { return r < l; } template inline bool operator>=(const Basic_version& l, const Basic_version& r) { return !(l < r); } template inline bool operator<=(const Basic_version& l, const Basic_version& r) { return !(l > r); } } ================================================ FILE: src/SyncReleases/App.config ================================================  ================================================ FILE: src/SyncReleases/Mono.Options/Options.cs ================================================ // // Options.cs // // Authors: // Jonathan Pryor // Federico Di Gregorio // Rolf Bjarne Kvinge // // Copyright (C) 2008 Novell (http://www.novell.com) // Copyright (C) 2009 Federico Di Gregorio. // Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // Compile With: // gmcs -debug+ -r:System.Core Options.cs -o:NDesk.Options.dll // gmcs -debug+ -d:LINQ -r:System.Core Options.cs -o:NDesk.Options.dll // // The LINQ version just changes the implementation of // OptionSet.Parse(IEnumerable), and confers no semantic changes. // // A Getopt::Long-inspired option parsing library for C#. // // NDesk.Options.OptionSet is built upon a key/value table, where the // key is a option format string and the value is a delegate that is // invoked when the format string is matched. // // Option format strings: // Regex-like BNF Grammar: // name: .+ // type: [=:] // sep: ( [^{}]+ | '{' .+ '}' )? // aliases: ( name type sep ) ( '|' name type sep )* // // Each '|'-delimited name is an alias for the associated action. If the // format string ends in a '=', it has a required value. If the format // string ends in a ':', it has an optional value. If neither '=' or ':' // is present, no value is supported. `=' or `:' need only be defined on one // alias, but if they are provided on more than one they must be consistent. // // Each alias portion may also end with a "key/value separator", which is used // to split option values if the option accepts > 1 value. If not specified, // it defaults to '=' and ':'. If specified, it can be any character except // '{' and '}' OR the *string* between '{' and '}'. If no separator should be // used (i.e. the separate values should be distinct arguments), then "{}" // should be used as the separator. // // Options are extracted either from the current option by looking for // the option name followed by an '=' or ':', or is taken from the // following option IFF: // - The current option does not contain a '=' or a ':' // - The current option requires a value (i.e. not a Option type of ':') // // The `name' used in the option format string does NOT include any leading // option indicator, such as '-', '--', or '/'. All three of these are // permitted/required on any named option. // // Option bundling is permitted so long as: // - '-' is used to start the option group // - all of the bundled options are a single character // - at most one of the bundled options accepts a value, and the value // provided starts from the next character to the end of the string. // // This allows specifying '-a -b -c' as '-abc', and specifying '-D name=value' // as '-Dname=value'. // // Option processing is disabled by specifying "--". All options after "--" // are returned by OptionSet.Parse() unchanged and unprocessed. // // Unprocessed options are returned from OptionSet.Parse(). // // Examples: // int verbose = 0; // OptionSet p = new OptionSet () // .Add ("v", v => ++verbose) // .Add ("name=|value=", v => Console.WriteLine (v)); // p.Parse (new string[]{"-v", "--v", "/v", "-name=A", "/name", "B", "extra"}); // // The above would parse the argument string array, and would invoke the // lambda expression three times, setting `verbose' to 3 when complete. // It would also print out "A" and "B" to standard output. // The returned array would contain the string "extra". // // C# 3.0 collection initializers are supported and encouraged: // var p = new OptionSet () { // { "h|?|help", v => ShowHelp () }, // }; // // System.ComponentModel.TypeConverter is also supported, allowing the use of // custom data types in the callback type; TypeConverter.ConvertFromString() // is used to convert the value option to an instance of the specified // type: // // var p = new OptionSet () { // { "foo=", (Foo f) => Console.WriteLine (f.ToString ()) }, // }; // // Random other tidbits: // - Boolean options (those w/o '=' or ':' in the option format string) // are explicitly enabled if they are followed with '+', and explicitly // disabled if they are followed with '-': // string a = null; // var p = new OptionSet () { // { "a", s => a = s }, // }; // p.Parse (new string[]{"-a"}); // sets v != null // p.Parse (new string[]{"-a+"}); // sets v != null // p.Parse (new string[]{"-a-"}); // sets v == null // using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; using System.IO; using System.Runtime.Serialization; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; #if LINQ using System.Linq; #endif #if TEST using NDesk.Options; #endif #if NDESK_OPTIONS namespace NDesk.Options #else namespace Mono.Options #endif { static class StringCoda { public static IEnumerable WrappedLines (string self, params int[] widths) { IEnumerable w = widths; return WrappedLines (self, w); } public static IEnumerable WrappedLines (string self, IEnumerable widths) { if (widths == null) throw new ArgumentNullException ("widths"); return CreateWrappedLinesIterator (self, widths); } private static IEnumerable CreateWrappedLinesIterator (string self, IEnumerable widths) { if (string.IsNullOrEmpty (self)) { yield return string.Empty; yield break; } using (IEnumerator ewidths = widths.GetEnumerator ()) { bool? hw = null; int width = GetNextWidth (ewidths, int.MaxValue, ref hw); int start = 0, end; do { end = GetLineEnd (start, width, self); char c = self [end-1]; if (char.IsWhiteSpace (c)) --end; bool needContinuation = end != self.Length && !IsEolChar (c); string continuation = ""; if (needContinuation) { --end; continuation = "-"; } string line = self.Substring (start, end - start) + continuation; yield return line; start = end; if (char.IsWhiteSpace (c)) ++start; width = GetNextWidth (ewidths, width, ref hw); } while (start < self.Length); } } private static int GetNextWidth (IEnumerator ewidths, int curWidth, ref bool? eValid) { if (!eValid.HasValue || (eValid.HasValue && eValid.Value)) { curWidth = (eValid = ewidths.MoveNext ()).Value ? ewidths.Current : curWidth; // '.' is any character, - is for a continuation const string minWidth = ".-"; if (curWidth < minWidth.Length) throw new ArgumentOutOfRangeException ("widths", string.Format ("Element must be >= {0}, was {1}.", minWidth.Length, curWidth)); return curWidth; } // no more elements, use the last element. return curWidth; } private static bool IsEolChar (char c) { return !char.IsLetterOrDigit (c); } private static int GetLineEnd (int start, int length, string description) { int end = System.Math.Min (start + length, description.Length); int sep = -1; for (int i = start; i < end; ++i) { if (description [i] == '\n') return i+1; if (IsEolChar (description [i])) sep = i+1; } if (sep == -1 || end == description.Length) return end; return sep; } } public class OptionValueCollection : IList, IList { List values = new List (); OptionContext c; internal OptionValueCollection (OptionContext c) { this.c = c; } #region ICollection void ICollection.CopyTo (Array array, int index) {(values as ICollection).CopyTo (array, index);} bool ICollection.IsSynchronized {get {return (values as ICollection).IsSynchronized;}} object ICollection.SyncRoot {get {return (values as ICollection).SyncRoot;}} #endregion #region ICollection public void Add (string item) {values.Add (item);} public void Clear () {values.Clear ();} public bool Contains (string item) {return values.Contains (item);} public void CopyTo (string[] array, int arrayIndex) {values.CopyTo (array, arrayIndex);} public bool Remove (string item) {return values.Remove (item);} public int Count {get {return values.Count;}} public bool IsReadOnly {get {return false;}} #endregion #region IEnumerable IEnumerator IEnumerable.GetEnumerator () {return values.GetEnumerator ();} #endregion #region IEnumerable public IEnumerator GetEnumerator () {return values.GetEnumerator ();} #endregion #region IList int IList.Add (object value) {return (values as IList).Add (value);} bool IList.Contains (object value) {return (values as IList).Contains (value);} int IList.IndexOf (object value) {return (values as IList).IndexOf (value);} void IList.Insert (int index, object value) {(values as IList).Insert (index, value);} void IList.Remove (object value) {(values as IList).Remove (value);} void IList.RemoveAt (int index) {(values as IList).RemoveAt (index);} bool IList.IsFixedSize {get {return false;}} object IList.this [int index] {get {return this [index];} set {(values as IList)[index] = value;}} #endregion #region IList public int IndexOf (string item) {return values.IndexOf (item);} public void Insert (int index, string item) {values.Insert (index, item);} public void RemoveAt (int index) {values.RemoveAt (index);} private void AssertValid (int index) { if (c.Option == null) throw new InvalidOperationException ("OptionContext.Option is null."); if (index >= c.Option.MaxValueCount) throw new ArgumentOutOfRangeException ("index"); if (c.Option.OptionValueType == OptionValueType.Required && index >= values.Count) throw new OptionException (string.Format ( c.OptionSet.MessageLocalizer ("Missing required value for option '{0}'."), c.OptionName), c.OptionName); } public string this [int index] { get { AssertValid (index); return index >= values.Count ? null : values [index]; } set { values [index] = value; } } #endregion public List ToList () { return new List (values); } public string[] ToArray () { return values.ToArray (); } public override string ToString () { return string.Join (", ", values.ToArray ()); } } public class OptionContext { private Option option; private string name; private int index; private OptionSet set; private OptionValueCollection c; public OptionContext (OptionSet set) { this.set = set; this.c = new OptionValueCollection (this); } public Option Option { get {return option;} set {option = value;} } public string OptionName { get {return name;} set {name = value;} } public int OptionIndex { get {return index;} set {index = value;} } public OptionSet OptionSet { get {return set;} } public OptionValueCollection OptionValues { get {return c;} } } public enum OptionValueType { None, Optional, Required, } public abstract class Option { string prototype, description; string[] names; OptionValueType type; int count; string[] separators; bool hidden; protected Option (string prototype, string description) : this (prototype, description, 1, false) { } protected Option (string prototype, string description, int maxValueCount) : this (prototype, description, maxValueCount, false) { } protected Option (string prototype, string description, int maxValueCount, bool hidden) { if (prototype == null) throw new ArgumentNullException ("prototype"); if (prototype.Length == 0) throw new ArgumentException ("Cannot be the empty string.", "prototype"); if (maxValueCount < 0) throw new ArgumentOutOfRangeException ("maxValueCount"); this.prototype = prototype; this.description = description; this.count = maxValueCount; this.names = (this is OptionSet.Category) // append GetHashCode() so that "duplicate" categories have distinct // names, e.g. adding multiple "" categories should be valid. ? new[]{prototype + this.GetHashCode ()} : prototype.Split ('|'); if (this is OptionSet.Category) return; this.type = ParsePrototype (); this.hidden = hidden; if (this.count == 0 && type != OptionValueType.None) throw new ArgumentException ( "Cannot provide maxValueCount of 0 for OptionValueType.Required or " + "OptionValueType.Optional.", "maxValueCount"); if (this.type == OptionValueType.None && maxValueCount > 1) throw new ArgumentException ( string.Format ("Cannot provide maxValueCount of {0} for OptionValueType.None.", maxValueCount), "maxValueCount"); if (Array.IndexOf (names, "<>") >= 0 && ((names.Length == 1 && this.type != OptionValueType.None) || (names.Length > 1 && this.MaxValueCount > 1))) throw new ArgumentException ( "The default option handler '<>' cannot require values.", "prototype"); } public string Prototype {get {return prototype;}} public string Description {get {return description;}} public OptionValueType OptionValueType {get {return type;}} public int MaxValueCount {get {return count;}} public bool Hidden {get {return hidden;}} public string[] GetNames () { return (string[]) names.Clone (); } public string[] GetValueSeparators () { if (separators == null) return new string [0]; return (string[]) separators.Clone (); } protected static T Parse (string value, OptionContext c) { Type tt = typeof (T); bool nullable = tt.IsValueType && tt.IsGenericType && !tt.IsGenericTypeDefinition && tt.GetGenericTypeDefinition () == typeof (Nullable<>); Type targetType = nullable ? tt.GetGenericArguments () [0] : typeof (T); TypeConverter conv = TypeDescriptor.GetConverter (targetType); T t = default (T); try { if (value != null) t = (T) conv.ConvertFromString (value); } catch (Exception e) { throw new OptionException ( string.Format ( c.OptionSet.MessageLocalizer ("Could not convert string `{0}' to type {1} for option `{2}'."), value, targetType.Name, c.OptionName), c.OptionName, e); } return t; } internal string[] Names {get {return names;}} internal string[] ValueSeparators {get {return separators;}} static readonly char[] NameTerminator = new char[]{'=', ':'}; private OptionValueType ParsePrototype () { char type = '\0'; List seps = new List (); for (int i = 0; i < names.Length; ++i) { string name = names [i]; if (name.Length == 0) throw new ArgumentException ("Empty option names are not supported.", "prototype"); int end = name.IndexOfAny (NameTerminator); if (end == -1) continue; names [i] = name.Substring (0, end); if (type == '\0' || type == name [end]) type = name [end]; else throw new ArgumentException ( string.Format ("Conflicting option types: '{0}' vs. '{1}'.", type, name [end]), "prototype"); AddSeparators (name, end, seps); } if (type == '\0') return OptionValueType.None; if (count <= 1 && seps.Count != 0) throw new ArgumentException ( string.Format ("Cannot provide key/value separators for Options taking {0} value(s).", count), "prototype"); if (count > 1) { if (seps.Count == 0) this.separators = new string[]{":", "="}; else if (seps.Count == 1 && seps [0].Length == 0) this.separators = null; else this.separators = seps.ToArray (); } return type == '=' ? OptionValueType.Required : OptionValueType.Optional; } private static void AddSeparators (string name, int end, ICollection seps) { int start = -1; for (int i = end+1; i < name.Length; ++i) { switch (name [i]) { case '{': if (start != -1) throw new ArgumentException ( string.Format ("Ill-formed name/value separator found in \"{0}\".", name), "prototype"); start = i+1; break; case '}': if (start == -1) throw new ArgumentException ( string.Format ("Ill-formed name/value separator found in \"{0}\".", name), "prototype"); seps.Add (name.Substring (start, i-start)); start = -1; break; default: if (start == -1) seps.Add (name [i].ToString ()); break; } } if (start != -1) throw new ArgumentException ( string.Format ("Ill-formed name/value separator found in \"{0}\".", name), "prototype"); } public void Invoke (OptionContext c) { OnParseComplete (c); c.OptionName = null; c.Option = null; c.OptionValues.Clear (); } protected abstract void OnParseComplete (OptionContext c); public override string ToString () { return Prototype; } } public abstract class ArgumentSource { protected ArgumentSource () { } public abstract string[] GetNames (); public abstract string Description { get; } public abstract bool GetArguments (string value, out IEnumerable replacement); public static IEnumerable GetArgumentsFromFile (string file) { return GetArguments (File.OpenText (file), true); } public static IEnumerable GetArguments (TextReader reader) { return GetArguments (reader, false); } // Cribbed from mcs/driver.cs:LoadArgs(string) static IEnumerable GetArguments (TextReader reader, bool close) { try { StringBuilder arg = new StringBuilder (); string line; while ((line = reader.ReadLine ()) != null) { int t = line.Length; for (int i = 0; i < t; i++) { char c = line [i]; if (c == '"' || c == '\'') { char end = c; for (i++; i < t; i++){ c = line [i]; if (c == end) break; arg.Append (c); } } else if (c == ' ') { if (arg.Length > 0) { yield return arg.ToString (); arg.Length = 0; } } else arg.Append (c); } if (arg.Length > 0) { yield return arg.ToString (); arg.Length = 0; } } } finally { if (close) reader.Close (); } } } public class ResponseFileSource : ArgumentSource { public override string[] GetNames () { return new string[]{"@file"}; } public override string Description { get {return "Read response file for more options.";} } public override bool GetArguments (string value, out IEnumerable replacement) { if (string.IsNullOrEmpty (value) || !value.StartsWith ("@")) { replacement = null; return false; } replacement = ArgumentSource.GetArgumentsFromFile (value.Substring (1)); return true; } } [Serializable] public class OptionException : Exception { private string option; public OptionException () { } public OptionException (string message, string optionName) : base (message) { this.option = optionName; } public OptionException (string message, string optionName, Exception innerException) : base (message, innerException) { this.option = optionName; } protected OptionException (SerializationInfo info, StreamingContext context) : base (info, context) { this.option = info.GetString ("OptionName"); } public string OptionName { get {return this.option;} } [SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)] public override void GetObjectData (SerializationInfo info, StreamingContext context) { base.GetObjectData (info, context); info.AddValue ("OptionName", option); } } public delegate void OptionAction (TKey key, TValue value); public class OptionSet : KeyedCollection { public OptionSet () : this (delegate (string f) {return f;}) { } public OptionSet (Converter localizer) { this.localizer = localizer; this.roSources = new ReadOnlyCollection(sources); } Converter localizer; public Converter MessageLocalizer { get {return localizer;} } List sources = new List (); ReadOnlyCollection roSources; public ReadOnlyCollection ArgumentSources { get {return roSources;} } protected override string GetKeyForItem (Option item) { if (item == null) throw new ArgumentNullException ("option"); if (item.Names != null && item.Names.Length > 0) return item.Names [0]; // This should never happen, as it's invalid for Option to be // constructed w/o any names. throw new InvalidOperationException ("Option has no names!"); } [Obsolete ("Use KeyedCollection.this[string]")] protected Option GetOptionForName (string option) { if (option == null) throw new ArgumentNullException ("option"); try { return base [option]; } catch (KeyNotFoundException) { return null; } } protected override void InsertItem (int index, Option item) { base.InsertItem (index, item); AddImpl (item); } protected override void RemoveItem (int index) { Option p = Items [index]; base.RemoveItem (index); // KeyedCollection.RemoveItem() handles the 0th item for (int i = 1; i < p.Names.Length; ++i) { Dictionary.Remove (p.Names [i]); } } protected override void SetItem (int index, Option item) { base.SetItem (index, item); AddImpl (item); } private void AddImpl (Option option) { if (option == null) throw new ArgumentNullException ("option"); List added = new List (option.Names.Length); try { // KeyedCollection.InsertItem/SetItem handle the 0th name. for (int i = 1; i < option.Names.Length; ++i) { Dictionary.Add (option.Names [i], option); added.Add (option.Names [i]); } } catch (Exception) { foreach (string name in added) Dictionary.Remove (name); throw; } } public OptionSet Add (string header) { if (header == null) throw new ArgumentNullException ("header"); Add (new Category (header)); return this; } internal sealed class Category : Option { // Prototype starts with '=' because this is an invalid prototype // (see Option.ParsePrototype(), and thus it'll prevent Category // instances from being accidentally used as normal options. public Category (string description) : base ("=:Category:= " + description, description) { } protected override void OnParseComplete (OptionContext c) { throw new NotSupportedException ("Category.OnParseComplete should not be invoked."); } } public new OptionSet Add (Option option) { base.Add (option); return this; } sealed class ActionOption : Option { Action action; public ActionOption (string prototype, string description, int count, Action action) : this (prototype, description, count, action, false) { } public ActionOption (string prototype, string description, int count, Action action, bool hidden) : base (prototype, description, count, hidden) { if (action == null) throw new ArgumentNullException ("action"); this.action = action; } protected override void OnParseComplete (OptionContext c) { action (c.OptionValues); } } public OptionSet Add (string prototype, Action action) { return Add (prototype, null, action); } public OptionSet Add (string prototype, string description, Action action) { return Add (prototype, description, action, false); } public OptionSet Add (string prototype, string description, Action action, bool hidden) { if (action == null) throw new ArgumentNullException ("action"); Option p = new ActionOption (prototype, description, 1, delegate (OptionValueCollection v) { action (v [0]); }, hidden); base.Add (p); return this; } public OptionSet Add (string prototype, OptionAction action) { return Add (prototype, null, action); } public OptionSet Add (string prototype, string description, OptionAction action) { return Add (prototype, description, action, false); } public OptionSet Add (string prototype, string description, OptionAction action, bool hidden) { if (action == null) throw new ArgumentNullException ("action"); Option p = new ActionOption (prototype, description, 2, delegate (OptionValueCollection v) {action (v [0], v [1]);}, hidden); base.Add (p); return this; } sealed class ActionOption : Option { Action action; public ActionOption (string prototype, string description, Action action) : base (prototype, description, 1) { if (action == null) throw new ArgumentNullException ("action"); this.action = action; } protected override void OnParseComplete (OptionContext c) { action (Parse (c.OptionValues [0], c)); } } sealed class ActionOption : Option { OptionAction action; public ActionOption (string prototype, string description, OptionAction action) : base (prototype, description, 2) { if (action == null) throw new ArgumentNullException ("action"); this.action = action; } protected override void OnParseComplete (OptionContext c) { action ( Parse (c.OptionValues [0], c), Parse (c.OptionValues [1], c)); } } public OptionSet Add (string prototype, Action action) { return Add (prototype, null, action); } public OptionSet Add (string prototype, string description, Action action) { return Add (new ActionOption (prototype, description, action)); } public OptionSet Add (string prototype, OptionAction action) { return Add (prototype, null, action); } public OptionSet Add (string prototype, string description, OptionAction action) { return Add (new ActionOption (prototype, description, action)); } public OptionSet Add (ArgumentSource source) { if (source == null) throw new ArgumentNullException ("source"); sources.Add (source); return this; } protected virtual OptionContext CreateOptionContext () { return new OptionContext (this); } public List Parse (IEnumerable arguments) { if (arguments == null) throw new ArgumentNullException ("arguments"); OptionContext c = CreateOptionContext (); c.OptionIndex = -1; bool process = true; List unprocessed = new List (); Option def = Contains ("<>") ? this ["<>"] : null; ArgumentEnumerator ae = new ArgumentEnumerator (arguments); foreach (string argument in ae) { ++c.OptionIndex; if (argument == "--") { process = false; continue; } if (!process) { Unprocessed (unprocessed, def, c, argument); continue; } if (AddSource (ae, argument)) continue; if (!Parse (argument, c)) Unprocessed (unprocessed, def, c, argument); } if (c.Option != null) c.Option.Invoke (c); return unprocessed; } class ArgumentEnumerator : IEnumerable { List> sources = new List> (); public ArgumentEnumerator (IEnumerable arguments) { sources.Add (arguments.GetEnumerator ()); } public void Add (IEnumerable arguments) { sources.Add (arguments.GetEnumerator ()); } public IEnumerator GetEnumerator () { do { IEnumerator c = sources [sources.Count-1]; if (c.MoveNext ()) yield return c.Current; else { c.Dispose (); sources.RemoveAt (sources.Count-1); } } while (sources.Count > 0); } IEnumerator IEnumerable.GetEnumerator () { return GetEnumerator (); } } bool AddSource (ArgumentEnumerator ae, string argument) { foreach (ArgumentSource source in sources) { IEnumerable replacement; if (!source.GetArguments (argument, out replacement)) continue; ae.Add (replacement); return true; } return false; } private static bool Unprocessed (ICollection extra, Option def, OptionContext c, string argument) { if (def == null) { extra.Add (argument); return false; } c.OptionValues.Add (argument); c.Option = def; c.Option.Invoke (c); return false; } private readonly Regex ValueOption = new Regex ( @"^(?--|-|/)(?[^:=]+)((?[:=])(?.*))?$"); protected bool GetOptionParts (string argument, out string flag, out string name, out string sep, out string value) { if (argument == null) throw new ArgumentNullException ("argument"); flag = name = sep = value = null; Match m = ValueOption.Match (argument); if (!m.Success) { return false; } flag = m.Groups ["flag"].Value; name = m.Groups ["name"].Value; if (m.Groups ["sep"].Success && m.Groups ["value"].Success) { sep = m.Groups ["sep"].Value; value = m.Groups ["value"].Value; } return true; } protected virtual bool Parse (string argument, OptionContext c) { if (c.Option != null) { ParseValue (argument, c); return true; } string f, n, s, v; if (!GetOptionParts (argument, out f, out n, out s, out v)) return false; Option p; if (Contains (n)) { p = this [n]; c.OptionName = f + n; c.Option = p; switch (p.OptionValueType) { case OptionValueType.None: c.OptionValues.Add (n); c.Option.Invoke (c); break; case OptionValueType.Optional: case OptionValueType.Required: ParseValue (v, c); break; } return true; } // no match; is it a bool option? if (ParseBool (argument, n, c)) return true; // is it a bundled option? if (ParseBundledValue (f, string.Concat (n + s + v), c)) return true; return false; } private void ParseValue (string option, OptionContext c) { if (option != null) foreach (string o in c.Option.ValueSeparators != null ? option.Split (c.Option.ValueSeparators, c.Option.MaxValueCount - c.OptionValues.Count, StringSplitOptions.None) : new string[]{option}) { c.OptionValues.Add (o); } if (c.OptionValues.Count == c.Option.MaxValueCount || c.Option.OptionValueType == OptionValueType.Optional) c.Option.Invoke (c); else if (c.OptionValues.Count > c.Option.MaxValueCount) { throw new OptionException (localizer (string.Format ( "Error: Found {0} option values when expecting {1}.", c.OptionValues.Count, c.Option.MaxValueCount)), c.OptionName); } } private bool ParseBool (string option, string n, OptionContext c) { Option p; string rn; if (n.Length >= 1 && (n [n.Length-1] == '+' || n [n.Length-1] == '-') && Contains ((rn = n.Substring (0, n.Length-1)))) { p = this [rn]; string v = n [n.Length-1] == '+' ? option : null; c.OptionName = option; c.Option = p; c.OptionValues.Add (v); p.Invoke (c); return true; } return false; } private bool ParseBundledValue (string f, string n, OptionContext c) { if (f != "-") return false; for (int i = 0; i < n.Length; ++i) { Option p; string opt = f + n [i].ToString (); string rn = n [i].ToString (); if (!Contains (rn)) { if (i == 0) return false; throw new OptionException (string.Format (localizer ( "Cannot bundle unregistered option '{0}'."), opt), opt); } p = this [rn]; switch (p.OptionValueType) { case OptionValueType.None: Invoke (c, opt, n, p); break; case OptionValueType.Optional: case OptionValueType.Required: { string v = n.Substring (i+1); c.Option = p; c.OptionName = opt; ParseValue (v.Length != 0 ? v : null, c); return true; } default: throw new InvalidOperationException ("Unknown OptionValueType: " + p.OptionValueType); } } return true; } private static void Invoke (OptionContext c, string name, string value, Option option) { c.OptionName = name; c.Option = option; c.OptionValues.Add (value); option.Invoke (c); } private const int OptionWidth = 29; private const int Description_FirstWidth = 80 - OptionWidth; private const int Description_RemWidth = 80 - OptionWidth - 2; public void WriteOptionDescriptions (TextWriter o) { foreach (Option p in this) { int written = 0; if (p.Hidden) continue; Category c = p as Category; if (c != null) { WriteDescription (o, p.Description, "", 80, 80); continue; } if (!WriteOptionPrototype (o, p, ref written)) continue; if (written < OptionWidth) o.Write (new string (' ', OptionWidth - written)); else { o.WriteLine (); o.Write (new string (' ', OptionWidth)); } WriteDescription (o, p.Description, new string (' ', OptionWidth+2), Description_FirstWidth, Description_RemWidth); } foreach (ArgumentSource s in sources) { string[] names = s.GetNames (); if (names == null || names.Length == 0) continue; int written = 0; Write (o, ref written, " "); Write (o, ref written, names [0]); for (int i = 1; i < names.Length; ++i) { Write (o, ref written, ", "); Write (o, ref written, names [i]); } if (written < OptionWidth) o.Write (new string (' ', OptionWidth - written)); else { o.WriteLine (); o.Write (new string (' ', OptionWidth)); } WriteDescription (o, s.Description, new string (' ', OptionWidth+2), Description_FirstWidth, Description_RemWidth); } } void WriteDescription (TextWriter o, string value, string prefix, int firstWidth, int remWidth) { bool indent = false; foreach (string line in GetLines (localizer (GetDescription (value)), firstWidth, remWidth)) { if (indent) o.Write (prefix); o.WriteLine (line); indent = true; } } bool WriteOptionPrototype (TextWriter o, Option p, ref int written) { string[] names = p.Names; int i = GetNextOptionIndex (names, 0); if (i == names.Length) return false; if (names [i].Length == 1) { Write (o, ref written, " -"); Write (o, ref written, names [0]); } else { Write (o, ref written, " --"); Write (o, ref written, names [0]); } for ( i = GetNextOptionIndex (names, i+1); i < names.Length; i = GetNextOptionIndex (names, i+1)) { Write (o, ref written, ", "); Write (o, ref written, names [i].Length == 1 ? "-" : "--"); Write (o, ref written, names [i]); } if (p.OptionValueType == OptionValueType.Optional || p.OptionValueType == OptionValueType.Required) { if (p.OptionValueType == OptionValueType.Optional) { Write (o, ref written, localizer ("[")); } Write (o, ref written, localizer ("=" + GetArgumentName (0, p.MaxValueCount, p.Description))); string sep = p.ValueSeparators != null && p.ValueSeparators.Length > 0 ? p.ValueSeparators [0] : " "; for (int c = 1; c < p.MaxValueCount; ++c) { Write (o, ref written, localizer (sep + GetArgumentName (c, p.MaxValueCount, p.Description))); } if (p.OptionValueType == OptionValueType.Optional) { Write (o, ref written, localizer ("]")); } } return true; } static int GetNextOptionIndex (string[] names, int i) { while (i < names.Length && names [i] == "<>") { ++i; } return i; } static void Write (TextWriter o, ref int n, string s) { n += s.Length; o.Write (s); } private static string GetArgumentName (int index, int maxIndex, string description) { if (description == null) return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1); string[] nameStart; if (maxIndex == 1) nameStart = new string[]{"{0:", "{"}; else nameStart = new string[]{"{" + index + ":"}; for (int i = 0; i < nameStart.Length; ++i) { int start, j = 0; do { start = description.IndexOf (nameStart [i], j); } while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false); if (start == -1) continue; int end = description.IndexOf ("}", start); if (end == -1) continue; return description.Substring (start + nameStart [i].Length, end - start - nameStart [i].Length); } return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1); } private static string GetDescription (string description) { if (description == null) return string.Empty; StringBuilder sb = new StringBuilder (description.Length); int start = -1; for (int i = 0; i < description.Length; ++i) { switch (description [i]) { case '{': if (i == start) { sb.Append ('{'); start = -1; } else if (start < 0) start = i + 1; break; case '}': if (start < 0) { if ((i+1) == description.Length || description [i+1] != '}') throw new InvalidOperationException ("Invalid option description: " + description); ++i; sb.Append ("}"); } else { sb.Append (description.Substring (start, i - start)); start = -1; } break; case ':': if (start < 0) goto default; start = i + 1; break; default: if (start < 0) sb.Append (description [i]); break; } } return sb.ToString (); } private static IEnumerable GetLines (string description, int firstWidth, int remWidth) { return StringCoda.WrappedLines (description, firstWidth, remWidth); } } } ================================================ FILE: src/SyncReleases/Program.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Reflection; using System.Text; using System.Threading.Tasks; using Mono.Options; using Octokit; using Squirrel.SimpleSplat; using Squirrel; using Squirrel.Json; namespace SyncReleases { class Program : IEnableLogger { static OptionSet opts; public static int Main(string[] args) { var pg = new Program(); try { return pg.main(args).Result; } catch (Exception ex) { // NB: Normally this is a terrible idea but we want to make // sure Setup.exe above us gets the nonzero error code Console.Error.WriteLine(ex); return -1; } } async Task main(string[] args) { using (var logger = new SetupLogLogger(false) { Level = Squirrel.SimpleSplat.LogLevel.Info }) { Squirrel.SimpleSplat.SquirrelLocator.CurrentMutable.Register(() => logger, typeof(Squirrel.SimpleSplat.ILogger)); var releaseDir = default(string); var repoUrl = default(string); var token = default(string); opts = new OptionSet() { "Usage: SyncReleases.exe command [OPTS]", "Builds a Releases directory from releases on GitHub", "", "Options:", { "h|?|help", "Display Help and exit", _ => {} }, { "r=|releaseDir=", "Path to a release directory to download to", v => releaseDir = v}, { "u=|url=", "When pointing to GitHub, use the URL to the repository root page, else point to an existing remote Releases folder", v => repoUrl = v}, { "t=|token=", "The OAuth token to use as login credentials", v => token = v}, }; opts.Parse(args); if (repoUrl == null || repoUrl.StartsWith("http", true, CultureInfo.InvariantCulture) == false) { ShowHelp(); return -1; } var releaseDirectoryInfo = new DirectoryInfo(releaseDir ?? Path.Combine(".", "Releases")); if (!releaseDirectoryInfo.Exists) releaseDirectoryInfo.Create(); var githubException = default(Exception); try { await SyncImplementations.SyncFromGitHub(repoUrl, token, releaseDirectoryInfo); return 0; } catch (Exception ex) { githubException = ex; Console.Error.WriteLine("Attempting to sync URL as remote RELEASES folder"); } try { await SyncImplementations.SyncRemoteReleases(new Uri(repoUrl), releaseDirectoryInfo); } catch (Exception) { Console.Error.WriteLine("Failed to sync URL as GitHub repo: " + githubException.Message); throw; } } return 0; } public void ShowHelp() { opts.WriteOptionDescriptions(Console.Out); } } class SetupLogLogger : Squirrel.SimpleSplat.ILogger, IDisposable { StreamWriter inner; readonly object gate = 42; public Squirrel.SimpleSplat.LogLevel Level { get; set; } public SetupLogLogger(bool saveInTemp) { var dir = saveInTemp ? Path.GetTempPath() : Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); var file = Path.Combine(dir, "SquirrelSetup.log"); if (File.Exists(file)) File.Delete(file); inner = new StreamWriter(file, false, Encoding.UTF8); } public void Write(string message, LogLevel logLevel) { if (logLevel < Level) { return; } lock (gate) inner.WriteLine(message); } public void Dispose() { lock(gate) inner.Dispose(); } } } ================================================ FILE: src/SyncReleases/SyncImplementations.cs ================================================ using System; using System.IO; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Squirrel; using Octokit; using System.Reflection; using System.Net; namespace SyncReleases { internal class SyncImplementations { static SyncImplementations() { System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; } public static async Task SyncRemoteReleases(Uri targetUri, DirectoryInfo releasesDir) { var releasesUri = Utility.AppendPathToUri(targetUri, "RELEASES"); var releasesIndex = await retryAsync(3, () => downloadReleasesIndex(releasesUri)); File.WriteAllText(Path.Combine(releasesDir.FullName, "RELEASES"), releasesIndex); var releasesToDownload = ReleaseEntry.ParseReleaseFile(releasesIndex) .Where(x => !x.IsDelta) .OrderByDescending(x => x.Version) .Take(1) .Select(x => new { LocalPath = Path.Combine(releasesDir.FullName, x.Filename), RemoteUrl = new Uri(Utility.EnsureTrailingSlash(targetUri), x.BaseUrl + x.Filename + x.Query) }); foreach (var releaseToDownload in releasesToDownload) { await retryAsync(3, () => downloadRelease(releaseToDownload.LocalPath, releaseToDownload.RemoteUrl)); } } public static async Task SyncFromGitHub(string repoUrl, string token, DirectoryInfo releaseDirectoryInfo) { var repoUri = new Uri(repoUrl); var userAgent = new ProductHeaderValue("SyncReleases", Assembly.GetExecutingAssembly().GetName().Version.ToString()); var client = new GitHubClient(userAgent, repoUri); if (token != null) { client.Credentials = new Credentials(token); } var nwo = nwoFromRepoUrl(repoUrl); var releases = (await client.Release.GetAll(nwo.Item1, nwo.Item2)) .OrderByDescending(x => x.PublishedAt) .Take(5); await releases.ForEachAsync(async release => { // NB: Why do I have to double-fetch the release assets? It's already in GetAll var assets = await client.Release.GetAllAssets(nwo.Item1, nwo.Item2, release.Id); await assets .Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)) .Where(x => { var fi = new FileInfo(Path.Combine(releaseDirectoryInfo.FullName, x.Name)); return !(fi.Exists && fi.Length == x.Size); }) .ForEachAsync(async x => { var target = new FileInfo(Path.Combine(releaseDirectoryInfo.FullName, x.Name)); if (target.Exists) target.Delete(); await retryAsync(3, async () => { var hc = new HttpClient(); var rq = new HttpRequestMessage(HttpMethod.Get, x.Url); rq.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/octet-stream")); rq.Headers.UserAgent.Add(new System.Net.Http.Headers.ProductInfoHeaderValue(userAgent.Name, userAgent.Version)); if (token != null) { rq.Headers.Add("Authorization", "Bearer " + token); } var resp = await hc.SendAsync(rq); resp.EnsureSuccessStatusCode(); using (var from = await resp.Content.ReadAsStreamAsync()) using (var to = File.OpenWrite(target.FullName)) { await from.CopyToAsync(to); } }); }); }); var entries = releaseDirectoryInfo.GetFiles("*.nupkg") .AsParallel() .Select(x => ReleaseEntry.GenerateFromFile(x.FullName)); ReleaseEntry.WriteReleaseFile(entries, Path.Combine(releaseDirectoryInfo.FullName, "RELEASES")); } static async Task downloadReleasesIndex(Uri uri) { Console.WriteLine("Trying to download RELEASES index from {0}", uri); var userAgent = new System.Net.Http.Headers.ProductInfoHeaderValue("Squirrel", Assembly.GetExecutingAssembly().GetName().Version.ToString()); using (HttpClient client = new HttpClient()) { client.DefaultRequestHeaders.UserAgent.Add(userAgent); return await client.GetStringAsync(uri); } } static async Task downloadRelease(string localPath, Uri remoteUrl) { if (File.Exists(localPath)) { File.Delete(localPath); } Console.WriteLine("Downloading release from {0}", remoteUrl); var wc = new NotBrokenWebClient(); await wc.DownloadFileTaskAsync(remoteUrl, localPath); } static Tuple nwoFromRepoUrl(string repoUrl) { var uri = new Uri(repoUrl); var segments = uri.AbsolutePath.Split('/'); if (segments.Count() != 3) { throw new Exception("Repo URL must be to the root URL of the repo e.g. https://github.com/myuser/myrepo"); } return Tuple.Create(segments[1], segments[2]); } static async Task retryAsync(int count, Func> block) { int retryCount = count; retry: try { return await block(); } catch (Exception) { retryCount--; if (retryCount >= 0) goto retry; throw; } } static async Task retryAsync(int count, Func block) { await retryAsync(count, async () => { await block(); return false; }); } } class NotBrokenWebClient : WebClient { protected override WebRequest GetWebRequest(Uri address) { var wr = base.GetWebRequest(address); var hwr = wr as HttpWebRequest; if (hwr == null) return wr; hwr.AllowAutoRedirect = true; hwr.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; return hwr; } } } ================================================ FILE: src/SyncReleases/SyncReleases.csproj ================================================  net45 Exe SyncReleases SyncReleases cd "$(TargetDir)" "$(NuGetPackageRoot)ilrepack\1.26.0\tools\ILRepack.exe" /internalize /out:$(TargetFileName).tmp $(TargetFileName) SharpCompress.dll Microsoft.Web.XmlTransform.dll Squirrel.dll Octokit.dll NuGet.Squirrel.dll del "$(TargetFileName)" ren "$(TargetFileName).tmp" "$(TargetFileName)" ================================================ FILE: src/Update/AnimatedGifWindow.cs ================================================ #if !MONO using System; using System.IO; using System.Reflection; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shell; using WpfAnimatedGif; namespace Squirrel.Update { public class AnimatedGifWindow : Window { public AnimatedGifWindow() { var img = new Image(); var src = default(BitmapImage); var executionLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var source = Path.Combine( executionLocation, "background.gif"); if (File.Exists(source)) { src = new BitmapImage(); src.BeginInit(); src.StreamSource = File.OpenRead(source); src.EndInit(); ImageBehavior.SetAnimatedSource(img, src); this.Content = img; this.Width = src.Width; this.Height = src.Height; } var setupIcon = Path.Combine(executionLocation, "setupIcon.ico"); if (File.Exists(setupIcon)) { Icon = BitmapFrame.Create(new Uri(setupIcon, UriKind.Relative)); } this.AllowsTransparency = true; this.WindowStyle = WindowStyle.None; this.WindowStartupLocation = WindowStartupLocation.CenterScreen; this.ShowInTaskbar = true; this.Topmost = true; this.TaskbarItemInfo = new TaskbarItemInfo { ProgressState = TaskbarItemProgressState.Normal }; this.Title = "Installing..."; this.Background = new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)); } public static void ShowWindow(TimeSpan initialDelay, CancellationToken token, ProgressSource progressSource) { var wnd = default(AnimatedGifWindow); var thread = new Thread(() => { if (token.IsCancellationRequested) return; try { Task.Delay(initialDelay, token).ContinueWith(t => { return true; }).Wait(); } catch (Exception) { return; } wnd = new AnimatedGifWindow(); wnd.Show(); Task.Delay(TimeSpan.FromSeconds(5.0), token).ContinueWith(t => { if (t.IsCanceled) return; wnd.Dispatcher.BeginInvoke(new Action(() => wnd.Topmost = false)); }); token.Register(() => wnd.Dispatcher.BeginInvoke(new Action(wnd.Close))); EventHandler progressSourceOnProgress = ((sender, p) => wnd.Dispatcher.BeginInvoke( new Action(() => wnd.TaskbarItemInfo.ProgressValue = p/100.0))); progressSource.Progress += progressSourceOnProgress; try { (new Application()).Run(wnd); } finally { progressSource.Progress -= progressSourceOnProgress; } }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); } protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); this.DragMove(); } } } #endif ================================================ FILE: src/Update/App.config ================================================  ================================================ FILE: src/Update/AuthenticodeTools.cs ================================================ using System; using System.Runtime.InteropServices; namespace Squirrel.Update { internal static class AuthenticodeTools { [DllImport("Wintrust.dll", PreserveSig = true, SetLastError = false)] static extern uint WinVerifyTrust(IntPtr hWnd, IntPtr pgActionID, IntPtr pWinTrustData); static uint winVerifyTrust(string fileName) { Guid wintrust_action_generic_verify_v2 = new Guid("{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}"); uint result = 0; using (WINTRUST_FILE_INFO fileInfo = new WINTRUST_FILE_INFO(fileName, Guid.Empty)) using (UnmanagedPointer guidPtr = new UnmanagedPointer(Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid))), AllocMethod.HGlobal)) using (UnmanagedPointer wvtDataPtr = new UnmanagedPointer(Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WINTRUST_DATA))), AllocMethod.HGlobal)) { WINTRUST_DATA data = new WINTRUST_DATA(fileInfo); IntPtr pGuid = guidPtr; IntPtr pData = wvtDataPtr; Marshal.StructureToPtr(wintrust_action_generic_verify_v2, pGuid, true); Marshal.StructureToPtr(data, pData, true); result = WinVerifyTrust(IntPtr.Zero, pGuid, pData); } return result; } public static bool IsTrusted(string fileName) { return winVerifyTrust(fileName) == 0; } } internal struct WINTRUST_FILE_INFO : IDisposable { public WINTRUST_FILE_INFO(string fileName, Guid subject) { cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_FILE_INFO)); pcwszFilePath = fileName; if (subject != Guid.Empty) { pgKnownSubject = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid))); Marshal.StructureToPtr(subject, pgKnownSubject, true); } else { pgKnownSubject = IntPtr.Zero; } hFile = IntPtr.Zero; } public uint cbStruct; [MarshalAs(UnmanagedType.LPTStr)] public string pcwszFilePath; public IntPtr hFile; public IntPtr pgKnownSubject; public void Dispose() { Dispose(true); } void Dispose(bool disposing) { if (pgKnownSubject != IntPtr.Zero) { Marshal.DestroyStructure(this.pgKnownSubject, typeof(Guid)); Marshal.FreeHGlobal(this.pgKnownSubject); } } } enum AllocMethod { HGlobal, CoTaskMem }; enum UnionChoice { File = 1, Catalog, Blob, Signer, Cert }; enum UiChoice { All = 1, NoUI, NoBad, NoGood }; enum RevocationCheckFlags { None = 0, WholeChain }; enum StateAction { Ignore = 0, Verify, Close, AutoCache, AutoCacheFlush }; enum TrustProviderFlags { UseIE4Trust = 1, NoIE4Chain = 2, NoPolicyUsage = 4, RevocationCheckNone = 16, RevocationCheckEndCert = 32, RevocationCheckChain = 64, RecovationCheckChainExcludeRoot = 128, Safer = 256, HashOnly = 512, UseDefaultOSVerCheck = 1024, LifetimeSigning = 2048 }; enum UIContext { Execute = 0, Install }; [StructLayout(LayoutKind.Sequential)] internal struct WINTRUST_DATA : IDisposable { public WINTRUST_DATA(WINTRUST_FILE_INFO fileInfo) { this.cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_DATA)); pInfoStruct = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WINTRUST_FILE_INFO))); Marshal.StructureToPtr(fileInfo, pInfoStruct, false); this.dwUnionChoice = UnionChoice.File; pPolicyCallbackData = IntPtr.Zero; pSIPCallbackData = IntPtr.Zero; dwUIChoice = UiChoice.NoUI; fdwRevocationChecks = RevocationCheckFlags.None; dwStateAction = StateAction.Ignore; hWVTStateData = IntPtr.Zero; pwszURLReference = IntPtr.Zero; dwProvFlags = TrustProviderFlags.Safer; dwUIContext = UIContext.Execute; } public uint cbStruct; public IntPtr pPolicyCallbackData; public IntPtr pSIPCallbackData; public UiChoice dwUIChoice; public RevocationCheckFlags fdwRevocationChecks; public UnionChoice dwUnionChoice; public IntPtr pInfoStruct; public StateAction dwStateAction; public IntPtr hWVTStateData; public TrustProviderFlags dwProvFlags; public UIContext dwUIContext; IntPtr pwszURLReference; public void Dispose() { Dispose(true); } void Dispose(bool disposing) { if (dwUnionChoice == UnionChoice.File) { WINTRUST_FILE_INFO info = new WINTRUST_FILE_INFO(); Marshal.PtrToStructure(pInfoStruct, info); info.Dispose(); Marshal.DestroyStructure(pInfoStruct, typeof(WINTRUST_FILE_INFO)); } Marshal.FreeHGlobal(pInfoStruct); } } internal sealed class UnmanagedPointer : IDisposable { IntPtr m_ptr; AllocMethod m_meth; internal UnmanagedPointer(IntPtr ptr, AllocMethod method) { m_meth = method; m_ptr = ptr; } ~UnmanagedPointer() { Dispose(false); } void Dispose(bool disposing) { if (m_ptr != IntPtr.Zero) { if (m_meth == AllocMethod.HGlobal) { Marshal.FreeHGlobal(m_ptr); } else if (m_meth == AllocMethod.CoTaskMem) { Marshal.FreeCoTaskMem(m_ptr); } m_ptr = IntPtr.Zero; } if (disposing) { GC.SuppressFinalize(this); } } public void Dispose() { Dispose(true); } public static implicit operator IntPtr(UnmanagedPointer ptr) { return ptr.m_ptr; } } } ================================================ FILE: src/Update/CopStache.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Security; using System.Text; using System.Threading.Tasks; namespace Squirrel.Update { public static class CopStache { public static string Render(string template, Dictionary identifiers) { var buf = new StringBuilder(); foreach (var line in template.Split('\n')) { identifiers["RandomGuid"] = (Guid.NewGuid()).ToString(); foreach (var key in identifiers.Keys) { buf.Replace("{{" + key + "}}", SecurityElement.Escape(identifiers[key])); } buf.AppendLine(line); } return buf.ToString(); } } } ================================================ FILE: src/Update/Mono.Options/Options.cs ================================================ // // Options.cs // // Authors: // Jonathan Pryor // Federico Di Gregorio // Rolf Bjarne Kvinge // // Copyright (C) 2008 Novell (http://www.novell.com) // Copyright (C) 2009 Federico Di Gregorio. // Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // Compile With: // gmcs -debug+ -r:System.Core Options.cs -o:NDesk.Options.dll // gmcs -debug+ -d:LINQ -r:System.Core Options.cs -o:NDesk.Options.dll // // The LINQ version just changes the implementation of // OptionSet.Parse(IEnumerable), and confers no semantic changes. // // A Getopt::Long-inspired option parsing library for C#. // // NDesk.Options.OptionSet is built upon a key/value table, where the // key is a option format string and the value is a delegate that is // invoked when the format string is matched. // // Option format strings: // Regex-like BNF Grammar: // name: .+ // type: [=:] // sep: ( [^{}]+ | '{' .+ '}' )? // aliases: ( name type sep ) ( '|' name type sep )* // // Each '|'-delimited name is an alias for the associated action. If the // format string ends in a '=', it has a required value. If the format // string ends in a ':', it has an optional value. If neither '=' or ':' // is present, no value is supported. `=' or `:' need only be defined on one // alias, but if they are provided on more than one they must be consistent. // // Each alias portion may also end with a "key/value separator", which is used // to split option values if the option accepts > 1 value. If not specified, // it defaults to '=' and ':'. If specified, it can be any character except // '{' and '}' OR the *string* between '{' and '}'. If no separator should be // used (i.e. the separate values should be distinct arguments), then "{}" // should be used as the separator. // // Options are extracted either from the current option by looking for // the option name followed by an '=' or ':', or is taken from the // following option IFF: // - The current option does not contain a '=' or a ':' // - The current option requires a value (i.e. not a Option type of ':') // // The `name' used in the option format string does NOT include any leading // option indicator, such as '-', '--', or '/'. All three of these are // permitted/required on any named option. // // Option bundling is permitted so long as: // - '-' is used to start the option group // - all of the bundled options are a single character // - at most one of the bundled options accepts a value, and the value // provided starts from the next character to the end of the string. // // This allows specifying '-a -b -c' as '-abc', and specifying '-D name=value' // as '-Dname=value'. // // Option processing is disabled by specifying "--". All options after "--" // are returned by OptionSet.Parse() unchanged and unprocessed. // // Unprocessed options are returned from OptionSet.Parse(). // // Examples: // int verbose = 0; // OptionSet p = new OptionSet () // .Add ("v", v => ++verbose) // .Add ("name=|value=", v => Console.WriteLine (v)); // p.Parse (new string[]{"-v", "--v", "/v", "-name=A", "/name", "B", "extra"}); // // The above would parse the argument string array, and would invoke the // lambda expression three times, setting `verbose' to 3 when complete. // It would also print out "A" and "B" to standard output. // The returned array would contain the string "extra". // // C# 3.0 collection initializers are supported and encouraged: // var p = new OptionSet () { // { "h|?|help", v => ShowHelp () }, // }; // // System.ComponentModel.TypeConverter is also supported, allowing the use of // custom data types in the callback type; TypeConverter.ConvertFromString() // is used to convert the value option to an instance of the specified // type: // // var p = new OptionSet () { // { "foo=", (Foo f) => Console.WriteLine (f.ToString ()) }, // }; // // Random other tidbits: // - Boolean options (those w/o '=' or ':' in the option format string) // are explicitly enabled if they are followed with '+', and explicitly // disabled if they are followed with '-': // string a = null; // var p = new OptionSet () { // { "a", s => a = s }, // }; // p.Parse (new string[]{"-a"}); // sets v != null // p.Parse (new string[]{"-a+"}); // sets v != null // p.Parse (new string[]{"-a-"}); // sets v == null // using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; using System.IO; using System.Runtime.Serialization; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; #if LINQ using System.Linq; #endif #if TEST using NDesk.Options; #endif #if NDESK_OPTIONS namespace NDesk.Options #else namespace Mono.Options #endif { static class StringCoda { public static IEnumerable WrappedLines (string self, params int[] widths) { IEnumerable w = widths; return WrappedLines (self, w); } public static IEnumerable WrappedLines (string self, IEnumerable widths) { if (widths == null) throw new ArgumentNullException ("widths"); return CreateWrappedLinesIterator (self, widths); } private static IEnumerable CreateWrappedLinesIterator (string self, IEnumerable widths) { if (string.IsNullOrEmpty (self)) { yield return string.Empty; yield break; } using (IEnumerator ewidths = widths.GetEnumerator ()) { bool? hw = null; int width = GetNextWidth (ewidths, int.MaxValue, ref hw); int start = 0, end; do { end = GetLineEnd (start, width, self); char c = self [end-1]; if (char.IsWhiteSpace (c)) --end; bool needContinuation = end != self.Length && !IsEolChar (c); string continuation = ""; if (needContinuation) { --end; continuation = "-"; } string line = self.Substring (start, end - start) + continuation; yield return line; start = end; if (char.IsWhiteSpace (c)) ++start; width = GetNextWidth (ewidths, width, ref hw); } while (start < self.Length); } } private static int GetNextWidth (IEnumerator ewidths, int curWidth, ref bool? eValid) { if (!eValid.HasValue || (eValid.HasValue && eValid.Value)) { curWidth = (eValid = ewidths.MoveNext ()).Value ? ewidths.Current : curWidth; // '.' is any character, - is for a continuation const string minWidth = ".-"; if (curWidth < minWidth.Length) throw new ArgumentOutOfRangeException ("widths", string.Format ("Element must be >= {0}, was {1}.", minWidth.Length, curWidth)); return curWidth; } // no more elements, use the last element. return curWidth; } private static bool IsEolChar (char c) { return !char.IsLetterOrDigit (c); } private static int GetLineEnd (int start, int length, string description) { int end = System.Math.Min (start + length, description.Length); int sep = -1; for (int i = start; i < end; ++i) { if (description [i] == '\n') return i+1; if (IsEolChar (description [i])) sep = i+1; } if (sep == -1 || end == description.Length) return end; return sep; } } public class OptionValueCollection : IList, IList { List values = new List (); OptionContext c; internal OptionValueCollection (OptionContext c) { this.c = c; } #region ICollection void ICollection.CopyTo (Array array, int index) {(values as ICollection).CopyTo (array, index);} bool ICollection.IsSynchronized {get {return (values as ICollection).IsSynchronized;}} object ICollection.SyncRoot {get {return (values as ICollection).SyncRoot;}} #endregion #region ICollection public void Add (string item) {values.Add (item);} public void Clear () {values.Clear ();} public bool Contains (string item) {return values.Contains (item);} public void CopyTo (string[] array, int arrayIndex) {values.CopyTo (array, arrayIndex);} public bool Remove (string item) {return values.Remove (item);} public int Count {get {return values.Count;}} public bool IsReadOnly {get {return false;}} #endregion #region IEnumerable IEnumerator IEnumerable.GetEnumerator () {return values.GetEnumerator ();} #endregion #region IEnumerable public IEnumerator GetEnumerator () {return values.GetEnumerator ();} #endregion #region IList int IList.Add (object value) {return (values as IList).Add (value);} bool IList.Contains (object value) {return (values as IList).Contains (value);} int IList.IndexOf (object value) {return (values as IList).IndexOf (value);} void IList.Insert (int index, object value) {(values as IList).Insert (index, value);} void IList.Remove (object value) {(values as IList).Remove (value);} void IList.RemoveAt (int index) {(values as IList).RemoveAt (index);} bool IList.IsFixedSize {get {return false;}} object IList.this [int index] {get {return this [index];} set {(values as IList)[index] = value;}} #endregion #region IList public int IndexOf (string item) {return values.IndexOf (item);} public void Insert (int index, string item) {values.Insert (index, item);} public void RemoveAt (int index) {values.RemoveAt (index);} private void AssertValid (int index) { if (c.Option == null) throw new InvalidOperationException ("OptionContext.Option is null."); if (index >= c.Option.MaxValueCount) throw new ArgumentOutOfRangeException ("index"); if (c.Option.OptionValueType == OptionValueType.Required && index >= values.Count) throw new OptionException (string.Format ( c.OptionSet.MessageLocalizer ("Missing required value for option '{0}'."), c.OptionName), c.OptionName); } public string this [int index] { get { AssertValid (index); return index >= values.Count ? null : values [index]; } set { values [index] = value; } } #endregion public List ToList () { return new List (values); } public string[] ToArray () { return values.ToArray (); } public override string ToString () { return string.Join (", ", values.ToArray ()); } } public class OptionContext { private Option option; private string name; private int index; private OptionSet set; private OptionValueCollection c; public OptionContext (OptionSet set) { this.set = set; this.c = new OptionValueCollection (this); } public Option Option { get {return option;} set {option = value;} } public string OptionName { get {return name;} set {name = value;} } public int OptionIndex { get {return index;} set {index = value;} } public OptionSet OptionSet { get {return set;} } public OptionValueCollection OptionValues { get {return c;} } } public enum OptionValueType { None, Optional, Required, } public abstract class Option { string prototype, description; string[] names; OptionValueType type; int count; string[] separators; bool hidden; protected Option (string prototype, string description) : this (prototype, description, 1, false) { } protected Option (string prototype, string description, int maxValueCount) : this (prototype, description, maxValueCount, false) { } protected Option (string prototype, string description, int maxValueCount, bool hidden) { if (prototype == null) throw new ArgumentNullException ("prototype"); if (prototype.Length == 0) throw new ArgumentException ("Cannot be the empty string.", "prototype"); if (maxValueCount < 0) throw new ArgumentOutOfRangeException ("maxValueCount"); this.prototype = prototype; this.description = description; this.count = maxValueCount; this.names = (this is OptionSet.Category) // append GetHashCode() so that "duplicate" categories have distinct // names, e.g. adding multiple "" categories should be valid. ? new[]{prototype + this.GetHashCode ()} : prototype.Split ('|'); if (this is OptionSet.Category) return; this.type = ParsePrototype (); this.hidden = hidden; if (this.count == 0 && type != OptionValueType.None) throw new ArgumentException ( "Cannot provide maxValueCount of 0 for OptionValueType.Required or " + "OptionValueType.Optional.", "maxValueCount"); if (this.type == OptionValueType.None && maxValueCount > 1) throw new ArgumentException ( string.Format ("Cannot provide maxValueCount of {0} for OptionValueType.None.", maxValueCount), "maxValueCount"); if (Array.IndexOf (names, "<>") >= 0 && ((names.Length == 1 && this.type != OptionValueType.None) || (names.Length > 1 && this.MaxValueCount > 1))) throw new ArgumentException ( "The default option handler '<>' cannot require values.", "prototype"); } public string Prototype {get {return prototype;}} public string Description {get {return description;}} public OptionValueType OptionValueType {get {return type;}} public int MaxValueCount {get {return count;}} public bool Hidden {get {return hidden;}} public string[] GetNames () { return (string[]) names.Clone (); } public string[] GetValueSeparators () { if (separators == null) return new string [0]; return (string[]) separators.Clone (); } protected static T Parse (string value, OptionContext c) { Type tt = typeof (T); bool nullable = tt.IsValueType && tt.IsGenericType && !tt.IsGenericTypeDefinition && tt.GetGenericTypeDefinition () == typeof (Nullable<>); Type targetType = nullable ? tt.GetGenericArguments () [0] : typeof (T); TypeConverter conv = TypeDescriptor.GetConverter (targetType); T t = default (T); try { if (value != null) t = (T) conv.ConvertFromString (value); } catch (Exception e) { throw new OptionException ( string.Format ( c.OptionSet.MessageLocalizer ("Could not convert string `{0}' to type {1} for option `{2}'."), value, targetType.Name, c.OptionName), c.OptionName, e); } return t; } internal string[] Names {get {return names;}} internal string[] ValueSeparators {get {return separators;}} static readonly char[] NameTerminator = new char[]{'=', ':'}; private OptionValueType ParsePrototype () { char type = '\0'; List seps = new List (); for (int i = 0; i < names.Length; ++i) { string name = names [i]; if (name.Length == 0) throw new ArgumentException ("Empty option names are not supported.", "prototype"); int end = name.IndexOfAny (NameTerminator); if (end == -1) continue; names [i] = name.Substring (0, end); if (type == '\0' || type == name [end]) type = name [end]; else throw new ArgumentException ( string.Format ("Conflicting option types: '{0}' vs. '{1}'.", type, name [end]), "prototype"); AddSeparators (name, end, seps); } if (type == '\0') return OptionValueType.None; if (count <= 1 && seps.Count != 0) throw new ArgumentException ( string.Format ("Cannot provide key/value separators for Options taking {0} value(s).", count), "prototype"); if (count > 1) { if (seps.Count == 0) this.separators = new string[]{":", "="}; else if (seps.Count == 1 && seps [0].Length == 0) this.separators = null; else this.separators = seps.ToArray (); } return type == '=' ? OptionValueType.Required : OptionValueType.Optional; } private static void AddSeparators (string name, int end, ICollection seps) { int start = -1; for (int i = end+1; i < name.Length; ++i) { switch (name [i]) { case '{': if (start != -1) throw new ArgumentException ( string.Format ("Ill-formed name/value separator found in \"{0}\".", name), "prototype"); start = i+1; break; case '}': if (start == -1) throw new ArgumentException ( string.Format ("Ill-formed name/value separator found in \"{0}\".", name), "prototype"); seps.Add (name.Substring (start, i-start)); start = -1; break; default: if (start == -1) seps.Add (name [i].ToString ()); break; } } if (start != -1) throw new ArgumentException ( string.Format ("Ill-formed name/value separator found in \"{0}\".", name), "prototype"); } public void Invoke (OptionContext c) { OnParseComplete (c); c.OptionName = null; c.Option = null; c.OptionValues.Clear (); } protected abstract void OnParseComplete (OptionContext c); public override string ToString () { return Prototype; } } public abstract class ArgumentSource { protected ArgumentSource () { } public abstract string[] GetNames (); public abstract string Description { get; } public abstract bool GetArguments (string value, out IEnumerable replacement); public static IEnumerable GetArgumentsFromFile (string file) { return GetArguments (File.OpenText (file), true); } public static IEnumerable GetArguments (TextReader reader) { return GetArguments (reader, false); } // Cribbed from mcs/driver.cs:LoadArgs(string) static IEnumerable GetArguments (TextReader reader, bool close) { try { StringBuilder arg = new StringBuilder (); string line; while ((line = reader.ReadLine ()) != null) { int t = line.Length; for (int i = 0; i < t; i++) { char c = line [i]; if (c == '"' || c == '\'') { char end = c; for (i++; i < t; i++){ c = line [i]; if (c == end) break; arg.Append (c); } } else if (c == ' ') { if (arg.Length > 0) { yield return arg.ToString (); arg.Length = 0; } } else arg.Append (c); } if (arg.Length > 0) { yield return arg.ToString (); arg.Length = 0; } } } finally { if (close) reader.Close (); } } } public class ResponseFileSource : ArgumentSource { public override string[] GetNames () { return new string[]{"@file"}; } public override string Description { get {return "Read response file for more options.";} } public override bool GetArguments (string value, out IEnumerable replacement) { if (string.IsNullOrEmpty (value) || !value.StartsWith ("@")) { replacement = null; return false; } replacement = ArgumentSource.GetArgumentsFromFile (value.Substring (1)); return true; } } [Serializable] public class OptionException : Exception { private string option; public OptionException () { } public OptionException (string message, string optionName) : base (message) { this.option = optionName; } public OptionException (string message, string optionName, Exception innerException) : base (message, innerException) { this.option = optionName; } protected OptionException (SerializationInfo info, StreamingContext context) : base (info, context) { this.option = info.GetString ("OptionName"); } public string OptionName { get {return this.option;} } [SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)] public override void GetObjectData (SerializationInfo info, StreamingContext context) { base.GetObjectData (info, context); info.AddValue ("OptionName", option); } } public delegate void OptionAction (TKey key, TValue value); public class OptionSet : KeyedCollection { public OptionSet () : this (delegate (string f) {return f;}) { } public OptionSet (Converter localizer) { this.localizer = localizer; this.roSources = new ReadOnlyCollection(sources); } Converter localizer; public Converter MessageLocalizer { get {return localizer;} } List sources = new List (); ReadOnlyCollection roSources; public ReadOnlyCollection ArgumentSources { get {return roSources;} } protected override string GetKeyForItem (Option item) { if (item == null) throw new ArgumentNullException ("option"); if (item.Names != null && item.Names.Length > 0) return item.Names [0]; // This should never happen, as it's invalid for Option to be // constructed w/o any names. throw new InvalidOperationException ("Option has no names!"); } [Obsolete ("Use KeyedCollection.this[string]")] protected Option GetOptionForName (string option) { if (option == null) throw new ArgumentNullException ("option"); try { return base [option]; } catch (KeyNotFoundException) { return null; } } protected override void InsertItem (int index, Option item) { base.InsertItem (index, item); AddImpl (item); } protected override void RemoveItem (int index) { Option p = Items [index]; base.RemoveItem (index); // KeyedCollection.RemoveItem() handles the 0th item for (int i = 1; i < p.Names.Length; ++i) { Dictionary.Remove (p.Names [i]); } } protected override void SetItem (int index, Option item) { base.SetItem (index, item); AddImpl (item); } private void AddImpl (Option option) { if (option == null) throw new ArgumentNullException ("option"); List added = new List (option.Names.Length); try { // KeyedCollection.InsertItem/SetItem handle the 0th name. for (int i = 1; i < option.Names.Length; ++i) { Dictionary.Add (option.Names [i], option); added.Add (option.Names [i]); } } catch (Exception) { foreach (string name in added) Dictionary.Remove (name); throw; } } public OptionSet Add (string header) { if (header == null) throw new ArgumentNullException ("header"); Add (new Category (header)); return this; } internal sealed class Category : Option { // Prototype starts with '=' because this is an invalid prototype // (see Option.ParsePrototype(), and thus it'll prevent Category // instances from being accidentally used as normal options. public Category (string description) : base ("=:Category:= " + description, description) { } protected override void OnParseComplete (OptionContext c) { throw new NotSupportedException ("Category.OnParseComplete should not be invoked."); } } public new OptionSet Add (Option option) { base.Add (option); return this; } sealed class ActionOption : Option { Action action; public ActionOption (string prototype, string description, int count, Action action) : this (prototype, description, count, action, false) { } public ActionOption (string prototype, string description, int count, Action action, bool hidden) : base (prototype, description, count, hidden) { if (action == null) throw new ArgumentNullException ("action"); this.action = action; } protected override void OnParseComplete (OptionContext c) { action (c.OptionValues); } } public OptionSet Add (string prototype, Action action) { return Add (prototype, null, action); } public OptionSet Add (string prototype, string description, Action action) { return Add (prototype, description, action, false); } public OptionSet Add (string prototype, string description, Action action, bool hidden) { if (action == null) throw new ArgumentNullException ("action"); Option p = new ActionOption (prototype, description, 1, delegate (OptionValueCollection v) { action (v [0]); }, hidden); base.Add (p); return this; } public OptionSet Add (string prototype, OptionAction action) { return Add (prototype, null, action); } public OptionSet Add (string prototype, string description, OptionAction action) { return Add (prototype, description, action, false); } public OptionSet Add (string prototype, string description, OptionAction action, bool hidden) { if (action == null) throw new ArgumentNullException ("action"); Option p = new ActionOption (prototype, description, 2, delegate (OptionValueCollection v) {action (v [0], v [1]);}, hidden); base.Add (p); return this; } sealed class ActionOption : Option { Action action; public ActionOption (string prototype, string description, Action action) : base (prototype, description, 1) { if (action == null) throw new ArgumentNullException ("action"); this.action = action; } protected override void OnParseComplete (OptionContext c) { action (Parse (c.OptionValues [0], c)); } } sealed class ActionOption : Option { OptionAction action; public ActionOption (string prototype, string description, OptionAction action) : base (prototype, description, 2) { if (action == null) throw new ArgumentNullException ("action"); this.action = action; } protected override void OnParseComplete (OptionContext c) { action ( Parse (c.OptionValues [0], c), Parse (c.OptionValues [1], c)); } } public OptionSet Add (string prototype, Action action) { return Add (prototype, null, action); } public OptionSet Add (string prototype, string description, Action action) { return Add (new ActionOption (prototype, description, action)); } public OptionSet Add (string prototype, OptionAction action) { return Add (prototype, null, action); } public OptionSet Add (string prototype, string description, OptionAction action) { return Add (new ActionOption (prototype, description, action)); } public OptionSet Add (ArgumentSource source) { if (source == null) throw new ArgumentNullException ("source"); sources.Add (source); return this; } protected virtual OptionContext CreateOptionContext () { return new OptionContext (this); } public List Parse (IEnumerable arguments) { if (arguments == null) throw new ArgumentNullException ("arguments"); OptionContext c = CreateOptionContext (); c.OptionIndex = -1; bool process = true; List unprocessed = new List (); Option def = Contains ("<>") ? this ["<>"] : null; ArgumentEnumerator ae = new ArgumentEnumerator (arguments); foreach (string argument in ae) { ++c.OptionIndex; if (argument == "--") { process = false; continue; } if (!process) { Unprocessed (unprocessed, def, c, argument); continue; } if (AddSource (ae, argument)) continue; if (!Parse (argument, c)) Unprocessed (unprocessed, def, c, argument); } if (c.Option != null) c.Option.Invoke (c); return unprocessed; } class ArgumentEnumerator : IEnumerable { List> sources = new List> (); public ArgumentEnumerator (IEnumerable arguments) { sources.Add (arguments.GetEnumerator ()); } public void Add (IEnumerable arguments) { sources.Add (arguments.GetEnumerator ()); } public IEnumerator GetEnumerator () { do { IEnumerator c = sources [sources.Count-1]; if (c.MoveNext ()) yield return c.Current; else { c.Dispose (); sources.RemoveAt (sources.Count-1); } } while (sources.Count > 0); } IEnumerator IEnumerable.GetEnumerator () { return GetEnumerator (); } } bool AddSource (ArgumentEnumerator ae, string argument) { foreach (ArgumentSource source in sources) { IEnumerable replacement; if (!source.GetArguments (argument, out replacement)) continue; ae.Add (replacement); return true; } return false; } private static bool Unprocessed (ICollection extra, Option def, OptionContext c, string argument) { if (def == null) { extra.Add (argument); return false; } c.OptionValues.Add (argument); c.Option = def; c.Option.Invoke (c); return false; } private readonly Regex ValueOption = new Regex ( @"^(?--|-|/)(?[^:=]+)((?[:=])(?.*))?$"); protected bool GetOptionParts (string argument, out string flag, out string name, out string sep, out string value) { if (argument == null) throw new ArgumentNullException ("argument"); flag = name = sep = value = null; Match m = ValueOption.Match (argument); if (!m.Success) { return false; } flag = m.Groups ["flag"].Value; name = m.Groups ["name"].Value; if (m.Groups ["sep"].Success && m.Groups ["value"].Success) { sep = m.Groups ["sep"].Value; value = m.Groups ["value"].Value; } return true; } protected virtual bool Parse (string argument, OptionContext c) { if (c.Option != null) { ParseValue (argument, c); return true; } string f, n, s, v; if (!GetOptionParts (argument, out f, out n, out s, out v)) return false; Option p; if (Contains (n)) { p = this [n]; c.OptionName = f + n; c.Option = p; switch (p.OptionValueType) { case OptionValueType.None: c.OptionValues.Add (n); c.Option.Invoke (c); break; case OptionValueType.Optional: case OptionValueType.Required: ParseValue (v, c); break; } return true; } // no match; is it a bool option? if (ParseBool (argument, n, c)) return true; // is it a bundled option? if (ParseBundledValue (f, string.Concat (n + s + v), c)) return true; return false; } private void ParseValue (string option, OptionContext c) { if (option != null) foreach (string o in c.Option.ValueSeparators != null ? option.Split (c.Option.ValueSeparators, c.Option.MaxValueCount - c.OptionValues.Count, StringSplitOptions.None) : new string[]{option}) { c.OptionValues.Add (o); } if (c.OptionValues.Count == c.Option.MaxValueCount || c.Option.OptionValueType == OptionValueType.Optional) c.Option.Invoke (c); else if (c.OptionValues.Count > c.Option.MaxValueCount) { throw new OptionException (localizer (string.Format ( "Error: Found {0} option values when expecting {1}.", c.OptionValues.Count, c.Option.MaxValueCount)), c.OptionName); } } private bool ParseBool (string option, string n, OptionContext c) { Option p; string rn; if (n.Length >= 1 && (n [n.Length-1] == '+' || n [n.Length-1] == '-') && Contains ((rn = n.Substring (0, n.Length-1)))) { p = this [rn]; string v = n [n.Length-1] == '+' ? option : null; c.OptionName = option; c.Option = p; c.OptionValues.Add (v); p.Invoke (c); return true; } return false; } private bool ParseBundledValue (string f, string n, OptionContext c) { if (f != "-") return false; for (int i = 0; i < n.Length; ++i) { Option p; string opt = f + n [i].ToString (); string rn = n [i].ToString (); if (!Contains (rn)) { if (i == 0) return false; throw new OptionException (string.Format (localizer ( "Cannot bundle unregistered option '{0}'."), opt), opt); } p = this [rn]; switch (p.OptionValueType) { case OptionValueType.None: Invoke (c, opt, n, p); break; case OptionValueType.Optional: case OptionValueType.Required: { string v = n.Substring (i+1); c.Option = p; c.OptionName = opt; ParseValue (v.Length != 0 ? v : null, c); return true; } default: throw new InvalidOperationException ("Unknown OptionValueType: " + p.OptionValueType); } } return true; } private static void Invoke (OptionContext c, string name, string value, Option option) { c.OptionName = name; c.Option = option; c.OptionValues.Add (value); option.Invoke (c); } private const int OptionWidth = 29; private const int Description_FirstWidth = 80 - OptionWidth; private const int Description_RemWidth = 80 - OptionWidth - 2; public void WriteOptionDescriptions (TextWriter o) { foreach (Option p in this) { int written = 0; if (p.Hidden) continue; Category c = p as Category; if (c != null) { WriteDescription (o, p.Description, "", 80, 80); continue; } if (!WriteOptionPrototype (o, p, ref written)) continue; if (written < OptionWidth) o.Write (new string (' ', OptionWidth - written)); else { o.WriteLine (); o.Write (new string (' ', OptionWidth)); } WriteDescription (o, p.Description, new string (' ', OptionWidth+2), Description_FirstWidth, Description_RemWidth); } foreach (ArgumentSource s in sources) { string[] names = s.GetNames (); if (names == null || names.Length == 0) continue; int written = 0; Write (o, ref written, " "); Write (o, ref written, names [0]); for (int i = 1; i < names.Length; ++i) { Write (o, ref written, ", "); Write (o, ref written, names [i]); } if (written < OptionWidth) o.Write (new string (' ', OptionWidth - written)); else { o.WriteLine (); o.Write (new string (' ', OptionWidth)); } WriteDescription (o, s.Description, new string (' ', OptionWidth+2), Description_FirstWidth, Description_RemWidth); } } void WriteDescription (TextWriter o, string value, string prefix, int firstWidth, int remWidth) { bool indent = false; foreach (string line in GetLines (localizer (GetDescription (value)), firstWidth, remWidth)) { if (indent) o.Write (prefix); o.WriteLine (line); indent = true; } } bool WriteOptionPrototype (TextWriter o, Option p, ref int written) { string[] names = p.Names; int i = GetNextOptionIndex (names, 0); if (i == names.Length) return false; if (names [i].Length == 1) { Write (o, ref written, " -"); Write (o, ref written, names [0]); } else { Write (o, ref written, " --"); Write (o, ref written, names [0]); } for ( i = GetNextOptionIndex (names, i+1); i < names.Length; i = GetNextOptionIndex (names, i+1)) { Write (o, ref written, ", "); Write (o, ref written, names [i].Length == 1 ? "-" : "--"); Write (o, ref written, names [i]); } if (p.OptionValueType == OptionValueType.Optional || p.OptionValueType == OptionValueType.Required) { if (p.OptionValueType == OptionValueType.Optional) { Write (o, ref written, localizer ("[")); } Write (o, ref written, localizer ("=" + GetArgumentName (0, p.MaxValueCount, p.Description))); string sep = p.ValueSeparators != null && p.ValueSeparators.Length > 0 ? p.ValueSeparators [0] : " "; for (int c = 1; c < p.MaxValueCount; ++c) { Write (o, ref written, localizer (sep + GetArgumentName (c, p.MaxValueCount, p.Description))); } if (p.OptionValueType == OptionValueType.Optional) { Write (o, ref written, localizer ("]")); } } return true; } static int GetNextOptionIndex (string[] names, int i) { while (i < names.Length && names [i] == "<>") { ++i; } return i; } static void Write (TextWriter o, ref int n, string s) { n += s.Length; o.Write (s); } private static string GetArgumentName (int index, int maxIndex, string description) { if (description == null) return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1); string[] nameStart; if (maxIndex == 1) nameStart = new string[]{"{0:", "{"}; else nameStart = new string[]{"{" + index + ":"}; for (int i = 0; i < nameStart.Length; ++i) { int start, j = 0; do { start = description.IndexOf (nameStart [i], j); } while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false); if (start == -1) continue; int end = description.IndexOf ("}", start); if (end == -1) continue; return description.Substring (start + nameStart [i].Length, end - start - nameStart [i].Length); } return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1); } private static string GetDescription (string description) { if (description == null) return string.Empty; StringBuilder sb = new StringBuilder (description.Length); int start = -1; for (int i = 0; i < description.Length; ++i) { switch (description [i]) { case '{': if (i == start) { sb.Append ('{'); start = -1; } else if (start < 0) start = i + 1; break; case '}': if (start < 0) { if ((i+1) == description.Length || description [i+1] != '}') throw new InvalidOperationException ("Invalid option description: " + description); ++i; sb.Append ("}"); } else { sb.Append (description.Substring (start, i - start)); start = -1; } break; case ':': if (start < 0) goto default; start = i + 1; break; default: if (start < 0) sb.Append (description [i]); break; } } return sb.ToString (); } private static IEnumerable GetLines (string description, int firstWidth, int remWidth) { return StringCoda.WrappedLines (description, firstWidth, remWidth); } } } ================================================ FILE: src/Update/Program.cs ================================================ using NuGet; using Squirrel.SimpleSplat; using Squirrel.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace Squirrel.Update { enum UpdateAction { Unset = 0, Install, Uninstall, Download, Update, Releasify, Shortcut, Deshortcut, ProcessStart, UpdateSelf, CheckForUpdate } class Program : IEnableLogger { static StartupOption opt; public static int Main(string[] args) { var pg = new Program(); try { return pg.main(args); } catch (Exception ex) { // NB: Normally this is a terrible idea but we want to make // sure Setup.exe above us gets the nonzero error code Console.Error.WriteLine(ex); return -1; } } int main(string[] args) { try { opt = new StartupOption(args); } catch (Exception ex) { using (var logger = new SetupLogLogger(true, "OptionParsing") { Level = LogLevel.Info }) { SquirrelLocator.CurrentMutable.Register(() => logger, typeof(Squirrel.SimpleSplat.ILogger)); logger.Write($"Failed to parse command line options. {ex.Message}", LogLevel.Error); } throw; } // NB: Trying to delete the app directory while we have Setup.log // open will actually crash the uninstaller bool isUninstalling = opt.updateAction == UpdateAction.Uninstall; using (var logger = new SetupLogLogger(isUninstalling, opt.updateAction.ToString()) {Level = LogLevel.Info}) { SquirrelLocator.CurrentMutable.Register(() => logger, typeof (SimpleSplat.ILogger)); try { return executeCommandLine(args); } catch (Exception ex) { logger.Write("Finished with unhandled exception: " + ex, LogLevel.Fatal); throw; } } } int executeCommandLine(string[] args) { var animatedGifWindowToken = new CancellationTokenSource(); #if !MONO // Uncomment to test Gifs /* var ps = new ProgressSource(); int i = 0; var t = new Timer(_ => ps.Raise(i += 10), null, 0, 1000); AnimatedGifWindow.ShowWindow(TimeSpan.FromMilliseconds(0), animatedGifWindowToken.Token, ps); Thread.Sleep(10 * 60 * 1000); */ #endif using (Disposable.Create(() => animatedGifWindowToken.Cancel())) { this.Log().Info("Starting Squirrel Updater: " + String.Join(" ", args)); if (args.Any(x => x.StartsWith("/squirrel", StringComparison.OrdinalIgnoreCase))) { // NB: We're marked as Squirrel-aware, but we don't want to do // anything in response to these events return 0; } if (opt.updateAction == UpdateAction.Unset) { ShowHelp(); return -1; } switch (opt.updateAction) { #if !MONO case UpdateAction.Install: var progressSource = new ProgressSource(); if (!opt.silentInstall) { AnimatedGifWindow.ShowWindow(TimeSpan.FromSeconds(4), animatedGifWindowToken.Token, progressSource); } Install(opt.silentInstall, progressSource, Path.GetFullPath(opt.target)).Wait(); animatedGifWindowToken.Cancel(); break; case UpdateAction.Uninstall: Uninstall().Wait(); break; case UpdateAction.Download: Console.WriteLine(Download(opt.target).Result); break; case UpdateAction.Update: Update(opt.target).Wait(); break; case UpdateAction.CheckForUpdate: Console.WriteLine(CheckForUpdate(opt.target).Result); break; case UpdateAction.UpdateSelf: UpdateSelf().Wait(); break; case UpdateAction.Shortcut: Shortcut(opt.target, opt.shortcutArgs, opt.processStartArgs, opt.setupIcon, opt.onlyUpdateShortcuts); break; case UpdateAction.Deshortcut: Deshortcut(opt.target, opt.shortcutArgs); break; case UpdateAction.ProcessStart: ProcessStart(opt.processStart, opt.processStartArgs, opt.shouldWait); break; #endif case UpdateAction.Releasify: Releasify(opt.target, opt.releaseDir, opt.packagesDir, opt.bootstrapperExe, opt.backgroundGif, opt.signingParameters, opt.baseUrl, opt.setupIcon, !opt.noMsi, opt.packageAs64Bit, opt.frameworkVersion, !opt.noDelta); break; } } this.Log().Info("Finished Squirrel Updater"); return 0; } public async Task Install(bool silentInstall, ProgressSource progressSource, string sourceDirectory = null) { sourceDirectory = sourceDirectory ?? Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var releasesPath = Path.Combine(sourceDirectory, "RELEASES"); this.Log().Info("Starting install, writing to {0}", sourceDirectory); if (!File.Exists(releasesPath)) { this.Log().Info("RELEASES doesn't exist, creating it at " + releasesPath); var nupkgs = (new DirectoryInfo(sourceDirectory)).GetFiles() .Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)) .Select(x => ReleaseEntry.GenerateFromFile(x.FullName)); ReleaseEntry.WriteReleaseFile(nupkgs, releasesPath); } var ourAppName = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasesPath, Encoding.UTF8)) .First().PackageName; using (var mgr = new UpdateManager(sourceDirectory, ourAppName)) { this.Log().Info("About to install to: " + mgr.RootAppDirectory); if (Directory.Exists(mgr.RootAppDirectory)) { this.Log().Warn("Install path {0} already exists, burning it to the ground", mgr.RootAppDirectory); mgr.KillAllExecutablesBelongingToPackage(); await Task.Delay(500); await this.ErrorIfThrows(() => Utility.DeleteDirectory(mgr.RootAppDirectory), "Failed to remove existing directory on full install, is the app still running???"); this.ErrorIfThrows(() => Utility.Retry(() => Directory.CreateDirectory(mgr.RootAppDirectory), 3), "Couldn't recreate app directory, perhaps Antivirus is blocking it"); } Directory.CreateDirectory(mgr.RootAppDirectory); var updateTarget = Path.Combine(mgr.RootAppDirectory, "Update.exe"); this.ErrorIfThrows(() => File.Copy(Assembly.GetExecutingAssembly().Location, updateTarget, true), "Failed to copy Update.exe to " + updateTarget); await mgr.FullInstall(silentInstall, progressSource.Raise); await this.ErrorIfThrows(() => mgr.CreateUninstallerRegistryEntry(), "Failed to create uninstaller registry entry"); } } public async Task Update(string updateUrl, string appName = null) { appName = appName ?? getAppNameFromDirectory(); this.Log().Info("Starting update, downloading from " + updateUrl); using (var mgr = new UpdateManager(updateUrl, appName)) { bool ignoreDeltaUpdates = false; this.Log().Info("About to update to: " + mgr.RootAppDirectory); retry: try { // 3 % (3 stages) var updateInfo = await mgr.CheckForUpdate(intention: UpdaterIntention.Update, ignoreDeltaUpdates: ignoreDeltaUpdates, progress: x => Console.WriteLine(UpdateManager.CalculateProgress(x, 0, 3))); // 3 - 30 % await mgr.DownloadReleases(updateInfo.ReleasesToApply, x => Console.WriteLine(UpdateManager.CalculateProgress(x, 3, 30))); // 30 - 100 % await mgr.ApplyReleases(updateInfo, x => Console.WriteLine(UpdateManager.CalculateProgress(x, 30, 100))); } catch (Exception ex) { if (ignoreDeltaUpdates) { this.Log().ErrorException("Really couldn't apply updates!", ex); throw; } this.Log().WarnException("Failed to apply updates, falling back to full updates", ex); ignoreDeltaUpdates = true; goto retry; } var updateTarget = Path.Combine(mgr.RootAppDirectory, "Update.exe"); await this.ErrorIfThrows(() => mgr.CreateUninstallerRegistryEntry(), "Failed to create uninstaller registry entry"); } } public async Task UpdateSelf() { waitForParentToExit(); var src = Assembly.GetExecutingAssembly().Location; var updateDotExeForOurPackage = Path.Combine( Path.GetDirectoryName(src), "..", "Update.exe"); await Task.Run(() => { File.Copy(src, updateDotExeForOurPackage, true); }); } public async Task Download(string updateUrl, string appName = null) { appName = appName ?? getAppNameFromDirectory(); this.Log().Info("Fetching update information, downloading from " + updateUrl); using (var mgr = new UpdateManager(updateUrl, appName)) { var updateInfo = await mgr.CheckForUpdate(intention: UpdaterIntention.Update, progress: x => Console.WriteLine(x / 3)); await mgr.DownloadReleases(updateInfo.ReleasesToApply, x => Console.WriteLine(33 + x / 3)); var releaseNotes = updateInfo.FetchReleaseNotes(); var sanitizedUpdateInfo = new { currentVersion = updateInfo.CurrentlyInstalledVersion.Version.ToString(), futureVersion = updateInfo.FutureReleaseEntry.Version.ToString(), releasesToApply = updateInfo.ReleasesToApply.Select(x => new { version = x.Version.ToString(), releaseNotes = releaseNotes.ContainsKey(x) ? releaseNotes[x] : "", }).ToArray(), }; return SimpleJson.SerializeObject(sanitizedUpdateInfo); } } public async Task CheckForUpdate(string updateUrl, string appName = null) { appName = appName ?? getAppNameFromDirectory(); this.Log().Info("Fetching update information, downloading from " + updateUrl); using (var mgr = new UpdateManager(updateUrl, appName)) { var updateInfo = await mgr.CheckForUpdate(intention: UpdaterIntention.Update, progress: x => Console.WriteLine(x)); var releaseNotes = updateInfo.FetchReleaseNotes(); var sanitizedUpdateInfo = new { currentVersion = updateInfo.CurrentlyInstalledVersion.Version.ToString(), futureVersion = updateInfo.FutureReleaseEntry.Version.ToString(), releasesToApply = updateInfo.ReleasesToApply.Select(x => new { version = x.Version.ToString(), releaseNotes = releaseNotes.ContainsKey(x) ? releaseNotes[x] : "", }).ToArray(), }; return SimpleJson.SerializeObject(sanitizedUpdateInfo); } } public async Task Uninstall(string appName = null) { this.Log().Info("Starting uninstall for app: " + appName); appName = appName ?? getAppNameFromDirectory(); using (var mgr = new UpdateManager("", appName)) { await mgr.FullUninstall(); mgr.RemoveUninstallerRegistryEntry(); } } public void Releasify(string package, string targetDir = null, string packagesDir = null, string bootstrapperExe = null, string backgroundGif = null, string signingOpts = null, string baseUrl = null, string setupIcon = null, bool generateMsi = true, bool packageAs64Bit = false, string frameworkVersion = null, bool generateDeltas = true) { ensureConsole(); if (baseUrl != null) { if (!Utility.IsHttpUrl(baseUrl)) { throw new Exception(string.Format("Invalid --baseUrl '{0}'. A base URL must start with http or https and be a valid URI.", baseUrl)); } if (!baseUrl.EndsWith("/")) { baseUrl += "/"; } } targetDir = targetDir ?? Path.Combine(".", "Releases"); packagesDir = packagesDir ?? "."; bootstrapperExe = bootstrapperExe ?? Path.Combine(".", "Setup.exe"); if (!Directory.Exists(targetDir)) { Directory.CreateDirectory(targetDir); } if (!File.Exists(bootstrapperExe)) { bootstrapperExe = Path.Combine( Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Setup.exe"); } this.Log().Info("Bootstrapper EXE found at:" + bootstrapperExe); var di = new DirectoryInfo(targetDir); File.Copy(package, Path.Combine(di.FullName, Path.GetFileName(package)), true); var allNuGetFiles = di.EnumerateFiles() .Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase)); var toProcess = allNuGetFiles.Where(x => !x.Name.Contains("-delta") && !x.Name.Contains("-full")); var processed = new List(); var releaseFilePath = Path.Combine(di.FullName, "RELEASES"); var previousReleases = new List(); if (File.Exists(releaseFilePath)) { previousReleases.AddRange(ReleaseEntry.ParseReleaseFile(File.ReadAllText(releaseFilePath, Encoding.UTF8))); } foreach (var file in toProcess) { this.Log().Info("Creating release package: " + file.FullName); var rp = new ReleasePackage(file.FullName); rp.CreateReleasePackage(Path.Combine(di.FullName, rp.SuggestedReleaseFileName), packagesDir, contentsPostProcessHook: pkgPath => { new DirectoryInfo(pkgPath).GetAllFilesRecursively() .Where(x => x.Name.ToLowerInvariant().EndsWith(".exe")) .Where(x => !x.Name.ToLowerInvariant().Contains("squirrel.exe")) .Where(x => Utility.IsFileTopLevelInPackage(x.FullName, pkgPath)) .Where(x => Utility.ExecutableUsesWin32Subsystem(x.FullName)) .ForEachAsync(x => createExecutableStubForExe(x.FullName)) .Wait(); if (signingOpts == null) return; new DirectoryInfo(pkgPath).GetAllFilesRecursively() .Where(x => Utility.FileIsLikelyPEImage(x.Name)) .ForEachAsync(async x => { if (isPEFileSigned(x.FullName)) { this.Log().Info("{0} is already signed, skipping", x.FullName); return; } this.Log().Info("About to sign {0}", x.FullName); await signPEFile(x.FullName, signingOpts); }, 1) .Wait(); }); processed.Add(rp.ReleasePackageFile); var prev = ReleaseEntry.GetPreviousRelease(previousReleases, rp, targetDir); if (prev != null && generateDeltas) { var deltaBuilder = new DeltaPackageBuilder(null); var dp = deltaBuilder.CreateDeltaPackage(prev, rp, Path.Combine(di.FullName, rp.SuggestedReleaseFileName.Replace("full", "delta"))); processed.Insert(0, dp.InputPackageFile); } } foreach (var file in toProcess) { File.Delete(file.FullName); } var newReleaseEntries = processed .Select(packageFilename => ReleaseEntry.GenerateFromFile(packageFilename, baseUrl)) .ToList(); var distinctPreviousReleases = previousReleases .Where(x => !newReleaseEntries.Select(e => e.Version).Contains(x.Version)); var releaseEntries = distinctPreviousReleases.Concat(newReleaseEntries).ToList(); ReleaseEntry.WriteReleaseFile(releaseEntries, releaseFilePath); var targetSetupExe = Path.Combine(di.FullName, "Setup.exe"); var newestFullRelease = releaseEntries.MaxBy(x => x.Version).Where(x => !x.IsDelta).First(); File.Copy(bootstrapperExe, targetSetupExe, true); var zipPath = createSetupEmbeddedZip(Path.Combine(di.FullName, newestFullRelease.Filename), di.FullName, backgroundGif, signingOpts, setupIcon).Result; var writeZipToSetup = Utility.FindHelperExecutable("WriteZipToSetup.exe"); try { var arguments = String.Format("\"{0}\" \"{1}\" \"--set-required-framework\" \"{2}\"", targetSetupExe, zipPath, frameworkVersion); var result = Utility.InvokeProcessAsync(writeZipToSetup, arguments, CancellationToken.None).Result; if (result.Item1 != 0) throw new Exception("Failed to write Zip to Setup.exe!\n\n" + result.Item2); } catch (Exception ex) { this.Log().ErrorException("Failed to update Setup.exe with new Zip file", ex); } finally { File.Delete(zipPath); } Utility.Retry(() => setPEVersionInfoAndIcon(targetSetupExe, new ZipPackage(package), setupIcon).Wait()); if (signingOpts != null) { signPEFile(targetSetupExe, signingOpts).Wait(); } if (generateMsi) { createMsiPackage(targetSetupExe, new ZipPackage(package), packageAs64Bit).Wait(); if (signingOpts != null) { signPEFile(targetSetupExe.Replace(".exe", ".msi"), signingOpts).Wait(); } } } public void Shortcut(string exeName, string shortcutArgs, string processStartArgs, string icon, bool onlyUpdate) { if (String.IsNullOrWhiteSpace(exeName)) { ShowHelp(); return; } var appName = getAppNameFromDirectory(); var defaultLocations = ShortcutLocation.StartMenu | ShortcutLocation.Desktop; var locations = parseShortcutLocations(shortcutArgs); using (var mgr = new UpdateManager("", appName)) { mgr.CreateShortcutsForExecutable(exeName, locations ?? defaultLocations, onlyUpdate, processStartArgs, icon); } } public void Deshortcut(string exeName, string shortcutArgs) { if (String.IsNullOrWhiteSpace(exeName)) { ShowHelp(); return; } var appName = getAppNameFromDirectory(); var defaultLocations = ShortcutLocation.StartMenu | ShortcutLocation.Desktop; var locations = parseShortcutLocations(shortcutArgs); using (var mgr = new UpdateManager("", appName)) { mgr.RemoveShortcutsForExecutable(exeName, locations ?? defaultLocations); } } public void ProcessStart(string exeName, string arguments, bool shouldWait) { if (String.IsNullOrWhiteSpace(exeName)) { ShowHelp(); return; } // Find the latest installed version's app dir var appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var releases = ReleaseEntry.ParseReleaseFile( File.ReadAllText(Utility.LocalReleaseFileForAppDir(appDir), Encoding.UTF8)); // NB: We add the hacked up version in here to handle a migration // issue, where versions of Squirrel pre PR #450 will not understand // prerelease tags, so it will end up writing the release name sans // tags. However, the RELEASES file _will_ have them, so we need to look // for directories that match both the real version, and the sanitized // version, giving priority to the former. var latestAppDir = releases .OrderByDescending(x => x.Version) .SelectMany(x => new[] { Utility.AppDirForRelease(appDir, x), Utility.AppDirForVersion(appDir, new SemanticVersion(x.Version.Version.Major, x.Version.Version.Minor, x.Version.Version.Build, "")) }) .FirstOrDefault(x => Directory.Exists(x)); // Check for the EXE name they want var targetExe = new FileInfo(Path.Combine(latestAppDir, exeName.Replace("%20", " "))); this.Log().Info("Want to launch '{0}'", targetExe); // Check for path canonicalization attacks if (!targetExe.FullName.StartsWith(latestAppDir, StringComparison.Ordinal)) { throw new ArgumentException(); } if (!targetExe.Exists) { this.Log().Error("File {0} doesn't exist in current release", targetExe); throw new ArgumentException(); } if (shouldWait) waitForParentToExit(); try { this.Log().Info("About to launch: '{0}': {1}", targetExe.FullName, arguments ?? ""); Process.Start(new ProcessStartInfo(targetExe.FullName, arguments ?? "") { WorkingDirectory = Path.GetDirectoryName(targetExe.FullName) }); } catch (Exception ex) { this.Log().ErrorException("Failed to start process", ex); } } public void ShowHelp() { ensureConsole(); opt.WriteOptionDescriptions(); } void waitForParentToExit() { // Grab a handle the parent process var parentPid = NativeMethods.GetParentProcessId(); var handle = default(IntPtr); // Wait for our parent to exit try { handle = NativeMethods.OpenProcess(ProcessAccess.Synchronize, false, parentPid); if (handle != IntPtr.Zero) { this.Log().Info("About to wait for parent PID {0}", parentPid); NativeMethods.WaitForSingleObject(handle, 0xFFFFFFFF /*INFINITE*/); } else { this.Log().Info("Parent PID {0} no longer valid - ignoring", parentPid); } } finally { if (handle != IntPtr.Zero) NativeMethods.CloseHandle(handle); } } async Task createSetupEmbeddedZip(string fullPackage, string releasesDir, string backgroundGif, string signingOpts, string setupIcon) { string tempPath; this.Log().Info("Building embedded zip file for Setup.exe"); using (Utility.WithTempDirectory(out tempPath, null)) { this.ErrorIfThrows(() => { File.Copy(Assembly.GetEntryAssembly().Location.Replace("-Mono.exe", ".exe"), Path.Combine(tempPath, "Update.exe")); File.Copy(fullPackage, Path.Combine(tempPath, Path.GetFileName(fullPackage))); }, "Failed to write package files to temp dir: " + tempPath); if (!String.IsNullOrWhiteSpace(backgroundGif)) { this.ErrorIfThrows(() => { File.Copy(backgroundGif, Path.Combine(tempPath, "background.gif")); }, "Failed to write animated GIF to temp dir: " + tempPath); } if (!String.IsNullOrWhiteSpace(setupIcon)) { this.ErrorIfThrows(() => { File.Copy(setupIcon, Path.Combine(tempPath, "setupIcon.ico")); }, "Failed to write icon to temp dir: " + tempPath); } var releases = new[] { ReleaseEntry.GenerateFromFile(fullPackage) }; ReleaseEntry.WriteReleaseFile(releases, Path.Combine(tempPath, "RELEASES")); var target = Path.GetTempFileName(); File.Delete(target); // Sign Update.exe so that virus scanners don't think we're // pulling one over on them if (signingOpts != null) { var di = new DirectoryInfo(tempPath); var files = di.EnumerateFiles() .Where(x => x.Name.ToLowerInvariant().EndsWith(".exe")) .Select(x => x.FullName); await files.ForEachAsync(x => signPEFile(x, signingOpts)); } this.ErrorIfThrows(() => ZipFile.CreateFromDirectory(tempPath, target, CompressionLevel.Optimal, false), "Failed to create Zip file from directory: " + tempPath); return target; } } static async Task signPEFile(string exePath, string signingOpts) { // Try to find SignTool.exe var exe = @".\signtool.exe"; if (!File.Exists(exe)) { exe = Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "signtool.exe"); // Run down PATH and hope for the best if (!File.Exists(exe)) exe = "signtool.exe"; } var processResult = await Utility.InvokeProcessAsync(exe, String.Format("sign {0} \"{1}\"", signingOpts, exePath), CancellationToken.None); if (processResult.Item1 != 0) { var optsWithPasswordHidden = new Regex(@"(?x) #ignore pattern white space so we can leave comments (?i) #ignore case (?<=/p\s+) #positive look behind for /p that is followed by white space(s) .*? #get everything lazy way (?=\s+) #positive look ahead for white space(s) " ).Replace(signingOpts, "/p ********"); var msg = String.Format("Failed to sign, command invoked was: '{0} sign {1} {2}'", exe, optsWithPasswordHidden, exePath); throw new Exception(msg); } else { Console.WriteLine(processResult.Item2); } } bool isPEFileSigned(string path) { #if MONO return Path.GetExtension(path).Equals(".exe", StringComparison.OrdinalIgnoreCase); #else try { return AuthenticodeTools.IsTrusted(path); } catch (Exception ex) { this.Log().ErrorException("Failed to determine signing status for " + path, ex); return false; } #endif } async Task createExecutableStubForExe(string fullName) { var exe = Utility.FindHelperExecutable(@"StubExecutable.exe"); var target = Path.Combine( Path.GetDirectoryName(fullName), Path.GetFileNameWithoutExtension(fullName) + "_ExecutionStub.exe"); await Utility.CopyToAsync(exe, target); await Utility.InvokeProcessAsync( Utility.FindHelperExecutable("WriteZipToSetup.exe"), String.Format("--copy-stub-resources \"{0}\" \"{1}\"", fullName, target), CancellationToken.None); } static async Task setPEVersionInfoAndIcon(string exePath, IPackage package, string iconPath = null) { var realExePath = Path.GetFullPath(exePath); var company = String.Join(",", package.Authors); var verStrings = new Dictionary() { { "CompanyName", company }, { "LegalCopyright", package.Copyright ?? "Copyright © " + DateTime.Now.Year.ToString() + " " + company }, { "FileDescription", package.Summary ?? package.Description ?? "Installer for " + package.Id }, { "ProductName", package.Description ?? package.Summary ?? package.Id }, }; var args = verStrings.Aggregate(new StringBuilder("\"" + realExePath + "\""), (acc, x) => { acc.AppendFormat(" --set-version-string \"{0}\" \"{1}\"", x.Key, x.Value); return acc; }); args.AppendFormat(" --set-file-version {0} --set-product-version {0}", package.Version.ToString()); if (iconPath != null) { args.AppendFormat(" --set-icon \"{0}\"", Path.GetFullPath(iconPath)); } // Try to find rcedit.exe string exe = Utility.FindHelperExecutable("rcedit.exe"); var processResult = await Utility.InvokeProcessAsync(exe, args.ToString(), CancellationToken.None); if (processResult.Item1 != 0) { var msg = String.Format( "Failed to modify resources, command invoked was: '{0} {1}'\n\nOutput was:\n{2}", exe, args, processResult.Item2); throw new Exception(msg); } else { Console.WriteLine(processResult.Item2); } } static async Task createMsiPackage(string setupExe, IPackage package, bool packageAs64Bit) { var pathToWix = pathToWixTools(); var setupExeDir = Path.GetDirectoryName(setupExe); var company = String.Join(",", package.Authors); var culture = CultureInfo.GetCultureInfo(package.Language ?? "").TextInfo.ANSICodePage; var templateText = File.ReadAllText(Path.Combine(pathToWix, "template.wxs")); var templateData = new Dictionary { { "Id", package.Id }, { "Title", package.Title }, { "Author", company }, { "Version", Regex.Replace(package.Version.ToString(), @"-.*$", "") }, { "Summary", package.Summary ?? package.Description ?? package.Id }, { "Codepage", $"{culture}" }, { "Platform", packageAs64Bit ? "x64" : "x86" }, { "ProgramFilesFolder", packageAs64Bit ? "ProgramFiles64Folder" : "ProgramFilesFolder" }, { "Win64YesNo", packageAs64Bit ? "yes" : "no" } }; // NB: We need some GUIDs that are based on the package ID, but unique (i.e. // "Unique but consistent"). for (int i=1; i <= 10; i++) { templateData[String.Format("IdAsGuid{0}", i)] = Utility.CreateGuidFromHash(String.Format("{0}:{1}", package.Id, i)).ToString(); } var templateResult = CopStache.Render(templateText, templateData); var wxsTarget = Path.Combine(setupExeDir, "Setup.wxs"); File.WriteAllText(wxsTarget, templateResult, Encoding.UTF8); var candleParams = String.Format("-nologo -ext WixNetFxExtension -out \"{0}\" \"{1}\"", wxsTarget.Replace(".wxs", ".wixobj"), wxsTarget); var processResult = await Utility.InvokeProcessAsync( Path.Combine(pathToWix, "candle.exe"), candleParams, CancellationToken.None, setupExeDir); if (processResult.Item1 != 0) { var msg = String.Format( "Failed to compile WiX template, command invoked was: '{0} {1}'\n\nOutput was:\n{2}", "candle.exe", candleParams, processResult.Item2); throw new Exception(msg); } var lightParams = String.Format("-ext WixNetFxExtension -sval -out \"{0}\" \"{1}\"", wxsTarget.Replace(".wxs", ".msi"), wxsTarget.Replace(".wxs", ".wixobj")); processResult = await Utility.InvokeProcessAsync( Path.Combine(pathToWix, "light.exe"), lightParams, CancellationToken.None, setupExeDir); if (processResult.Item1 != 0) { var msg = String.Format( "Failed to link WiX template, command invoked was: '{0} {1}'\n\nOutput was:\n{2}", "light.exe", lightParams, processResult.Item2); throw new Exception(msg); } var toDelete = new[] { wxsTarget, wxsTarget.Replace(".wxs", ".wixobj"), wxsTarget.Replace(".wxs", ".wixpdb"), }; await Utility.ForEachAsync(toDelete, x => Utility.DeleteFileHarder(x)); } static string pathToWixTools() { var ourPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); // Same Directory? (i.e. released) if (File.Exists(Path.Combine(ourPath, "candle.exe"))) { return ourPath; } // Debug Mode (i.e. in vendor) var debugPath = Path.Combine(ourPath, "..", "..", "..", "vendor", "wix"); if (File.Exists(Path.Combine(debugPath, "candle.exe"))) { return Path.GetFullPath(debugPath); } throw new Exception("WiX tools can't be found"); } static string getAppNameFromDirectory(string path = null) { path = path ?? Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); return (new DirectoryInfo(path)).Name; } static ShortcutLocation? parseShortcutLocations(string shortcutArgs) { var ret = default(ShortcutLocation?); if (!String.IsNullOrWhiteSpace(shortcutArgs)) { var args = shortcutArgs.Split(new[] { ',' }); foreach (var arg in args) { var location = (ShortcutLocation)(Enum.Parse(typeof(ShortcutLocation), arg, false)); if (ret.HasValue) { ret |= location; } else { ret = location; } } } return ret; } static int consoleCreated = 0; static void ensureConsole() { if (Environment.OSVersion.Platform != PlatformID.Win32NT) return; if (Interlocked.CompareExchange(ref consoleCreated, 1, 0) == 1) return; if (!NativeMethods.AttachConsole(-1)) { NativeMethods.AllocConsole(); } NativeMethods.GetStdHandle(StandardHandles.STD_ERROR_HANDLE); NativeMethods.GetStdHandle(StandardHandles.STD_OUTPUT_HANDLE); } } public class ProgressSource { public event EventHandler Progress; public void Raise(int i) { if (Progress != null) Progress.Invoke(this, i); } } class SetupLogLogger : SimpleSplat.ILogger, IDisposable { TextWriter inner; readonly object gate = 42; public SimpleSplat.LogLevel Level { get; set; } public SetupLogLogger(bool saveInTemp, string commandSuffix = null) { for (int i=0; i < 10; i++) { try { var dir = saveInTemp ? Path.GetTempPath() : Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); var fileName = commandSuffix == null ? String.Format($"Squirrel.{i}.log", i) : String.Format($"Squirrel-{commandSuffix}.{i}.log", i); var file = Path.Combine(dir, fileName.Replace(".0.log", ".log")); var str = File.Open(file, FileMode.Append, FileAccess.Write, FileShare.Read); inner = new StreamWriter(str, Encoding.UTF8, 4096, false) { AutoFlush = true }; return; } catch (Exception ex) { // Didn't work? Keep going Console.Error.WriteLine("Couldn't open log file, trying new file: " + ex.ToString()); } } inner = Console.Error; } public void Write(string message, LogLevel logLevel) { if (logLevel < Level) { return; } lock (gate) inner.WriteLine($"[{DateTime.Now.ToString("dd/MM/yy HH:mm:ss")}] {logLevel.ToString().ToLower()}: {message}"); } public void Dispose() { lock (gate) { inner.Flush(); inner.Dispose(); } } } } ================================================ FILE: src/Update/StartupOption.cs ================================================ using Mono.Options; using System; namespace Squirrel.Update { internal class StartupOption { private readonly OptionSet optionSet; internal bool silentInstall { get; private set; } = false; internal UpdateAction updateAction { get; private set; } = default(UpdateAction); internal string target { get; private set; } = default(string); internal string releaseDir { get; private set; } = default(string); internal string packagesDir { get; private set; } = default(string); internal string bootstrapperExe { get; private set; } = default(string); internal string backgroundGif { get; private set; } = default(string); internal string signingParameters { get; private set; } = default(string); internal string baseUrl { get; private set; } = default(string); internal string processStart { get; private set; } = default(string); internal string processStartArgs { get; private set; } = default(string); internal string setupIcon { get; private set; } = default(string); internal string icon { get; private set; } = default(string); internal string shortcutArgs { get; private set; } = default(string); internal string frameworkVersion { get; private set; } = "net45"; internal bool shouldWait { get; private set; } = false; internal bool noMsi { get; private set; } = (Environment.OSVersion.Platform != PlatformID.Win32NT); // NB: WiX doesn't work under Mono / Wine internal bool packageAs64Bit { get; private set; } = false; internal bool noDelta { get; private set; } = false; internal bool onlyUpdateShortcuts { get; private set; } = false; public StartupOption(string[] args) { optionSet = Parse(args); } private OptionSet Parse(string[] args) { var opts = new OptionSet() { "Usage: Squirrel.exe command [OPTS]", "Manages Squirrel packages", "", "Commands", { "install=", "Install the app whose package is in the specified directory", v => { updateAction = UpdateAction.Install; target = v; } }, { "uninstall", "Uninstall the app the same dir as Update.exe", v => updateAction = UpdateAction.Uninstall}, { "download=", "Download the releases specified by the URL and write new results to stdout as JSON", v => { updateAction = UpdateAction.Download; target = v; } }, { "checkForUpdate=", "Check for one available update and writes new results to stdout as JSON", v => { updateAction = UpdateAction.CheckForUpdate; target = v; } }, { "update=", "Update the application to the latest remote version specified by URL", v => { updateAction = UpdateAction.Update; target = v; } }, { "releasify=", "Update or generate a releases directory with a given NuGet package", v => { updateAction = UpdateAction.Releasify; target = v; } }, { "createShortcut=", "Create a shortcut for the given executable name", v => { updateAction = UpdateAction.Shortcut; target = v; } }, { "removeShortcut=", "Remove a shortcut for the given executable name", v => { updateAction = UpdateAction.Deshortcut; target = v; } }, { "updateSelf=", "Copy the currently executing Update.exe into the default location", v => { updateAction = UpdateAction.UpdateSelf; target = v; } }, { "processStart=", "Start an executable in the latest version of the app package", v => { updateAction = UpdateAction.ProcessStart; processStart = v; }, true}, { "processStartAndWait=", "Start an executable in the latest version of the app package", v => { updateAction = UpdateAction.ProcessStart; processStart = v; shouldWait = true; }, true}, "", "Options:", { "h|?|help", "Display Help and exit", _ => {} }, { "r=|releaseDir=", "Path to a release directory to use with releasify", v => releaseDir = v}, { "p=|packagesDir=", "Path to the NuGet Packages directory for C# apps", v => packagesDir = v}, { "bootstrapperExe=", "Path to the Setup.exe to use as a template", v => bootstrapperExe = v}, { "g=|loadingGif=", "Path to an animated GIF to be displayed during installation", v => backgroundGif = v}, { "i=|icon", "Path to an ICO file that will be used for icon shortcuts", v => icon = v}, { "setupIcon=", "Path to an ICO file that will be used for the Setup executable's icon", v => setupIcon = v}, { "n=|signWithParams=", "Sign the installer via SignTool.exe with the parameters given", v => signingParameters = v}, { "s|silent", "Silent install", _ => silentInstall = true}, { "b=|baseUrl=", "Provides a base URL to prefix the RELEASES file packages with", v => baseUrl = v, true}, { "a=|process-start-args=", "Arguments that will be used when starting executable", v => processStartArgs = v, true}, { "l=|shortcut-locations=", "Comma-separated string of shortcut locations, e.g. 'Desktop,StartMenu'", v => shortcutArgs = v}, { "no-msi", "Don't generate an MSI package", v => noMsi = true}, { "no-delta", "Don't generate delta packages to save time", v => noDelta = true}, { "framework-version=", "Set the required .NET framework version, e.g. net461", v => frameworkVersion = v }, { "msi-win64", "Mark the MSI as 64-bit, which is useful in Enterprise deployment scenarios", _ => packageAs64Bit = true}, { "updateOnly", "Update shortcuts that already exist, rather than creating new ones", _ => onlyUpdateShortcuts = true}, }; opts.Parse(args); // NB: setupIcon and icon are just aliases for compatibility // reasons, because of a dumb breaking rename I made in 1.0.1 setupIcon = setupIcon ?? icon; return opts; } internal void WriteOptionDescriptions() { optionSet.WriteOptionDescriptions(Console.Out); } } } ================================================ FILE: src/Update/Update-Mono.csproj ================================================  net45 Exe Update Update app.manifest $(DefineConstants);MONO cd "$(TargetDir)" "$(NuGetPackageRoot)ilrepack\1.26.0\tools\ILRepack.exe" /internalize /out:$(TargetFileName).tmp $(TargetFileName) Microsoft.Web.XmlTransform.dll Squirrel.dll NuGet.Squirrel.dll Mono.Cecil.dll SharpCompress.dll del "$(TargetFileName)" ren "$(TargetFileName).tmp" "$(TargetFileName)" ================================================ FILE: src/Update/Update.csproj ================================================  net45 WinExe Update Update app.manifest cd "$(TargetDir)" "$(NuGetPackageRoot)ilrepack\1.26.0\tools\ILRepack.exe" /internalize /out:$(TargetFileName).tmp $(TargetFileName) WpfAnimatedGif.dll SharpCompress.dll Microsoft.Web.XmlTransform.dll Squirrel.dll NuGet.Squirrel.dll Mono.Cecil.dll del "$(TargetFileName)" ren "$(TargetFileName).tmp" "$(TargetFileName)" ================================================ FILE: src/Update/app.manifest ================================================  ================================================ FILE: src/Update/packages.config ================================================  ================================================ FILE: src/WriteZipToSetup/WriteZipToSetup.cpp ================================================ // WriteZipToSetup.cpp : Defines the entry point for the console application. // #include "stdafx.h" using namespace std; BOOL CALLBACK EnumResLangProc(HMODULE hModule, LPCTSTR lpszType, LPCTSTR lpszName, WORD wIDLanguage, LONG_PTR lParam) { HANDLE hUpdate = (HANDLE)lParam; HRSRC hFindItAgain = FindResourceEx(hModule, lpszType, lpszName, wIDLanguage); HGLOBAL hGlobal = LoadResource(hModule, hFindItAgain); if (!hGlobal) return true; UpdateResource(hUpdate, lpszType, lpszName, wIDLanguage, LockResource(hGlobal), SizeofResource(hModule, hFindItAgain)); return true; } BOOL CALLBACK EnumResNameProc(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam) { HANDLE hUpdate = (HANDLE)lParam; EnumResourceLanguages(hModule, lpszType, lpszName, EnumResLangProc, (LONG_PTR)hUpdate); return true; } BOOL CALLBACK EnumResTypeProc(HMODULE hMod, LPTSTR lpszType, LONG_PTR lParam) { std::vector* typeList = (std::vector*)lParam; if (IS_INTRESOURCE(lpszType)) { typeList->push_back(lpszType); } else { typeList->push_back(_wcsdup(lpszType)); } return true; } int CopyResourcesToStubExecutable(wchar_t* src, wchar_t* dest) { HMODULE hSrc = LoadLibraryEx(src, NULL, LOAD_LIBRARY_AS_DATAFILE); if (!hSrc) return GetLastError(); HANDLE hUpdate = BeginUpdateResource(dest, true); if (!hUpdate) return GetLastError(); std::vector typeList; EnumResourceTypes(hSrc, EnumResTypeProc, (LONG_PTR)&typeList); for (auto& type : typeList) { EnumResourceNames(hSrc, type, EnumResNameProc, (LONG_PTR)hUpdate); } EndUpdateResource(hUpdate, false); return 0; } int wmain(int argc, wchar_t* argv[]) { if (argc > 1 && wcscmp(argv[1], L"--copy-stub-resources") == 0) { if (argc != 4) goto fail; return CopyResourcesToStubExecutable(argv[2], argv[3]); } bool setFramework = false; if (argc == 5 && wcscmp(argv[3], L"--set-required-framework") == 0) { setFramework = true; } else if (argc != 3) { goto fail; } wprintf(L"Setup: %s, Zip: %s\n", argv[1], argv[2]); // Read the entire zip file into memory, yolo HANDLE hFile = CreateFile(argv[2], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("Can't open Zip file\n"); return -1; } BY_HANDLE_FILE_INFORMATION fileInfo; if (!GetFileInformationByHandle(hFile, &fileInfo)) { goto fail; } BYTE* pBuf = new BYTE[fileInfo.nFileSizeLow + 0x1000]; BYTE* pCurrent = pBuf; DWORD dwBytesRead; printf("Starting to read in Zip file!\n"); do { if (!ReadFile(hFile, pCurrent, 0x1000, &dwBytesRead, NULL)) { printf("Failed to read file! 0x%u\n", GetLastError()); goto fail; } pCurrent += dwBytesRead; } while (dwBytesRead > 0); printf("Updating Resource!\n"); HANDLE hRes = BeginUpdateResource(argv[1], false); if (!hRes) { printf("Couldn't open setup.exe for writing\n"); goto fail; } if (!UpdateResource(hRes, L"DATA", (LPCWSTR)131, 0x0409, pBuf, fileInfo.nFileSizeLow)) { printf("Failed to update resource\n"); goto fail; } if (setFramework) { if (!UpdateResource(hRes, L"FLAGS", (LPCWSTR)132, 0x0409, argv[4], (wcslen(argv[4])+1) * sizeof(wchar_t))) { printf("Failed to update resource\n"); goto fail; } } printf("Finished!\n"); if (!EndUpdateResource(hRes, false)) { printf("Failed to update resource\n"); goto fail; } printf("It worked!\n"); return 0; fail: printf("Usage: WriteZipToSetup [Setup.exe template] [Zip File]\n"); return -1; } ================================================ FILE: src/WriteZipToSetup/WriteZipToSetup.vcxproj ================================================  Debug Win32 Release Win32 {4D3C8B70-075D-48A5-9FF3-EDB87347B136} Application Win32Proj WriteZipToSetup v142 Unicode 10.0 true false true true false Use Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDebug Console true Level3 Use MinSpace true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Size MultiThreaded Console true true true Create ================================================ FILE: src/WriteZipToSetup/WriteZipToSetup.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Source Files Source Files ================================================ FILE: src/WriteZipToSetup/stdafx.cpp ================================================ // stdafx.cpp : source file that includes just the standard includes // WriteZipToSetup.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" // TODO: reference any additional headers you need in STDAFX.H // and not in this file ================================================ FILE: src/WriteZipToSetup/stdafx.h ================================================ // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #pragma once #include #include #define WIN32_LEAN_AND_MEAN #include // C RunTime Header Files #include #include #include #include #include #include #include // TODO: reference additional headers your program requires here ================================================ FILE: src/build_official.cmd ================================================ @echo off setlocal pushd %~dp0 :parse_args if not "%1"=="" shift & goto parse_args :: Init if "%VCToolsVersion%"=="" call :StartDeveloperCommandPrompt || exit /b :: Clean rd /s /q ..\build ..\packages 2> nul :: Build nuget restore ..\Squirrel.sln || exit /b msbuild -Restore ..\Squirrel.sln -p:Configuration=Release -v:m -m -nr:false -bl:..\build\logs\build.binlog || exit /b :: Pack .nupkg nuget pack Squirrel.nuspec -OutputDirectory ..\build\artifacts || exit /b :: Layout electron-winstaller :: :: The NPM package electron-winstaller allows developers to :: build Windows installers for Electron apps using Squirrel :: (https://github.com/electron/windows-installer) :: :: The following copies the required files into a single folder :: which can then be copied to the electron-winstaller/vendor folder :: (either manually or in an automated way). md ..\build\artifacts\electron-winstaller\vendor copy ..\build\Release\net45\Update.exe ..\build\artifacts\electron-winstaller\vendor\Squirrel.exe || exit /b copy ..\build\Release\net45\update.com ..\build\artifacts\electron-winstaller\vendor\Squirrel.com || exit /b copy ..\build\Release\net45\Update.pdb ..\build\artifacts\electron-winstaller\vendor\Squirrel.pdb || exit /b copy ..\build\Release\Win32\Setup.exe ..\build\artifacts\electron-winstaller\vendor || exit /b copy ..\build\Release\Win32\Setup.pdb ..\build\artifacts\electron-winstaller\vendor || exit /b copy ..\build\Release\net45\Update-Mono.exe ..\build\artifacts\electron-winstaller\vendor\Squirrel-Mono.exe || exit /b copy ..\build\Release\net45\Update-Mono.pdb ..\build\artifacts\electron-winstaller\vendor\Squirrel-Mono.pdb || exit /b copy ..\build\Release\Win32\StubExecutable.exe ..\build\artifacts\electron-winstaller\vendor || exit /b copy ..\build\Release\net45\SyncReleases.exe ..\build\artifacts\electron-winstaller\vendor || exit /b copy ..\build\Release\net45\SyncReleases.pdb ..\build\artifacts\electron-winstaller\vendor || exit /b copy ..\build\Release\Win32\WriteZipToSetup.exe ..\build\artifacts\electron-winstaller\vendor || exit /b copy ..\build\Release\Win32\WriteZipToSetup.pdb ..\build\artifacts\electron-winstaller\vendor || exit /b goto LExit :StartDeveloperCommandPrompt if not "%SquirrelSkipVsDevCmd%"=="" ( echo Skipping initializing developer command prompt exit /b ) echo Initializing developer command prompt if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" ( "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" exit /b 2 ) for /f "usebackq delims=" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version [16.0^,18.0^) -property installationPath`) do ( if exist "%%i\Common7\Tools\vsdevcmd.bat" ( call "%%i\Common7\Tools\vsdevcmd.bat" -no_logo exit /b ) echo developer command prompt not found in %%i ) echo No versions of developer command prompt found exit /b 2 :LExit popd endlocal ================================================ FILE: src/squirrel.windows.props ================================================ $([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)..\tools')) ================================================ FILE: test/Directory.Build.props ================================================ $(BaseOutputPath)$(Configuration)\test\ ================================================ FILE: test/Squirrel.Tests/ApplyReleasesProgressTests.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Xunit; namespace Squirrel.Tests { public class ApplyReleasesProgressTests { [Fact(Skip = "Test does not pass consistently due to dependency on Task.Delay()")] public async Task CalculatesPercentageCorrectly() { // Just 1 complex situation should be enough to cover this var percentage = 0; var progress = new ApplyReleasesProgress(5, x => percentage = x); // 2 releases already finished progress.FinishRelease(); progress.FinishRelease(); // Report 40 % in current release progress.ReportReleaseProgress(50); // Required for callback to be invoked await Task.Delay(50); // 20 per release // 10 because we are half-way the 3rd release var expectedProgress = 20 + 20 + 10; Assert.Equal(expectedProgress, percentage); } } } ================================================ FILE: test/Squirrel.Tests/ApplyReleasesTests.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using NuGet; using Squirrel.SimpleSplat; using Squirrel; using Squirrel.Tests.TestHelpers; using Xunit; namespace Squirrel.Tests { public class FakeUrlDownloader : IFileDownloader { public Task DownloadUrl(string url) { return Task.FromResult(new byte[0]); } public async Task DownloadFile(string url, string targetFile, Action progress) { } } public class ApplyReleasesTests : IEnableLogger { [Fact] public async Task CleanInstallRunsSquirrelAwareAppsWithInstallFlag() { string tempDir; string remotePkgDir; using (Utility.WithTempDirectory(out tempDir)) using (Utility.WithTempDirectory(out remotePkgDir)) { IntegrationTestHelper.CreateFakeInstalledApp("0.1.0", remotePkgDir); var pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.FullInstall(); // NB: We execute the Squirrel-aware apps, so we need to give // them a minute to settle or else the using statement will // try to blow away a running process await Task.Delay(1000); Assert.False(File.Exists(Path.Combine(tempDir, "theApp", "app-0.1.0", "args2.txt"))); Assert.True(File.Exists(Path.Combine(tempDir, "theApp", "app-0.1.0", "args.txt"))); var text = File.ReadAllText(Path.Combine(tempDir, "theApp", "app-0.1.0", "args.txt"), Encoding.UTF8); Assert.Contains("firstrun", text); } } } [Fact] public async Task UpgradeRunsSquirrelAwareAppsWithUpgradeFlag() { string tempDir; string remotePkgDir; using (Utility.WithTempDirectory(out tempDir)) using (Utility.WithTempDirectory(out remotePkgDir)) { IntegrationTestHelper.CreateFakeInstalledApp("0.1.0", remotePkgDir); var pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.FullInstall(); } await Task.Delay(1000); IntegrationTestHelper.CreateFakeInstalledApp("0.2.0", remotePkgDir); pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.UpdateApp(); } await Task.Delay(1000); Assert.False(File.Exists(Path.Combine(tempDir, "theApp", "app-0.2.0", "args2.txt"))); Assert.True(File.Exists(Path.Combine(tempDir, "theApp", "app-0.2.0", "args.txt"))); var text = File.ReadAllText(Path.Combine(tempDir, "theApp", "app-0.2.0", "args.txt"), Encoding.UTF8); Assert.Contains("updated|0.2.0", text); } } [Fact] public async Task RunningUpgradeAppTwiceDoesntCrash() { string tempDir; string remotePkgDir; using (Utility.WithTempDirectory(out tempDir)) using (Utility.WithTempDirectory(out remotePkgDir)) { IntegrationTestHelper.CreateFakeInstalledApp("0.1.0", remotePkgDir); var pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.FullInstall(); } await Task.Delay(1000); IntegrationTestHelper.CreateFakeInstalledApp("0.2.0", remotePkgDir); pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.UpdateApp(); } await Task.Delay(1000); // NB: The 2nd time we won't have any updates to apply. We should just do nothing! using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.UpdateApp(); } await Task.Delay(1000); } } [Fact] public async Task FullUninstallRemovesAllVersions() { string tempDir; string remotePkgDir; using (Utility.WithTempDirectory(out tempDir)) using (Utility.WithTempDirectory(out remotePkgDir)) { IntegrationTestHelper.CreateFakeInstalledApp("0.1.0", remotePkgDir); var pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.FullInstall(); } await Task.Delay(1000); IntegrationTestHelper.CreateFakeInstalledApp("0.2.0", remotePkgDir); pkgs = ReleaseEntry.BuildReleasesFile(remotePkgDir); ReleaseEntry.WriteReleaseFile(pkgs, Path.Combine(remotePkgDir, "RELEASES")); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.UpdateApp(); } await Task.Delay(1000); using (var fixture = new UpdateManager(remotePkgDir, "theApp", tempDir)) { await fixture.FullUninstall(); } Assert.False(File.Exists(Path.Combine(tempDir, "theApp", "app-0.1.0", "args.txt"))); Assert.False(File.Exists(Path.Combine(tempDir, "theApp", "app-0.2.0", "args.txt"))); Assert.True(File.Exists(Path.Combine(tempDir, "theApp", ".dead"))); } } [Fact] public void WhenNoNewReleasesAreAvailableTheListIsEmpty() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var appDir = Directory.CreateDirectory(Path.Combine(tempDir, "theApp")); var packages = Path.Combine(appDir.FullName, "packages"); Directory.CreateDirectory(packages); var package = "Squirrel.Core.1.0.0.0-full.nupkg"; File.Copy(IntegrationTestHelper.GetPath("fixtures", package), Path.Combine(packages, package)); var aGivenPackage = Path.Combine(packages, package); var baseEntry = ReleaseEntry.GenerateFromFile(aGivenPackage); var updateInfo = UpdateInfo.Create(baseEntry, new[] { baseEntry }, "dontcare"); Assert.Empty(updateInfo.ReleasesToApply); } } [Fact] public void ThrowsWhenOnlyDeltaReleasesAreAvailable() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var appDir = Directory.CreateDirectory(Path.Combine(tempDir, "theApp")); var packages = Path.Combine(appDir.FullName, "packages"); Directory.CreateDirectory(packages); var baseFile = "Squirrel.Core.1.0.0.0-full.nupkg"; File.Copy(IntegrationTestHelper.GetPath("fixtures", baseFile), Path.Combine(packages, baseFile)); var basePackage = Path.Combine(packages, baseFile); var baseEntry = ReleaseEntry.GenerateFromFile(basePackage); var deltaFile = "Squirrel.Core.1.1.0.0-delta.nupkg"; File.Copy(IntegrationTestHelper.GetPath("fixtures", deltaFile), Path.Combine(packages, deltaFile)); var deltaPackage = Path.Combine(packages, deltaFile); var deltaEntry = ReleaseEntry.GenerateFromFile(deltaPackage); Assert.Throws( () => UpdateInfo.Create(baseEntry, new[] { deltaEntry }, "dontcare")); } } [Fact] public async Task ApplyReleasesWithOneReleaseFile() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { string appDir = Path.Combine(tempDir, "theApp"); string packagesDir = Path.Combine(appDir, "packages"); Directory.CreateDirectory(packagesDir); new[] { "Squirrel.Core.1.0.0.0-full.nupkg", "Squirrel.Core.1.1.0.0-full.nupkg", }.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x))); var fixture = new UpdateManager.ApplyReleasesImpl(appDir); var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.0.0.0-full.nupkg")); var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-full.nupkg")); var updateInfo = UpdateInfo.Create(baseEntry, new[] { latestFullEntry }, packagesDir); updateInfo.ReleasesToApply.Contains(latestFullEntry).ShouldBeTrue(); var progress = new List(); await fixture.ApplyReleases(updateInfo, false, false, progress.Add); this.Log().Info("Progress: [{0}]", String.Join(",", progress)); progress .Aggregate(0, (acc, x) => { (x >= acc).ShouldBeTrue(); return x; }) .ShouldEqual(100); var filesToFind = new[] { new {Name = "NLog.dll", Version = new Version("2.0.0.0")}, new {Name = "NSync.Core.dll", Version = new Version("1.1.0.0")}, }; filesToFind.ForEach(x => { var path = Path.Combine(tempDir, "theApp", "app-1.1.0.0", x.Name); this.Log().Info("Looking for {0}", path); File.Exists(path).ShouldBeTrue(); var vi = FileVersionInfo.GetVersionInfo(path); var verInfo = new Version(vi.FileVersion ?? "1.0.0.0"); x.Version.ShouldEqual(verInfo); }); } } [Fact] public async Task ApplyReleaseWhichRemovesAFile() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { string appDir = Path.Combine(tempDir, "theApp"); string packagesDir = Path.Combine(appDir, "packages"); Directory.CreateDirectory(packagesDir); new[] { "Squirrel.Core.1.1.0.0-full.nupkg", "Squirrel.Core.1.2.0.0-full.nupkg", }.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x))); var fixture = new UpdateManager.ApplyReleasesImpl(appDir); var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-full.nupkg")); var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.2.0.0-full.nupkg")); var updateInfo = UpdateInfo.Create(baseEntry, new[] { latestFullEntry }, packagesDir); updateInfo.ReleasesToApply.Contains(latestFullEntry).ShouldBeTrue(); var progress = new List(); await fixture.ApplyReleases(updateInfo, false, false, progress.Add); this.Log().Info("Progress: [{0}]", String.Join(",", progress)); progress .Aggregate(0, (acc, x) => { (x >= acc).ShouldBeTrue(); return x; }) .ShouldEqual(100); var rootDirectory = Path.Combine(tempDir, "theApp", "app-1.2.0.0"); new[] { new {Name = "NLog.dll", Version = new Version("2.0.0.0")}, new {Name = "NSync.Core.dll", Version = new Version("1.1.0.0")}, }.ForEach(x => { var path = Path.Combine(rootDirectory, x.Name); this.Log().Info("Looking for {0}", path); File.Exists(path).ShouldBeTrue(); }); var removedFile = Path.Combine("sub", "Ionic.Zip.dll"); var deployedPath = Path.Combine(rootDirectory, removedFile); File.Exists(deployedPath).ShouldBeFalse(); } } [Fact] public async Task ApplyReleaseWhichMovesAFileToADifferentDirectory() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { string appDir = Path.Combine(tempDir, "theApp"); string packagesDir = Path.Combine(appDir, "packages"); Directory.CreateDirectory(packagesDir); new[] { "Squirrel.Core.1.1.0.0-full.nupkg", "Squirrel.Core.1.3.0.0-full.nupkg", }.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x))); var fixture = new UpdateManager.ApplyReleasesImpl(appDir); var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-full.nupkg")); var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.3.0.0-full.nupkg")); var updateInfo = UpdateInfo.Create(baseEntry, new[] { latestFullEntry }, packagesDir); updateInfo.ReleasesToApply.Contains(latestFullEntry).ShouldBeTrue(); var progress = new List(); await fixture.ApplyReleases(updateInfo, false, false, progress.Add); this.Log().Info("Progress: [{0}]", String.Join(",", progress)); progress .Aggregate(0, (acc, x) => { (x >= acc).ShouldBeTrue(); return x; }) .ShouldEqual(100); var rootDirectory = Path.Combine(tempDir, "theApp", "app-1.3.0.0"); new[] { new {Name = "NLog.dll", Version = new Version("2.0.0.0")}, new {Name = "NSync.Core.dll", Version = new Version("1.1.0.0")}, }.ForEach(x => { var path = Path.Combine(rootDirectory, x.Name); this.Log().Info("Looking for {0}", path); File.Exists(path).ShouldBeTrue(); }); var oldFile = Path.Combine(rootDirectory, "sub", "Ionic.Zip.dll"); File.Exists(oldFile).ShouldBeFalse(); var newFile = Path.Combine(rootDirectory, "other", "Ionic.Zip.dll"); File.Exists(newFile).ShouldBeTrue(); } } [Fact] public async Task ApplyReleasesWithDeltaReleases() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { string appDir = Path.Combine(tempDir, "theApp"); string packagesDir = Path.Combine(appDir, "packages"); Directory.CreateDirectory(packagesDir); new[] { "Squirrel.Core.1.0.0.0-full.nupkg", "Squirrel.Core.1.1.0.0-delta.nupkg", "Squirrel.Core.1.1.0.0-full.nupkg", }.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(packagesDir, x))); var fixture = new UpdateManager.ApplyReleasesImpl(appDir); var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.0.0.0-full.nupkg")); var deltaEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-delta.nupkg")); var latestFullEntry = ReleaseEntry.GenerateFromFile(Path.Combine(packagesDir, "Squirrel.Core.1.1.0.0-full.nupkg")); var updateInfo = UpdateInfo.Create(baseEntry, new[] { deltaEntry, latestFullEntry }, packagesDir); updateInfo.ReleasesToApply.Contains(deltaEntry).ShouldBeTrue(); var progress = new List(); await fixture.ApplyReleases(updateInfo, false, false, progress.Add); this.Log().Info("Progress: [{0}]", String.Join(",", progress)); progress .Aggregate(0, (acc, x) => { (x >= acc).ShouldBeTrue(); return x; }) .ShouldEqual(100); var filesToFind = new[] { new {Name = "NLog.dll", Version = new Version("2.0.0.0")}, new {Name = "NSync.Core.dll", Version = new Version("1.1.0.0")}, }; filesToFind.ForEach(x => { var path = Path.Combine(tempDir, "theApp", "app-1.1.0.0", x.Name); this.Log().Info("Looking for {0}", path); File.Exists(path).ShouldBeTrue(); var vi = FileVersionInfo.GetVersionInfo(path); var verInfo = new Version(vi.FileVersion ?? "1.0.0.0"); x.Version.ShouldEqual(verInfo); }); } } [Fact] public async Task CreateFullPackagesFromDeltaSmokeTest() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { string appDir = Path.Combine(tempDir, "theApp"); string packagesDir = Path.Combine(appDir, "packages"); Directory.CreateDirectory(packagesDir); new[] { "Squirrel.Core.1.0.0.0-full.nupkg", "Squirrel.Core.1.1.0.0-delta.nupkg" }.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(tempDir, "theApp", "packages", x))); var urlDownloader = new FakeUrlDownloader(); var fixture = new UpdateManager.ApplyReleasesImpl(appDir); var baseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(tempDir, "theApp", "packages", "Squirrel.Core.1.0.0.0-full.nupkg")); var deltaEntry = ReleaseEntry.GenerateFromFile(Path.Combine(tempDir, "theApp", "packages", "Squirrel.Core.1.1.0.0-delta.nupkg")); var resultObs = (Task)fixture.GetType().GetMethod("createFullPackagesFromDeltas", BindingFlags.NonPublic | BindingFlags.Instance) .Invoke(fixture, new object[] { new[] {deltaEntry}, baseEntry, null }); var result = await resultObs; var zp = new ZipPackage(Path.Combine(tempDir, "theApp", "packages", result.Filename)); zp.Version.ToString().ShouldEqual("1.1.0.0"); } } [Fact] public async Task CreateShortcutsRoundTrip() { string remotePkgPath; string path; using (Utility.WithTempDirectory(out path)) { using (Utility.WithTempDirectory(out remotePkgPath)) using (var mgr = new UpdateManager(remotePkgPath, "theApp", path)) { IntegrationTestHelper.CreateFakeInstalledApp("1.0.0.1", remotePkgPath); await mgr.FullInstall(); } var fixture = new UpdateManager.ApplyReleasesImpl(Path.Combine(path, "theApp")); fixture.CreateShortcutsForExecutable("SquirrelAwareApp.exe", ShortcutLocation.Desktop | ShortcutLocation.StartMenu | ShortcutLocation.Startup | ShortcutLocation.AppRoot, false, null, null); // NB: COM is Weird. Thread.Sleep(1000); fixture.RemoveShortcutsForExecutable("SquirrelAwareApp.exe", ShortcutLocation.Desktop | ShortcutLocation.StartMenu | ShortcutLocation.Startup | ShortcutLocation.AppRoot); // NB: Squirrel-Aware first-run might still be running, slow // our roll before blowing away the temp path Thread.Sleep(1000); } } [Fact] public void UnshimOurselvesSmokeTest() { // NB: This smoke test is really more of a manual test - try it // by shimming Slack, then verifying the shim goes away var appDir = Environment.ExpandEnvironmentVariables(@"%LocalAppData%\Slack"); var fixture = new UpdateManager.ApplyReleasesImpl(appDir); fixture.unshimOurselves(); } [Fact(Skip = "This test is currently failing in CI")] public async Task GetShortcutsSmokeTest() { string remotePkgPath; string path; using (Utility.WithTempDirectory(out path)) { using (Utility.WithTempDirectory(out remotePkgPath)) using (var mgr = new UpdateManager(remotePkgPath, "theApp", path)) { IntegrationTestHelper.CreateFakeInstalledApp("1.0.0.1", remotePkgPath); await mgr.FullInstall(); } var fixture = new UpdateManager.ApplyReleasesImpl(Path.Combine(path, "theApp")); var result = fixture.GetShortcutsForExecutable("SquirrelAwareApp.exe", ShortcutLocation.Desktop | ShortcutLocation.StartMenu | ShortcutLocation.Startup, null); Assert.Equal(3, result.Keys.Count); // NB: Squirrel-Aware first-run might still be running, slow // our roll before blowing away the temp path Thread.Sleep(1000); } } } } ================================================ FILE: test/Squirrel.Tests/CheckForUpdateTests.cs ================================================ using System; using System.IO; using System.Linq; using System.Text; using Squirrel.Tests.TestHelpers; using Xunit; namespace Squirrel.Tests { public class CheckForUpdateTests { [Fact(Skip = "Rewrite this to be an integration test")] public void NewReleasesShouldBeDetected() { Assert.False(true, "Rewrite this to be an integration test"); /* string localReleasesFile = Path.Combine(".", "theApp", "packages", "RELEASES"); var fileInfo = new Mock(); fileInfo.Setup(x => x.OpenRead()) .Returns(File.OpenRead(IntegrationTestHelper.GetPath("fixtures", "RELEASES-OnePointOh"))); var fs = new Mock(); fs.Setup(x => x.GetFileInfo(localReleasesFile)).Returns(fileInfo.Object); var urlDownloader = new Mock(); var dlPath = IntegrationTestHelper.GetPath("fixtures", "RELEASES-OnePointOne"); urlDownloader.Setup(x => x.DownloadUrl(It.IsAny(), It.IsAny>())) .Returns(Observable.Return(File.ReadAllText(dlPath, Encoding.UTF8))); var fixture = new UpdateManager("http://lol", "theApp", ".", fs.Object, urlDownloader.Object); var result = default(UpdateInfo); using (fixture) { result = fixture.CheckForUpdate().First(); } Assert.NotNull(result); Assert.Equal(1, result.ReleasesToApply.Single().Version.Major); Assert.Equal(1, result.ReleasesToApply.Single().Version.Minor); */ } [Fact(Skip = "Rewrite this to be an integration test")] public void CorruptedReleaseFileMeansWeStartFromScratch() { Assert.False(true, "Rewrite this to be an integration test"); /* string localPackagesDir = Path.Combine(".", "theApp", "packages"); string localReleasesFile = Path.Combine(localPackagesDir, "RELEASES"); var fileInfo = new Mock(); fileInfo.Setup(x => x.Exists).Returns(true); fileInfo.Setup(x => x.OpenRead()) .Returns(new MemoryStream(Encoding.UTF8.GetBytes("lol this isn't right"))); var dirInfo = new Mock(); dirInfo.Setup(x => x.Exists).Returns(true); var fs = new Mock(); fs.Setup(x => x.GetFileInfo(localReleasesFile)).Returns(fileInfo.Object); fs.Setup(x => x.CreateDirectoryRecursive(localPackagesDir)).Verifiable(); fs.Setup(x => x.DeleteDirectoryRecursive(localPackagesDir)).Verifiable(); fs.Setup(x => x.GetDirectoryInfo(localPackagesDir)).Returns(dirInfo.Object); var urlDownloader = new Mock(); var dlPath = IntegrationTestHelper.GetPath("fixtures", "RELEASES-OnePointOne"); urlDownloader.Setup(x => x.DownloadUrl(It.IsAny(), It.IsAny>())) .Returns(Observable.Return(File.ReadAllText(dlPath, Encoding.UTF8))); var fixture = new UpdateManager("http://lol", "theApp", ".", fs.Object, urlDownloader.Object); using (fixture) { fixture.CheckForUpdate().First(); } fs.Verify(x => x.CreateDirectoryRecursive(localPackagesDir), Times.Once()); fs.Verify(x => x.DeleteDirectoryRecursive(localPackagesDir), Times.Once()); */ } [Fact(Skip = "Rewrite this to be an integration test")] public void CorruptRemoteFileShouldThrowOnCheck() { Assert.False(true, "Rewrite this to be an integration test"); /* string localPackagesDir = Path.Combine(".", "theApp", "packages"); string localReleasesFile = Path.Combine(localPackagesDir, "RELEASES"); var fileInfo = new Mock(); fileInfo.Setup(x => x.Exists).Returns(false); var dirInfo = new Mock(); dirInfo.Setup(x => x.Exists).Returns(true); var fs = new Mock(); fs.Setup(x => x.GetFileInfo(localReleasesFile)).Returns(fileInfo.Object); fs.Setup(x => x.CreateDirectoryRecursive(localPackagesDir)).Verifiable(); fs.Setup(x => x.DeleteDirectoryRecursive(localPackagesDir)).Verifiable(); fs.Setup(x => x.GetDirectoryInfo(localPackagesDir)).Returns(dirInfo.Object); var urlDownloader = new Mock(); urlDownloader.Setup(x => x.DownloadUrl(It.IsAny(), It.IsAny>())) .Returns(Observable.Return("lol this isn't right")); var fixture = new UpdateManager("http://lol", "theApp", ".", fs.Object, urlDownloader.Object); using (fixture) { Assert.Throws(() => fixture.CheckForUpdate().First()); } */ } [Fact(Skip = "TODO")] public void IfLocalVersionGreaterThanRemoteWeRollback() { throw new NotImplementedException(); } [Fact(Skip = "TODO")] public void IfLocalAndRemoteAreEqualThenDoNothing() { throw new NotImplementedException(); } } } ================================================ FILE: test/Squirrel.Tests/ContentTypeTests.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; using Squirrel; using Squirrel.Tests.TestHelpers; using Xunit; namespace Squirrel.Tests.Core { public class ContentTypeTests { [Theory(Skip = "This test is currently failing in CI")] [InlineData("basic.xml", "basic-merged.xml")] [InlineData("complex.xml", "complex-merged.xml")] public void MergeContentTypes(string inputFileName, string expectedFileName) { var inputFile = IntegrationTestHelper.GetPath("fixtures", "content-types", inputFileName); var expectedFile = IntegrationTestHelper.GetPath("fixtures", "content-types", expectedFileName); var tempFile = Path.GetTempFileName() + ".xml"; var expected = new XmlDocument(); expected.Load(expectedFile); var existingTypes = GetContentTypes(expected); try { File.Copy(inputFile, tempFile); var actual = new XmlDocument(); actual.Load(tempFile); ContentType.Merge(actual); var actualTypes = GetContentTypes(actual); Assert.Equal(existingTypes, actualTypes); } finally { File.Delete(tempFile); } } static IEnumerable GetContentTypes(XmlNode doc) { var expectedTypesElement = doc.FirstChild.NextSibling; return expectedTypesElement.ChildNodes.OfType(); } } } ================================================ FILE: test/Squirrel.Tests/DeltaPackageTests.cs ================================================ using System; using System.IO; using System.Linq; using System.Threading.Tasks; using NuGet; using Squirrel; using Squirrel.SimpleSplat; using Squirrel.Tests.TestHelpers; using Xunit; namespace Squirrel.Tests.Core { public class ApplyDeltaPackageTests : IEnableLogger { [Fact] public void ApplyDeltaPackageSmokeTest() { var basePackage = new ReleasePackage(IntegrationTestHelper.GetPath("fixtures", "Squirrel.Core.1.0.0.0-full.nupkg")); var deltaPackage = new ReleasePackage(IntegrationTestHelper.GetPath("fixtures", "Squirrel.Core.1.1.0.0-delta.nupkg")); var expectedPackageFile = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Core.1.1.0.0-full.nupkg"); var outFile = Path.GetTempFileName() + ".nupkg"; try { var deltaBuilder = new DeltaPackageBuilder(); deltaBuilder.ApplyDeltaPackage(basePackage, deltaPackage, outFile); var result = new ZipPackage(outFile); var expected = new ZipPackage(expectedPackageFile); result.Id.ShouldEqual(expected.Id); result.Version.ShouldEqual(expected.Version); this.Log().Info("Expected file list:"); var expectedList = expected.GetFiles().Select(x => x.Path).OrderBy(x => x).ToList(); expectedList.ForEach(x => this.Log().Info(x)); this.Log().Info("Actual file list:"); var actualList = result.GetFiles().Select(x => x.Path).OrderBy(x => x).ToList(); actualList.ForEach(x => this.Log().Info(x)); Enumerable.Zip(expectedList, actualList, (e, a) => e == a) .All(x => x != false) .ShouldBeTrue(); } finally { if (File.Exists(outFile)) { File.Delete(outFile); } } } [Fact] public void ApplyDeltaWithBothBsdiffAndNormalDiffDoesntFail() { var basePackage = new ReleasePackage(IntegrationTestHelper.GetPath("fixtures", "slack-1.1.8-full.nupkg")); var deltaPackage = new ReleasePackage(IntegrationTestHelper.GetPath("fixtures", "slack-1.2.0-delta.nupkg")); var outFile = Path.GetTempFileName() + ".nupkg"; try { var deltaBuilder = new DeltaPackageBuilder(); deltaBuilder.ApplyDeltaPackage(basePackage, deltaPackage, outFile); var result = new ZipPackage(outFile); result.Id.ShouldEqual("slack"); result.Version.ShouldEqual(new SemanticVersion("1.2.0")); } finally { if (File.Exists(outFile)) { File.Delete(outFile); } } } [Fact(Skip = "Rewrite this test, the original uses too many heavyweight fixtures")] public void ApplyMultipleDeltaPackagesGeneratesCorrectHash() { Assert.True(false, "Rewrite this test, the original uses too many heavyweight fixtures"); } } public class CreateDeltaPackageTests : IEnableLogger { [Fact] public void CreateDeltaPackageIntegrationTest() { var basePackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Tests.0.1.0-pre.nupkg"); var newPackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Tests.0.2.0-pre.nupkg"); var sourceDir = IntegrationTestHelper.GetPath("fixtures", "packages"); (new DirectoryInfo(sourceDir)).Exists.ShouldBeTrue(); var baseFixture = new ReleasePackage(basePackage); var fixture = new ReleasePackage(newPackage); var tempFiles = Enumerable.Range(0, 3) .Select(_ => Path.GetTempPath() + Guid.NewGuid().ToString() + ".nupkg") .ToArray(); try { baseFixture.CreateReleasePackage(tempFiles[0], sourceDir); fixture.CreateReleasePackage(tempFiles[1], sourceDir); (new FileInfo(baseFixture.ReleasePackageFile)).Exists.ShouldBeTrue(); (new FileInfo(fixture.ReleasePackageFile)).Exists.ShouldBeTrue(); var deltaBuilder = new DeltaPackageBuilder(); deltaBuilder.CreateDeltaPackage(baseFixture, fixture, tempFiles[2]); var fullPkg = new ZipPackage(tempFiles[1]); var deltaPkg = new ZipPackage(tempFiles[2]); // // Package Checks // fullPkg.Id.ShouldEqual(deltaPkg.Id); fullPkg.Version.CompareTo(deltaPkg.Version).ShouldEqual(0); // Delta packages should be smaller than the original! var fileInfos = tempFiles.Select(x => new FileInfo(x)).ToArray(); this.Log().Info("Base Size: {0}, Current Size: {1}, Delta Size: {2}", fileInfos[0].Length, fileInfos[1].Length, fileInfos[2].Length); (fileInfos[2].Length - fileInfos[1].Length).ShouldBeLessThan(0); // // File Checks /// var deltaPkgFiles = deltaPkg.GetFiles().ToList(); deltaPkgFiles.Count.ShouldBeGreaterThan(0); this.Log().Info("Files in delta package:"); deltaPkgFiles.ForEach(x => this.Log().Info(x.Path)); var newFilesAdded = new[] { "Newtonsoft.Json.dll", "Refit.dll", "Refit-Portable.dll", "Castle.Core.dll", }.Select(x => x.ToLowerInvariant()); // vNext adds a dependency on Refit newFilesAdded .All(x => deltaPkgFiles.Any(y => y.Path.ToLowerInvariant().Contains(x))) .ShouldBeTrue(); // All the other files should be diffs and shasums deltaPkgFiles .Where(x => !newFilesAdded.Any(y => x.Path.ToLowerInvariant().Contains(y))) .All(x => x.Path.ToLowerInvariant().EndsWith("diff") || x.Path.ToLowerInvariant().EndsWith("shasum")) .ShouldBeTrue(); // Every .diff file should have a shasum file deltaPkg.GetFiles().Any(x => x.Path.ToLowerInvariant().EndsWith(".diff")).ShouldBeTrue(); deltaPkg.GetFiles() .Where(x => x.Path.ToLowerInvariant().EndsWith(".diff")) .ForEach(x => { var lookingFor = x.Path.Replace(".diff", ".shasum"); this.Log().Info("Looking for corresponding shasum file: {0}", lookingFor); deltaPkg.GetFiles().Any(y => y.Path == lookingFor).ShouldBeTrue(); }); } finally { tempFiles.ForEach(File.Delete); } } [Fact] public void WhenBasePackageIsNewerThanNewPackageThrowException() { var basePackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Tests.0.2.0-pre.nupkg"); var newPackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Tests.0.1.0-pre.nupkg"); var sourceDir = IntegrationTestHelper.GetPath("fixtures", "packages"); (new DirectoryInfo(sourceDir)).Exists.ShouldBeTrue(); var baseFixture = new ReleasePackage(basePackage); var fixture = new ReleasePackage(newPackage); var tempFiles = Enumerable.Range(0, 3) .Select(_ => Path.GetTempPath() + Guid.NewGuid().ToString() + ".nupkg") .ToArray(); try { baseFixture.CreateReleasePackage(tempFiles[0], sourceDir); fixture.CreateReleasePackage(tempFiles[1], sourceDir); (new FileInfo(baseFixture.ReleasePackageFile)).Exists.ShouldBeTrue(); (new FileInfo(fixture.ReleasePackageFile)).Exists.ShouldBeTrue(); Assert.Throws(() => { var deltaBuilder = new DeltaPackageBuilder(); deltaBuilder.CreateDeltaPackage(baseFixture, fixture, tempFiles[2]); }); } finally { tempFiles.ForEach(File.Delete); } } [Fact] public void WhenBasePackageReleaseIsNullThrowsException() { var basePackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Core.1.0.0.0.nupkg"); var newPackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Core.1.1.0.0.nupkg"); var sourceDir = IntegrationTestHelper.GetPath("fixtures", "packages"); (new DirectoryInfo(sourceDir)).Exists.ShouldBeTrue(); var baseFixture = new ReleasePackage(basePackage); var fixture = new ReleasePackage(newPackage); var tempFile = Path.GetTempPath() + Guid.NewGuid() + ".nupkg"; try { Assert.Throws(() => { var deltaBuilder = new DeltaPackageBuilder(); deltaBuilder.CreateDeltaPackage(baseFixture, fixture, tempFile); }); } finally { File.Delete(tempFile); } } [Fact] public void WhenBasePackageDoesNotExistThrowException() { var basePackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Tests.0.1.0-pre.nupkg"); var newPackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Tests.0.2.0-pre.nupkg"); var sourceDir = IntegrationTestHelper.GetPath("fixtures", "packages"); (new DirectoryInfo(sourceDir)).Exists.ShouldBeTrue(); var baseFixture = new ReleasePackage(basePackage); var fixture = new ReleasePackage(newPackage); var tempFiles = Enumerable.Range(0, 3) .Select(_ => Path.GetTempPath() + Guid.NewGuid().ToString() + ".nupkg") .ToArray(); try { baseFixture.CreateReleasePackage(tempFiles[0], sourceDir); fixture.CreateReleasePackage(tempFiles[1], sourceDir); (new FileInfo(baseFixture.ReleasePackageFile)).Exists.ShouldBeTrue(); (new FileInfo(fixture.ReleasePackageFile)).Exists.ShouldBeTrue(); // NOW WATCH AS THE FILE DISAPPEARS File.Delete(baseFixture.ReleasePackageFile); Assert.Throws(() => { var deltaBuilder = new DeltaPackageBuilder(); deltaBuilder.CreateDeltaPackage(baseFixture, fixture, tempFiles[2]); }); } finally { tempFiles.ForEach(File.Delete); } } [Fact] public void WhenNewPackageDoesNotExistThrowException() { var basePackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Tests.0.1.0-pre.nupkg"); var newPackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Tests.0.2.0-pre.nupkg"); var sourceDir = IntegrationTestHelper.GetPath("fixtures", "packages"); (new DirectoryInfo(sourceDir)).Exists.ShouldBeTrue(); var baseFixture = new ReleasePackage(basePackage); var fixture = new ReleasePackage(newPackage); var tempFiles = Enumerable.Range(0, 3) .Select(_ => Path.GetTempPath() + Guid.NewGuid().ToString() + ".nupkg") .ToArray(); try { baseFixture.CreateReleasePackage(tempFiles[0], sourceDir); fixture.CreateReleasePackage(tempFiles[1], sourceDir); (new FileInfo(baseFixture.ReleasePackageFile)).Exists.ShouldBeTrue(); (new FileInfo(fixture.ReleasePackageFile)).Exists.ShouldBeTrue(); // NOW WATCH AS THE FILE DISAPPEARS File.Delete(fixture.ReleasePackageFile); Assert.Throws(() => { var deltaBuilder = new DeltaPackageBuilder(); deltaBuilder.CreateDeltaPackage(baseFixture, fixture, tempFiles[2]); }); } finally { tempFiles.ForEach(File.Delete); } } [Fact] public void HandleBsDiffWithoutExtraData() { var baseFileData = new byte[] { 1, 1, 1, 1 }; var newFileData = new byte[] { 2, 1, 1, 1 }; byte[] patchData; using (var patchOut = new MemoryStream()) { Bsdiff.BinaryPatchUtility.Create(baseFileData, newFileData, patchOut); patchData = patchOut.ToArray(); } using (var toPatch = new MemoryStream(baseFileData)) using (var patched = new MemoryStream()) { Bsdiff.BinaryPatchUtility.Apply(toPatch, () => new MemoryStream(patchData), patched); Assert.Equal(newFileData, patched.ToArray()); } } } } ================================================ FILE: test/Squirrel.Tests/DownloadReleasesTests.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using Squirrel.SimpleSplat; using Squirrel.Tests.TestHelpers; using Xunit; namespace Squirrel.Tests { public class DownloadReleasesTests : IEnableLogger { [Fact(Skip = "Rewrite this to be an integration test")] public void ChecksumShouldFailIfFilesAreMissing() { Assert.False(true, "Rewrite this to be an integration test"); /* var filename = "Squirrel.Core.1.0.0.0.nupkg"; var nuGetPkg = IntegrationTestHelper.GetPath("fixtures", filename); var fs = new Mock(); var urlDownloader = new Mock(); ReleaseEntry entry; using (var f = File.OpenRead(nuGetPkg)) { entry = ReleaseEntry.GenerateFromFile(f, filename); } var fileInfo = new Mock(); fileInfo.Setup(x => x.OpenRead()).Returns(File.OpenRead(nuGetPkg)); fileInfo.Setup(x => x.Exists).Returns(false); fs.Setup(x => x.GetFileInfo(Path.Combine(".", "theApp", "packages", filename))).Returns(fileInfo.Object); var fixture = ExposedObject.From( new UpdateManager("http://lol", "theApp", ".", fs.Object, urlDownloader.Object)); bool shouldDie = true; try { // NB: We can't use Assert.Throws here because the binder // will try to pick the wrong method fixture.checksumPackage(entry); } catch (Exception) { shouldDie = false; } shouldDie.ShouldBeFalse(); */ } [Fact(Skip = "Rewrite this to be an integration test")] public void ChecksumShouldFailIfFilesAreBogus() { Assert.False(true, "Rewrite this to be an integration test"); /* var filename = "Squirrel.Core.1.0.0.0.nupkg"; var nuGetPkg = IntegrationTestHelper.GetPath("fixtures", filename); var fs = new Mock(); var urlDownloader = new Mock(); ReleaseEntry entry; using (var f = File.OpenRead(nuGetPkg)) { entry = ReleaseEntry.GenerateFromFile(f, filename); } var fileInfo = new Mock(); fileInfo.Setup(x => x.OpenRead()).Returns(new MemoryStream(Encoding.UTF8.GetBytes("Lol broken"))); fileInfo.Setup(x => x.Exists).Returns(true); fileInfo.Setup(x => x.Length).Returns(new FileInfo(nuGetPkg).Length); fileInfo.Setup(x => x.Delete()).Verifiable(); fs.Setup(x => x.GetFileInfo(Path.Combine(".", "theApp", "packages", filename))).Returns(fileInfo.Object); var fixture = ExposedObject.From( new UpdateManager("http://lol", "theApp", ".", fs.Object, urlDownloader.Object)); bool shouldDie = true; try { fixture.checksumPackage(entry); } catch (Exception ex) { this.Log().InfoException("Checksum failure", ex); shouldDie = false; } shouldDie.ShouldBeFalse(); fileInfo.Verify(x => x.Delete(), Times.Once()); */ } [Fact(Skip = "Rewrite this to be an integration test")] public async Task DownloadReleasesFromHttpServerIntegrationTest() { Assert.False(true, "Rewrite this to not use the SampleUpdatingApp"); /* string tempDir = null; var updateDir = new DirectoryInfo(IntegrationTestHelper.GetPath("..", "SampleUpdatingApp", "SampleReleasesFolder")); IDisposable disp; try { var httpServer = new StaticHttpServer(30405, updateDir.FullName); disp = httpServer.Start(); } catch (HttpListenerException) { Assert.False(true, @"Windows sucks, go run 'netsh http add urlacl url=http://+:30405/ user=MYMACHINE\MyUser"); return; } var entriesToDownload = updateDir.GetFiles("*.nupkg") .Select(x => ReleaseEntry.GenerateFromFile(x.FullName)) .ToArray(); entriesToDownload.Count().ShouldBeGreaterThan(0); using (disp) using (Utility.WithTempDirectory(out tempDir)) { // NB: This is normally done by CheckForUpdates, but since // we're skipping that in the test we have to do it ourselves Directory.CreateDirectory(Path.Combine(tempDir, "SampleUpdatingApp", "packages")); var fixture = new UpdateManager("http://localhost:30405", "SampleUpdatingApp", tempDir); using (fixture) { var progress = new List(); await fixture.DownloadReleases(entriesToDownload, progress.Add); progress .Aggregate(0, (acc, x) => { x.ShouldBeGreaterThan(acc); return x; }) .ShouldEqual(100); } entriesToDownload.ForEach(x => { this.Log().Info("Looking for {0}", x.Filename); var actualFile = Path.Combine(tempDir, "SampleUpdatingApp", "packages", x.Filename); File.Exists(actualFile).ShouldBeTrue(); var actualEntry = ReleaseEntry.GenerateFromFile(actualFile); actualEntry.SHA1.ShouldEqual(x.SHA1); actualEntry.Version.ShouldEqual(x.Version); }); } */ } [Fact(Skip = "Rewrite this to be an integration test")] public async Task DownloadReleasesFromFileDirectoryIntegrationTest() { Assert.False(true, "Rewrite this to not use the SampleUpdatingApp"); /* string tempDir = null; var updateDir = new DirectoryInfo(IntegrationTestHelper.GetPath("..", "SampleUpdatingApp", "SampleReleasesFolder")); var entriesToDownload = updateDir.GetFiles("*.nupkg") .Select(x => ReleaseEntry.GenerateFromFile(x.FullName)) .ToArray(); entriesToDownload.Count().ShouldBeGreaterThan(0); using (Utility.WithTempDirectory(out tempDir)) { // NB: This is normally done by CheckForUpdates, but since // we're skipping that in the test we have to do it ourselves Directory.CreateDirectory(Path.Combine(tempDir, "SampleUpdatingApp", "packages")); var fixture = new UpdateManager(updateDir.FullName, "SampleUpdatingApp", tempDir); using (fixture) { var progress = new List(); await fixture.DownloadReleases(entriesToDownload, progress.Add); this.Log().Info("Progress: [{0}]", String.Join(",", progress)); progress .Aggregate(0, (acc, x) => { x.ShouldBeGreaterThan(acc); return x; }) .ShouldEqual(100); } entriesToDownload.ForEach(x => { this.Log().Info("Looking for {0}", x.Filename); var actualFile = Path.Combine(tempDir, "SampleUpdatingApp", "packages", x.Filename); File.Exists(actualFile).ShouldBeTrue(); var actualEntry = ReleaseEntry.GenerateFromFile(actualFile); actualEntry.SHA1.ShouldEqual(x.SHA1); actualEntry.Version.ShouldEqual(x.Version); }); } */ } } } ================================================ FILE: test/Squirrel.Tests/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.InteropServices; using Xunit; [assembly: ComVisible(false)] [assembly: CollectionBehavior(MaxParallelThreads=1, DisableTestParallelization=true)] [assembly: AssemblyMetadata("SquirrelAwareVersion", "1")] ================================================ FILE: test/Squirrel.Tests/ReleaseEntryTests.cs ================================================ using System; using System.IO; using System.Linq; using Squirrel; using Squirrel.Tests.TestHelpers; using Xunit; using NuGet; namespace Squirrel.Tests.Core { public class ReleaseEntryTests { [Theory] [InlineData(@"94689fede03fed7ab59c24337673a27837f0c3ec MyCoolApp-1.0.nupkg 1004502", "MyCoolApp-1.0.nupkg", 1004502, null, null)] [InlineData(@"3a2eadd15dd984e4559f2b4d790ec8badaeb6a39 MyCoolApp-1.1.nupkg 1040561", "MyCoolApp-1.1.nupkg", 1040561, null, null)] [InlineData(@"14db31d2647c6d2284882a2e101924a9c409ee67 MyCoolApp-1.1.nupkg.delta 80396", "MyCoolApp-1.1.nupkg.delta", 80396, null, null)] [InlineData(@"0000000000000000000000000000000000000000 http://test.org/Folder/MyCoolApp-1.2.nupkg 2569", "MyCoolApp-1.2.nupkg", 2569, "http://test.org/Folder/", null)] [InlineData(@"0000000000000000000000000000000000000000 http://test.org/Folder/MyCoolApp-1.2.nupkg?query=param 2569", "MyCoolApp-1.2.nupkg", 2569, "http://test.org/Folder/", "?query=param")] [InlineData(@"0000000000000000000000000000000000000000 https://www.test.org/Folder/MyCoolApp-1.2-delta.nupkg 1231953", "MyCoolApp-1.2-delta.nupkg", 1231953, "https://www.test.org/Folder/", null)] [InlineData(@"0000000000000000000000000000000000000000 https://www.test.org/Folder/MyCoolApp-1.2-delta.nupkg?query=param 1231953", "MyCoolApp-1.2-delta.nupkg", 1231953, "https://www.test.org/Folder/", "?query=param")] public void ParseValidReleaseEntryLines(string releaseEntry, string fileName, long fileSize, string baseUrl, string query) { var fixture = ReleaseEntry.ParseReleaseEntry(releaseEntry); Assert.Equal(fileName, fixture.Filename); Assert.Equal(fileSize, fixture.Filesize); Assert.Equal(baseUrl, fixture.BaseUrl); Assert.Equal(query, fixture.Query); } [Theory] [InlineData(@"0000000000000000000000000000000000000000 file:/C/Folder/MyCoolApp-0.0.nupkg 0")] [InlineData(@"0000000000000000000000000000000000000000 C:\Folder\MyCoolApp-0.0.nupkg 0")] [InlineData(@"0000000000000000000000000000000000000000 ..\OtherFolder\MyCoolApp-0.0.nupkg 0")] [InlineData(@"0000000000000000000000000000000000000000 ../OtherFolder/MyCoolApp-0.0.nupkg 0")] [InlineData(@"0000000000000000000000000000000000000000 \\Somewhere\NetworkShare\MyCoolApp-0.0.nupkg.delta 0")] public void ParseThrowsWhenInvalidReleaseEntryLines(string releaseEntry) { Assert.Throws(() => ReleaseEntry.ParseReleaseEntry(releaseEntry)); } [Theory] [InlineData(@"0000000000000000000000000000000000000000 file.nupkg 0")] [InlineData(@"0000000000000000000000000000000000000000 http://path/file.nupkg 0")] public void EntryAsStringMatchesParsedInput(string releaseEntry) { var fixture = ReleaseEntry.ParseReleaseEntry(releaseEntry); Assert.Equal(releaseEntry, fixture.EntryAsString); } [Theory] [InlineData("Squirrel.Core.1.0.0.0.nupkg", 4457, "75255cfd229a1ed1447abe1104f5635e69975d30")] [InlineData("Squirrel.Core.1.1.0.0.nupkg", 15830, "9baf1dbacb09940086c8c62d9a9dbe69fe1f7593")] public void GenerateFromFileTest(string name, long size, string sha1) { var path = IntegrationTestHelper.GetPath("fixtures", name); using (var f = File.OpenRead(path)) { var fixture = ReleaseEntry.GenerateFromFile(f, "dontcare"); Assert.Equal(size, fixture.Filesize); Assert.Equal(sha1, fixture.SHA1.ToLowerInvariant()); } } [Theory] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.nupkg 123", 1, 2, 0, 0, "", false)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2-full.nupkg 123", 1, 2, 0, 0, "", false)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2-delta.nupkg 123", 1, 2, 0, 0, "", true)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2-beta1.nupkg 123", 1, 2, 0, 0, "beta1", false)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2-beta1-full.nupkg 123", 1, 2, 0, 0, "beta1", false)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2-beta1-delta.nupkg 123", 1, 2, 0, 0, "beta1", true)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3.nupkg 123", 1, 2, 3, 0, "", false)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3-full.nupkg 123", 1, 2, 3, 0, "", false)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3-delta.nupkg 123", 1, 2, 3, 0, "", true)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3-beta1.nupkg 123", 1, 2, 3, 0, "beta1", false)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3-beta1-full.nupkg 123", 1, 2, 3, 0, "beta1", false)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3-beta1-delta.nupkg 123", 1, 2, 3, 0, "beta1", true)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3.4.nupkg 123", 1, 2, 3, 4, "", false)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3.4-full.nupkg 123", 1, 2, 3, 4, "", false)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3.4-delta.nupkg 123", 1, 2, 3, 4, "", true)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3.4-beta1.nupkg 123", 1, 2, 3, 4, "beta1", false)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3.4-beta1-full.nupkg 123", 1, 2, 3, 4, "beta1", false)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3.4-beta1-delta.nupkg 123", 1, 2, 3, 4, "beta1", true)] public void ParseVersionTest(string releaseEntry, int major, int minor, int patch, int revision, string prerelease, bool isDelta) { var fixture = ReleaseEntry.ParseReleaseEntry(releaseEntry); Assert.Equal(new SemanticVersion(new Version(major, minor, patch, revision), prerelease), fixture.Version); Assert.Equal(isDelta, fixture.IsDelta); } [Theory] [InlineData("0000000000000000000000000000000000000000 MyCool-App-1.2.nupkg 123", "MyCool-App")] [InlineData("0000000000000000000000000000000000000000 MyCool_App-1.2-full.nupkg 123", "MyCool_App")] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2-delta.nupkg 123", "MyCoolApp")] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2-beta1.nupkg 123", "MyCoolApp")] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2-beta1-full.nupkg 123", "MyCoolApp")] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2-beta1-delta.nupkg 123", "MyCoolApp")] [InlineData("0000000000000000000000000000000000000000 MyCool-App-1.2.3.nupkg 123", "MyCool-App")] [InlineData("0000000000000000000000000000000000000000 MyCool_App-1.2.3-full.nupkg 123", "MyCool_App")] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3-delta.nupkg 123", "MyCoolApp")] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3-beta1.nupkg 123", "MyCoolApp")] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3-beta1-full.nupkg 123", "MyCoolApp")] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3-beta1-delta.nupkg 123", "MyCoolApp")] [InlineData("0000000000000000000000000000000000000000 MyCool-App-1.2.3.4.nupkg 123", "MyCool-App")] [InlineData("0000000000000000000000000000000000000000 MyCool_App-1.2.3.4-full.nupkg 123", "MyCool_App")] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3.4-delta.nupkg 123", "MyCoolApp")] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3.4-beta1.nupkg 123", "MyCoolApp")] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.3.4-beta1-full.nupkg 123", "MyCoolApp")] [InlineData("0000000000000000000000000000000000000000 MyCool-App-1.2.3.4-beta1-delta.nupkg 123", "MyCool-App")] public void CheckPackageName(string releaseEntry, string expected) { var fixture = ReleaseEntry.ParseReleaseEntry(releaseEntry); Assert.Equal(expected, fixture.PackageName); } [Theory] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2.nupkg 123 # 10%", 1, 2, 0, 0, "", false, 0.1f)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2-full.nupkg 123 # 90%", 1, 2, 0, 0, "", false, 0.9f)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2-delta.nupkg 123", 1, 2, 0, 0, "", true, null)] [InlineData("0000000000000000000000000000000000000000 MyCoolApp-1.2-delta.nupkg 123 # 5%", 1, 2, 0, 0, "", true, 0.05f)] public void ParseStagingPercentageTest(string releaseEntry, int major, int minor, int patch, int revision, string prerelease, bool isDelta, float? stagingPercentage) { var fixture = ReleaseEntry.ParseReleaseEntry(releaseEntry); Assert.Equal(new SemanticVersion(new Version(major, minor, patch, revision), prerelease), fixture.Version); Assert.Equal(isDelta, fixture.IsDelta); if (stagingPercentage.HasValue) { Assert.True(Math.Abs(fixture.StagingPercentage.Value - stagingPercentage.Value) < 0.001); } else { Assert.Null(fixture.StagingPercentage); } } [Fact] public void CanParseGeneratedReleaseEntryAsString() { var path = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Core.1.1.0.0.nupkg"); var entryAsString = ReleaseEntry.GenerateFromFile(path).EntryAsString; ReleaseEntry.ParseReleaseEntry(entryAsString); } [Fact] public void InvalidReleaseNotesThrowsException() { var path = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Core.1.0.0.0.nupkg"); var fixture = ReleaseEntry.GenerateFromFile(path); Assert.Throws(() => fixture.GetReleaseNotes(IntegrationTestHelper.GetPath("fixtures"))); } [Fact] public void GetLatestReleaseWithNullCollectionReturnsNull() { Assert.Null(ReleaseEntry.GetPreviousRelease( null, null, null)); } [Fact] public void GetLatestReleaseWithEmptyCollectionReturnsNull() { Assert.Null(ReleaseEntry.GetPreviousRelease( Enumerable.Empty(), null, null)); } [Fact] public void WhenCurrentReleaseMatchesLastReleaseReturnNull() { var package = new ReleasePackage("Espera-1.7.6-beta.nupkg"); var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.7.6-beta.nupkg")) }; Assert.Null(ReleaseEntry.GetPreviousRelease( releaseEntries, package, @"C:\temp\somefolder")); } [Fact] public void WhenMultipleReleaseMatchesReturnEarlierResult() { var expected = new SemanticVersion("1.7.5-beta"); var package = new ReleasePackage("Espera-1.7.6-beta.nupkg"); var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.7.6-beta.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.7.5-beta.nupkg")) }; var actual = ReleaseEntry.GetPreviousRelease( releaseEntries, package, @"C:\temp\"); Assert.Equal(expected, actual.Version); } [Fact] public void WhenMultipleReleasesFoundReturnPreviousVersion() { var expected = new SemanticVersion("1.7.6-beta"); var input = new ReleasePackage("Espera-1.7.7-beta.nupkg"); var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.7.6-beta.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.7.5-beta.nupkg")) }; var actual = ReleaseEntry.GetPreviousRelease( releaseEntries, input, @"C:\temp\"); Assert.Equal(expected, actual.Version); } [Fact] public void WhenMultipleReleasesFoundInOtherOrderReturnPreviousVersion() { var expected = new SemanticVersion("1.7.6-beta"); var input = new ReleasePackage("Espera-1.7.7-beta.nupkg"); var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.7.5-beta.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.7.6-beta.nupkg")) }; var actual = ReleaseEntry.GetPreviousRelease( releaseEntries, input, @"C:\temp\"); Assert.Equal(expected, actual.Version); } [Fact] public void WhenReleasesAreOutOfOrderSortByVersion() { var path = Path.GetTempFileName(); var firstVersion = new SemanticVersion("1.0.0"); var secondVersion = new SemanticVersion("1.1.0"); var thirdVersion = new SemanticVersion("1.2.0"); var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-delta.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.1.0-delta.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.1.0-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.0.0-full.nupkg")) }; ReleaseEntry.WriteReleaseFile(releaseEntries, path); var releases = ReleaseEntry.ParseReleaseFile(File.ReadAllText(path)).ToArray(); Assert.Equal(firstVersion, releases[0].Version); Assert.Equal(secondVersion, releases[1].Version); Assert.Equal(true, releases[1].IsDelta); Assert.Equal(secondVersion, releases[2].Version); Assert.Equal(false, releases[2].IsDelta); Assert.Equal(thirdVersion, releases[3].Version); Assert.Equal(true, releases[3].IsDelta); Assert.Equal(thirdVersion, releases[4].Version); Assert.Equal(false, releases[4].IsDelta); } [Fact] public void WhenPreReleasesAreOutOfOrderSortByNumericSuffix() { var path = Path.GetTempFileName(); var firstVersion = new SemanticVersion("1.1.9-beta105"); var secondVersion = new SemanticVersion("1.2.0-beta9"); var thirdVersion = new SemanticVersion("1.2.0-beta10"); var fourthVersion = new SemanticVersion("1.2.0-beta100"); var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-beta1-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-beta9-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-beta100-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.1.9-beta105-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-beta10-full.nupkg")) }; ReleaseEntry.WriteReleaseFile(releaseEntries, path); var releases = ReleaseEntry.ParseReleaseFile(File.ReadAllText(path)).ToArray(); Assert.Equal(firstVersion, releases[0].Version); Assert.Equal(secondVersion, releases[2].Version); Assert.Equal(thirdVersion, releases[3].Version); Assert.Equal(fourthVersion, releases[4].Version); } [Fact] public void StagingUsersGetBetaSoftware() { // NB: We're kind of using a hack here, in that we know that the // last 4 bytes are used as the percentage, and the percentage // effectively measures, "How close are you to zero". Guid.Empty // is v close to zero, because it is zero. var path = Path.GetTempFileName(); var ourGuid = Guid.Empty; var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-full.nupkg", 0.1f)), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.1.0-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.0.0-full.nupkg")) }; ReleaseEntry.WriteReleaseFile(releaseEntries, path); var releases = ReleaseEntry.ParseReleaseFileAndApplyStaging(File.ReadAllText(path), ourGuid).ToArray(); Assert.Equal(3, releases.Length); } [Fact] public void BorkedUsersGetProductionSoftware() { var path = Path.GetTempFileName(); var ourGuid = default(Guid?); var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-full.nupkg", 0.1f)), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.1.0-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.0.0-full.nupkg")) }; ReleaseEntry.WriteReleaseFile(releaseEntries, path); var releases = ReleaseEntry.ParseReleaseFileAndApplyStaging(File.ReadAllText(path), ourGuid).ToArray(); Assert.Equal(2, releases.Length); } [Theory] [InlineData("{22b29e6f-bd2e-43d2-85ca-ffffffffffff}")] [InlineData("{22b29e6f-bd2e-43d2-85ca-888888888888}")] [InlineData("{22b29e6f-bd2e-43d2-85ca-444444444444}")] public void UnluckyUsersGetProductionSoftware(string inputGuid) { var path = Path.GetTempFileName(); var ourGuid = Guid.ParseExact(inputGuid, "B"); var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-full.nupkg", 0.1f)), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.1.0-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.0.0-full.nupkg")) }; ReleaseEntry.WriteReleaseFile(releaseEntries, path); var releases = ReleaseEntry.ParseReleaseFileAndApplyStaging(File.ReadAllText(path), ourGuid).ToArray(); Assert.Equal(2, releases.Length); } [Theory] [InlineData("{22b29e6f-bd2e-43d2-85ca-333333333333}")] [InlineData("{22b29e6f-bd2e-43d2-85ca-111111111111}")] [InlineData("{22b29e6f-bd2e-43d2-85ca-000000000000}")] public void LuckyUsersGetBetaSoftware(string inputGuid) { var path = Path.GetTempFileName(); var ourGuid = Guid.ParseExact(inputGuid, "B"); var releaseEntries = new[] { ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.2.0-full.nupkg", 0.25f)), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.1.0-full.nupkg")), ReleaseEntry.ParseReleaseEntry(MockReleaseEntry("Espera-1.0.0-full.nupkg")) }; ReleaseEntry.WriteReleaseFile(releaseEntries, path); var releases = ReleaseEntry.ParseReleaseFileAndApplyStaging(File.ReadAllText(path), ourGuid).ToArray(); Assert.Equal(3, releases.Length); } [Fact] public void ParseReleaseFileShouldReturnNothingForBlankFiles() { Assert.True(ReleaseEntry.ParseReleaseFile("").Count() == 0); Assert.True(ReleaseEntry.ParseReleaseFile(null).Count() == 0); } static string MockReleaseEntry(string name, float? percentage = null) { if (percentage.HasValue) { var ret = String.Format("94689fede03fed7ab59c24337673a27837f0c3ec {0} 1004502 # {1:F0}%", name, percentage * 100.0f); return ret; } else { return String.Format("94689fede03fed7ab59c24337673a27837f0c3ec {0} 1004502", name); } } } } ================================================ FILE: test/Squirrel.Tests/ReleasePackageTests.cs ================================================ using System.Runtime.Versioning; using MarkdownSharp; using NuGet; using Squirrel; using Squirrel.Tests.TestHelpers; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Xml.Linq; using Squirrel.SimpleSplat; using Xunit; namespace Squirrel.Tests.Core { public class CreateReleasePackageTests : IEnableLogger { [Fact] public void SemanticVersionDoesWhatIWant() { var sv = new SemanticVersion("1.2.3.4"); var dontcare = default(SemanticVersion); Assert.False(SemanticVersion.TryParseStrict(sv.ToString(), out dontcare)); } [Fact] public void ReleasePackageIntegrationTest() { var inputPackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Tests.0.1.0-pre.nupkg"); var outputPackage = Path.GetTempFileName() + ".nupkg"; var sourceDir = IntegrationTestHelper.GetPath("fixtures", "packages"); var fixture = new ReleasePackage(inputPackage); (new DirectoryInfo(sourceDir)).Exists.ShouldBeTrue(); try { fixture.CreateReleasePackage(outputPackage, sourceDir); this.Log().Info("Resulting package is at {0}", outputPackage); var pkg = new ZipPackage(outputPackage); int refs = pkg.FrameworkAssemblies.Count(); this.Log().Info("Found {0} refs", refs); refs.ShouldEqual(0); this.Log().Info("Files in release package:"); List files = pkg.GetFiles().ToList(); files.ForEach(x => this.Log().Info(x.Path)); List nonDesktopPaths = new[] {"sl", "winrt", "netcore", "win8", "windows8", "MonoAndroid", "MonoTouch", "MonoMac", "wp", } .Select(x => @"lib\" + x) .ToList(); files.Any(x => nonDesktopPaths.Any(y => x.Path.ToLowerInvariant().Contains(y.ToLowerInvariant()))).ShouldBeFalse(); files.Any(x => x.Path.ToLowerInvariant().EndsWith(@".xml")).ShouldBeFalse(); } finally { File.Delete(outputPackage); } } [Fact] public void FindPackageInOurLocalPackageList() { var inputPackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Core.1.0.0.0.nupkg"); var sourceDir = IntegrationTestHelper.GetPath("fixtures", "packages"); (new DirectoryInfo(sourceDir)).Exists.ShouldBeTrue(); var fixture = ExposedObject.From(new ReleasePackage(inputPackage)); IPackage result = fixture.matchPackage(new LocalPackageRepository(sourceDir), "xunit", VersionUtility.ParseVersionSpec("[1.0,2.0]")); result.Id.ShouldEqual("xunit"); result.Version.Version.Major.ShouldEqual(2); result.Version.Version.Minor.ShouldEqual(0); } [Fact] public void FindDependentPackagesForDummyPackage() { var inputPackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Tests.0.1.0-pre.nupkg"); var fixture = new ReleasePackage(inputPackage); var sourceDir = IntegrationTestHelper.GetPath("fixtures", "packages"); (new DirectoryInfo(sourceDir)).Exists.ShouldBeTrue(); IEnumerable results = fixture.findAllDependentPackages(default(IPackage), (IPackageRepository)new LocalPackageRepository(sourceDir), default(HashSet), default(FrameworkName)); results.Count().ShouldBeGreaterThan(0); } [Fact] public void CanLoadPackageWhichHasNoDependencies() { var inputPackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Core.NoDependencies.1.0.0.0.nupkg"); var outputPackage = Path.GetTempFileName() + ".nupkg"; var fixture = new ReleasePackage(inputPackage); var sourceDir = IntegrationTestHelper.GetPath("fixtures", "packages"); try { fixture.CreateReleasePackage(outputPackage, sourceDir); } finally { File.Delete(outputPackage); } } [Fact] public void CanResolveMultipleLevelsOfDependencies() { var inputPackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Tests.0.1.0-pre.nupkg"); var outputPackage = Path.GetTempFileName() + ".nupkg"; var sourceDir = IntegrationTestHelper.GetPath("fixtures", "packages"); var fixture = new ReleasePackage(inputPackage); (new DirectoryInfo(sourceDir)).Exists.ShouldBeTrue(); try { fixture.CreateReleasePackage(outputPackage, sourceDir); this.Log().Info("Resulting package is at {0}", outputPackage); var pkg = new ZipPackage(outputPackage); int refs = pkg.FrameworkAssemblies.Count(); this.Log().Info("Found {0} refs", refs); refs.ShouldEqual(0); this.Log().Info("Files in release package:"); pkg.GetFiles().ForEach(x => this.Log().Info(x.Path)); var filesToLookFor = new[] { "xunit.assert.dll", // Tests => Xunit => Xunit.Assert "NuGet.Core.dll", // Tests => NuGet "Squirrel.Tests.dll", }; filesToLookFor.ForEach(name => { this.Log().Info("Looking for {0}", name); pkg.GetFiles().Any(y => y.Path.ToLowerInvariant().Contains(name.ToLowerInvariant())).ShouldBeTrue(); }); } finally { File.Delete(outputPackage); } } [Fact] public void SpecFileMarkdownRenderingTest() { var dontcare = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Core.1.1.0.0.nupkg"); var inputSpec = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Core.1.1.0.0.nuspec"); var fixture = new ReleasePackage(dontcare); var targetFile = Path.GetTempFileName(); File.Copy(inputSpec, targetFile, true); try { var processor = new Func(input => (new Markdown()).Transform(input)); // NB: For No Reason At All, renderReleaseNotesMarkdown is // invulnerable to ExposedObject. Whyyyyyyyyy var renderMinfo = fixture.GetType().GetMethod("renderReleaseNotesMarkdown", BindingFlags.NonPublic | BindingFlags.Instance); renderMinfo.Invoke(fixture, new object[] {targetFile, processor}); var doc = XDocument.Load(targetFile); XNamespace ns = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"; var relNotesElement = doc.Descendants(ns + "releaseNotes").First(); var htmlText = relNotesElement.Value; this.Log().Info("HTML Text:\n{0}", htmlText); htmlText.Contains("## Release Notes").ShouldBeFalse(); } finally { File.Delete(targetFile); } } [Fact] public void UsesTheRightVersionOfADependencyWhenMultipleAreInPackages() { var outputPackage = Path.GetTempFileName() + ".nupkg"; string outputFile = null; var inputPackage = IntegrationTestHelper.GetPath("fixtures", "CaliburnMicroDemo.1.0.0.nupkg"); var wrongPackage = "Caliburn.Micro.1.4.1.nupkg"; var wrongPackagePath = IntegrationTestHelper.GetPath("fixtures", wrongPackage); var rightPackage = "Caliburn.Micro.1.5.2.nupkg"; var rightPackagePath = IntegrationTestHelper.GetPath("fixtures", rightPackage); try { var sourceDir = IntegrationTestHelper.GetPath("fixtures", "packages"); (new DirectoryInfo(sourceDir)).Exists.ShouldBeTrue(); File.Copy(wrongPackagePath, Path.Combine(sourceDir, wrongPackage), true); File.Copy(rightPackagePath, Path.Combine(sourceDir, rightPackage), true); var package = new ReleasePackage(inputPackage); var outputFileName = package.CreateReleasePackage(outputPackage, sourceDir); var zipPackage = new ZipPackage(outputFileName); var fileName = "Caliburn.Micro.dll"; var dependency = zipPackage.GetLibFiles() .Where(f => f.Path.EndsWith(fileName)) .Single(f => f.TargetFramework == FrameworkTargetVersion.Net40); outputFile = new FileInfo(Path.Combine(sourceDir, fileName)).FullName; using (var of = File.Create(outputFile)) { dependency.GetStream().CopyTo(of); } var assemblyName = AssemblyName.GetAssemblyName(outputFile); Assert.Equal(1, assemblyName.Version.Major); Assert.Equal(5, assemblyName.Version.Minor); } finally { File.Delete(outputPackage); File.Delete(outputFile); } } [Fact] public void DependentPackageNotFoundAndThrowsError() { string packagesDir; // use empty packages folder using (Utility.WithTempDirectory(out packagesDir)) { var inputPackage = IntegrationTestHelper.GetPath("fixtures", "ProjectDependsOnJsonDotNet.1.0.nupkg"); var outputPackage = Path.GetTempFileName() + ".nupkg"; try { var package = new ReleasePackage(inputPackage); Assert.Throws(() => package.CreateReleasePackage(outputPackage, packagesDir)); } finally { File.Delete(outputPackage); } } } [Fact] public void ContentFilesAreIncludedInCreatedPackage() { var inputPackage = IntegrationTestHelper.GetPath("fixtures", "ProjectWithContent.1.0.0.0-beta.nupkg"); var outputPackage = Path.GetTempFileName() + ".zip"; var sourceDir = IntegrationTestHelper.GetPath("fixtures", "packages"); var fixture = new ReleasePackage(inputPackage); (new DirectoryInfo(sourceDir)).Exists.ShouldBeTrue(); try { fixture.CreateReleasePackage(outputPackage, sourceDir); this.Log().Info("Resulting package is at {0}", outputPackage); var pkg = new ZipPackage(outputPackage); int refs = pkg.FrameworkAssemblies.Count(); this.Log().Info("Found {0} refs", refs); refs.ShouldEqual(0); this.Log().Info("Files in release package:"); var contentFiles = pkg.GetContentFiles(); Assert.Equal(2, contentFiles.Count()); var contentFilePaths = contentFiles.Select(f => f.EffectivePath); Assert.Contains("some-words.txt", contentFilePaths); Assert.Contains("dir\\item-in-subdirectory.txt", contentFilePaths); Assert.Equal(1, pkg.GetLibFiles().Count()); } finally { File.Delete(outputPackage); } } [Fact] public void WhenAProjectContainsNet45BinariesItContainsTheNecessaryDependency() { var outputPackage = Path.GetTempFileName() + ".nupkg"; var inputPackage = IntegrationTestHelper.GetPath("fixtures", "ThisShouldBeANet45Project.1.0.nupkg"); var rightPackage = "Caliburn.Micro.1.5.2.nupkg"; var rightPackagePath = IntegrationTestHelper.GetPath("fixtures", rightPackage); try { var sourceDir = IntegrationTestHelper.GetPath("fixtures", "packages"); (new DirectoryInfo(sourceDir)).Exists.ShouldBeTrue(); File.Copy(rightPackagePath, Path.Combine(sourceDir, rightPackage), true); var package = new ReleasePackage(inputPackage); var outputFileName = package.CreateReleasePackage(outputPackage, sourceDir); var zipPackage = new ZipPackage(outputFileName); var dependency = zipPackage.GetLibFiles() .Where(f => f.Path.EndsWith("Caliburn.Micro.dll")) .FirstOrDefault(f => f.TargetFramework == FrameworkTargetVersion.Net45); Assert.NotNull(dependency); } finally { File.Delete(outputPackage); } } } } ================================================ FILE: test/Squirrel.Tests/Squirrel.Tests.csproj ================================================  net45 Squirrel.Tests Squirrel.Tests false true ================================================ FILE: test/Squirrel.Tests/SquirrelAwareExecutableDetectorTests.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using Squirrel.Tests.TestHelpers; using Xunit; namespace Squirrel.Tests { public class SquirrelAwareExecutableDetectorTests { #if DEBUG private const string NativeBuildRootRelativePath = @"..\..\Win32\"; private const string ManagedBuildRootRelativePath = @"..\..\net45\"; #else private const string NativeBuildRootRelativePath = @"..\..\Win32\"; private const string ManagedBuildRootRelativePath = @"..\..\net45\"; #endif [Fact] public void AtomShellShouldBeSquirrelAware() { var target = IntegrationTestHelper.GetPath("fixtures", "atom.exe"); Assert.True(File.Exists(target)); Assert.True(SquirrelAwareExecutableDetector.GetPESquirrelAwareVersion(target) == 1); } [Fact] public void SquirrelAwareViaVersionBlock() { var target = IntegrationTestHelper.GetPath(NativeBuildRootRelativePath, "Setup.exe"); Assert.True(File.Exists(target)); var ret = SquirrelAwareExecutableDetector.GetPESquirrelAwareVersion(target); Assert.Equal(1, ret.Value); } [Fact] public void SquirrelAwareViaLanguageNeutralVersionBlock() { var target = IntegrationTestHelper.GetPath("fixtures", "SquirrelAwareTweakedNetCoreApp.exe"); Assert.True(File.Exists(target)); var ret = SquirrelAwareExecutableDetector.GetPESquirrelAwareVersion(target); Assert.NotNull(ret); Assert.Equal(1, ret.Value); } [Fact] public void SquirrelAwareViaAssemblyAttribute() { var target = Assembly.GetExecutingAssembly().Location; Assert.True(File.Exists(target)); var ret = SquirrelAwareExecutableDetector.GetPESquirrelAwareVersion(target); Assert.Equal(1, ret.Value); } [Fact] public void NotSquirrelAware() { var target = IntegrationTestHelper.GetPath(ManagedBuildRootRelativePath, "Update.exe"); Assert.True(File.Exists(target)); var ret = SquirrelAwareExecutableDetector.GetPESquirrelAwareVersion(target); Assert.Null(ret); } [Fact] public void SquirrelAwareTestAppShouldBeSquirrelAware() { var target = IntegrationTestHelper.GetPath("fixtures", "SquirrelAwareApp.exe"); Assert.True(File.Exists(target)); Assert.NotNull(SquirrelAwareExecutableDetector.GetPESquirrelAwareVersion(target)); } [Fact] public void NotSquirrelAwareTestAppShouldNotBeSquirrelAware() { var target = IntegrationTestHelper.GetPath("fixtures", "NotSquirrelAwareApp.exe"); Assert.True(File.Exists(target)); Assert.Null(SquirrelAwareExecutableDetector.GetPESquirrelAwareVersion(target)); } } } ================================================ FILE: test/Squirrel.Tests/TestHelpers/AssertExtensions.cs ================================================ using System; using System.Collections; using System.Globalization; using System.IO; using Xunit; namespace Squirrel.Tests.TestHelpers { public static class AssertExtensions { public static void ShouldBeAboutEqualTo(this DateTimeOffset expected, DateTimeOffset current) { Assert.Equal(expected.Date, current.Date); Assert.Equal(expected.Offset, current.Offset); Assert.Equal(expected.Hour, current.Hour); Assert.Equal(expected.Minute, current.Minute); Assert.Equal(expected.Second, current.Second); } public static void ShouldBeFalse(this bool currentObject) { Assert.False(currentObject); } public static void ShouldBeNull(this object currentObject) { Assert.Null(currentObject); } public static void ShouldBeEmpty(this IEnumerable items) { Assert.Empty(items); } public static void ShouldNotBeEmpty(this IEnumerable items) { Assert.NotEmpty(items); } public static void ShouldBeTrue(this bool currentObject) { Assert.True(currentObject); } public static void ShouldEqual(this object compareFrom, object compareTo) { Assert.Equal(compareTo, compareFrom); } public static void ShouldEqual(this T compareFrom, T compareTo) { Assert.Equal(compareTo, compareFrom); } public static void ShouldBeSameAs(this T actual, T expected) { Assert.Same(expected, actual); } public static void ShouldNotBeSameAs(this T actual, T expected) { Assert.NotSame(expected, actual); } public static void ShouldBeAssignableFrom(this object instance) where T : class { Assert.IsAssignableFrom(instance); } public static void ShouldBeType(this object instance, Type type) { Assert.IsType(type, instance); } public static void ShouldBeType(this object instance) { Assert.IsType(instance); } public static void ShouldNotBeType(this object instance) { Assert.IsNotType(instance); } public static void ShouldContain(this string current, string expectedSubstring, StringComparison comparison) { Assert.Contains(expectedSubstring, current, comparison); } public static void ShouldStartWith(this string current, string expectedSubstring, StringComparison comparison) { Assert.True(current.StartsWith(expectedSubstring, comparison)); } public static void ShouldNotBeNull(this object currentObject) { Assert.NotNull(currentObject); } public static void ShouldNotBeNullNorEmpty(this string value) { Assert.NotNull(value); Assert.NotEmpty(value); } public static void ShouldNotEqual(this object compareFrom, object compareTo) { Assert.NotEqual(compareTo, compareFrom); } public static void ShouldBeGreaterThan(this T current, T other) where T : IComparable { Assert.True(current.CompareTo(other) > 0, current + " is not greater than " + other); } public static void ShouldBeLessThan(this T current, T other) where T : IComparable { Assert.True(current.CompareTo(other) < 0, current + " is not less than " + other); } static string ToSafeString(this char c) { if (Char.IsControl(c) || Char.IsWhiteSpace(c)) { switch (c) { case '\r': return @"\r"; case '\n': return @"\n"; case '\t': return @"\t"; case '\a': return @"\a"; case '\v': return @"\v"; case '\f': return @"\f"; default: return String.Format("\\u{0:X};", (int)c); } } return c.ToString(CultureInfo.InvariantCulture); } } public enum DiffStyle { Full, Minimal } } ================================================ FILE: test/Squirrel.Tests/TestHelpers/ExposedClass.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Dynamic; using System.Reflection; // Lovingly stolen from http://exposedobject.codeplex.com/ namespace Squirrel.Tests.TestHelpers { public class ExposedClass : DynamicObject { private Type m_type; private Dictionary>> m_staticMethods; private Dictionary>> m_genStaticMethods; private ExposedClass(Type type) { m_type = type; m_staticMethods = m_type .GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static) .Where(m => !m.IsGenericMethod) .GroupBy(m => m.Name) .ToDictionary( p => p.Key, p => p.GroupBy(r => r.GetParameters().Length).ToDictionary(r => r.Key, r => r.ToList())); m_genStaticMethods = m_type .GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static) .Where(m => m.IsGenericMethod) .GroupBy(m => m.Name) .ToDictionary( p => p.Key, p => p.GroupBy(r => r.GetParameters().Length).ToDictionary(r => r.Key, r => r.ToList())); } public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { // Get type args of the call Type[] typeArgs = ExposedObjectHelper.GetTypeArgs(binder); if (typeArgs != null && typeArgs.Length == 0) typeArgs = null; // // Try to call a non-generic instance method // if (typeArgs == null && m_staticMethods.ContainsKey(binder.Name) && m_staticMethods[binder.Name].ContainsKey(args.Length) && ExposedObjectHelper.InvokeBestMethod(args, null, m_staticMethods[binder.Name][args.Length], out result)) { return true; } // // Try to call a generic instance method // if (m_staticMethods.ContainsKey(binder.Name) && m_staticMethods[binder.Name].ContainsKey(args.Length)) { List methods = new List(); foreach (var method in m_genStaticMethods[binder.Name][args.Length]) { if (method.GetGenericArguments().Length == typeArgs.Length) { methods.Add(method.MakeGenericMethod(typeArgs)); } } if (ExposedObjectHelper.InvokeBestMethod(args, null, methods, out result)) { return true; } } result = null; return false; } public override bool TrySetMember(SetMemberBinder binder, object value) { var propertyInfo = m_type.GetProperty( binder.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); if (propertyInfo != null) { propertyInfo.SetValue(null, value, null); return true; } var fieldInfo = m_type.GetField( binder.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); if (fieldInfo != null) { fieldInfo.SetValue(null, value); return true; } return false; } public override bool TryGetMember(GetMemberBinder binder, out object result) { var propertyInfo = m_type.GetProperty( binder.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); if (propertyInfo != null) { result = propertyInfo.GetValue(null, null); return true; } var fieldInfo = m_type.GetField( binder.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); if (fieldInfo != null) { result = fieldInfo.GetValue(null); return true; } result = null; return false; } public static dynamic From(Type type) { return new ExposedClass(type); } } } ================================================ FILE: test/Squirrel.Tests/TestHelpers/ExposedObject.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Dynamic; using System.Reflection; // Lovingly stolen from http://exposedobject.codeplex.com/ namespace Squirrel.Tests.TestHelpers { public class ExposedObject : DynamicObject { private object m_object; private Type m_type; private Dictionary>> m_instanceMethods; private Dictionary>> m_genInstanceMethods; private ExposedObject(object obj) { m_object = obj; m_type = obj.GetType(); m_instanceMethods = m_type .GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) .Where(m => !m.IsGenericMethod) .GroupBy(m => m.Name) .ToDictionary( p => p.Key, p => p.GroupBy(r => r.GetParameters().Length).ToDictionary(r => r.Key, r => r.ToList())); m_genInstanceMethods = m_type .GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) .Where(m => m.IsGenericMethod) .GroupBy(m => m.Name) .ToDictionary( p => p.Key, p => p.GroupBy(r => r.GetParameters().Length).ToDictionary(r => r.Key, r => r.ToList())); } public object Object { get { return m_object; } } public static dynamic New() { return New(typeof(T)); } public static dynamic New(Type type) { return new ExposedObject(Activator.CreateInstance(type)); } public static dynamic From(object obj) { return new ExposedObject(obj); } public static T Cast(ExposedObject t) { return (T)t.m_object; } public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { // Get type args of the call Type[] typeArgs = ExposedObjectHelper.GetTypeArgs(binder); if (typeArgs != null && typeArgs.Length == 0) typeArgs = null; // // Try to call a non-generic instance method // if (typeArgs == null && m_instanceMethods.ContainsKey(binder.Name) && m_instanceMethods[binder.Name].ContainsKey(args.Length) && ExposedObjectHelper.InvokeBestMethod(args, m_object, m_instanceMethods[binder.Name][args.Length], out result)) { return true; } // // Try to call a generic instance method // if (m_instanceMethods.ContainsKey(binder.Name) && m_instanceMethods[binder.Name].ContainsKey(args.Length)) { List methods = new List(); if (m_genInstanceMethods.ContainsKey(binder.Name) && m_genInstanceMethods[binder.Name].ContainsKey(args.Length)) { foreach (var method in m_genInstanceMethods[binder.Name][args.Length]) { if (method.GetGenericArguments().Length == typeArgs.Length) { methods.Add(method.MakeGenericMethod(typeArgs)); } } } if (ExposedObjectHelper.InvokeBestMethod(args, m_object, methods, out result)) { return true; } } result = null; return false; } public override bool TrySetMember(SetMemberBinder binder, object value) { var propertyInfo = m_type.GetProperty( binder.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (propertyInfo != null) { propertyInfo.SetValue(m_object, value, null); return true; } var fieldInfo = m_type.GetField( binder.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (fieldInfo != null) { fieldInfo.SetValue(m_object, value); return true; } return false; } public override bool TryGetMember(GetMemberBinder binder, out object result) { var propertyInfo = m_object.GetType().GetProperty( binder.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (propertyInfo != null) { result = propertyInfo.GetValue(m_object, null); return true; } var fieldInfo = m_object.GetType().GetField( binder.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (fieldInfo != null) { result = fieldInfo.GetValue(m_object); return true; } result = null; return false; } public override bool TryConvert(ConvertBinder binder, out object result) { result = m_object; return true; } } } ================================================ FILE: test/Squirrel.Tests/TestHelpers/ExposedObjectHelper.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Dynamic; // Lovingly stolen from http://exposedobject.codeplex.com/ namespace Squirrel.Tests.TestHelpers { internal class ExposedObjectHelper { private static Type s_csharpInvokePropertyType = typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException) .Assembly .GetType("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder"); internal static bool InvokeBestMethod(object[] args, object target, List instanceMethods, out object result) { if (instanceMethods.Count == 1) { // Just one matching instance method - call it if (TryInvoke(instanceMethods[0], target, args, out result)) { return true; } } else if (instanceMethods.Count > 1) { // Find a method with best matching parameters MethodInfo best = null; Type[] bestParams = null; Type[] actualParams = args.Select(p => p == null ? typeof(object) : p.GetType()).ToArray(); Func isAssignableFrom = (a, b) => { for (int i = 0; i < a.Length; i++) { if (!a[i].IsAssignableFrom(b[i])) return false; } return true; }; foreach (var method in instanceMethods.Where(m => m.GetParameters().Length == args.Length)) { Type[] mParams = method.GetParameters().Select(x => x.ParameterType).ToArray(); if (isAssignableFrom(mParams, actualParams)) { if (best == null || isAssignableFrom(bestParams, mParams)) { best = method; bestParams = mParams; } } } if (best != null && TryInvoke(best, target, args, out result)) { return true; } } result = null; return false; } internal static bool TryInvoke(MethodInfo methodInfo, object target, object[] args, out object result) { try { result = methodInfo.Invoke(target, args); return true; } catch (TargetInvocationException) { } catch (TargetParameterCountException) { } result = null; return false; } internal static Type[] GetTypeArgs(InvokeMemberBinder binder) { if (s_csharpInvokePropertyType.IsInstanceOfType(binder)) { PropertyInfo typeArgsProperty = s_csharpInvokePropertyType.GetProperty("TypeArguments"); return ((IEnumerable)typeArgsProperty.GetValue(binder, null)).ToArray(); } return null; } } } ================================================ FILE: test/Squirrel.Tests/TestHelpers/IntegrationTestHelper.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; using Squirrel; using Squirrel.SimpleSplat; using Xunit; using System.Text; using SharpCompress.Archives.Zip; using SharpCompress.Readers; using SharpCompress.Common; using System.Reflection; namespace Squirrel.Tests.TestHelpers { public static class IntegrationTestHelper { public static string GetPath(params string[] paths) { var ret = GetIntegrationTestRootDirectory(); return (new FileInfo(paths.Aggregate (ret, Path.Combine))).FullName; } public static string GetIntegrationTestRootDirectory() { return Path.GetDirectoryName(new Uri(Assembly.GetCallingAssembly().CodeBase).LocalPath); } public static bool SkipTestOnXPAndVista() { int osVersion = Environment.OSVersion.Version.Major*100 + Environment.OSVersion.Version.Minor; return (osVersion < 601); } public static void RunBlockAsSTA(Action block) { Exception ex = null; var t = new Thread(() => { try { block(); } catch (Exception e) { ex = e; } }); t.SetApartmentState(ApartmentState.STA); t.Start(); t.Join(); if (ex != null) { // NB: If we don't do this, the test silently passes throw new Exception("", ex); } } static object gate = 42; public static IDisposable WithFakeInstallDirectory(string packageFileName, out string path) { var ret = Utility.WithTempDirectory(out path); File.Copy(GetPath("fixtures", packageFileName), Path.Combine(path, packageFileName)); var rp = ReleaseEntry.GenerateFromFile(Path.Combine(path, packageFileName)); ReleaseEntry.WriteReleaseFile(new[] { rp }, Path.Combine(path, "RELEASES")); return ret; } public static string CreateFakeInstalledApp(string version, string outputDir, string nuspecFile = null) { var targetDir = default(string); var nuget = IntegrationTestHelper.GetPath("nuget.exe"); nuspecFile = nuspecFile ?? "SquirrelInstalledApp.nuspec"; using (var clearTemp = Utility.WithTempDirectory(out targetDir)) { var nuspec = File.ReadAllText(IntegrationTestHelper.GetPath("fixtures", nuspecFile), Encoding.UTF8); File.WriteAllText(Path.Combine(targetDir, nuspecFile), nuspec.Replace("0.1.0", version), Encoding.UTF8); File.Copy( IntegrationTestHelper.GetPath("fixtures", "SquirrelAwareApp.exe"), Path.Combine(targetDir, "SquirrelAwareApp.exe")); File.Copy( IntegrationTestHelper.GetPath("fixtures", "NotSquirrelAwareApp.exe"), Path.Combine(targetDir, "NotSquirrelAwareApp.exe")); var psi = new ProcessStartInfo(nuget, "pack " + Path.Combine(targetDir, nuspecFile)) { RedirectStandardError = true, RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true, WorkingDirectory = targetDir, WindowStyle = ProcessWindowStyle.Hidden, }; var pi = Process.Start(psi); pi.WaitForExit(); var output = pi.StandardOutput.ReadToEnd(); var err = pi.StandardError.ReadToEnd(); Console.WriteLine(output); Console.WriteLine(err); var di = new DirectoryInfo(targetDir); var pkg = di.EnumerateFiles("*.nupkg").First(); var targetPkgFile = Path.Combine(outputDir, pkg.Name); File.Copy(pkg.FullName, targetPkgFile); return targetPkgFile; } } public static IDisposable WithFakeInstallDirectory(out string path) { return WithFakeInstallDirectory("SampleUpdatingApp.1.1.0.0.nupkg", out path); } public static IDisposable WithFakeAlreadyInstalledApp(out string path) { return WithFakeAlreadyInstalledApp("InstalledSampleUpdatingApp-1.1.0.0.zip", out path); } public static IDisposable WithFakeAlreadyInstalledApp(string zipFile, out string path) { var ret = Utility.WithTempDirectory(out path); // NB: Apparently Ionic.Zip is perfectly content to extract a Zip // file that doesn't actually exist, without failing. var zipPath = GetPath("fixtures", zipFile); Assert.True(File.Exists(zipPath)); var opts = new ExtractionOptions() { ExtractFullPath = true, Overwrite = true, PreserveFileTime = true }; using (var za = ZipArchive.Open(zipFile)) using (var reader = za.ExtractAllEntries()) { reader.WriteEntryToDirectory(path, opts); } return ret; } } } ================================================ FILE: test/Squirrel.Tests/TestHelpers/StaticHttpServer.cs ================================================ using System; using System.IO; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Squirrel.Tests { public sealed class StaticHttpServer : IDisposable { public int Port { get; private set; } public string RootPath { get; private set; } IDisposable inner; public StaticHttpServer(int port, string rootPath) { Port = port; RootPath = rootPath; } public IDisposable Start() { if (inner != null) { throw new InvalidOperationException("Already started!"); } var server = new HttpListener(); server.Prefixes.Add(String.Format("http://+:{0}/", Port)); server.Start(); bool shouldStop = false; var listener = Task.Run(async () => { while (!shouldStop) { var ctx = await server.GetContextAsync(); if (ctx.Request.HttpMethod != "GET") { closeResponseWith(ctx, 400, "GETs only"); return; } var target = Path.Combine(RootPath, ctx.Request.Url.AbsolutePath.Replace('/', Path.DirectorySeparatorChar).Substring(1)); var fi = new FileInfo(target); if (!fi.FullName.StartsWith(RootPath)) { closeResponseWith(ctx, 401, "Not authorized"); return; } if (!fi.Exists) { closeResponseWith(ctx, 404, "Not found"); return; } try { using (var input = File.OpenRead(target)) { ctx.Response.StatusCode = 200; input.CopyTo(ctx.Response.OutputStream); ctx.Response.Close(); } } catch (Exception ex) { closeResponseWith(ctx, 500, ex.ToString()); } } }); var ret = Disposable.Create(() => { shouldStop = true; server.Stop(); listener.Wait(2000); inner = null; }); inner = ret; return ret; } static void closeResponseWith(HttpListenerContext ctx, int statusCode, string message) { ctx.Response.StatusCode = statusCode; using (var sw = new StreamWriter(ctx.Response.OutputStream, Encoding.UTF8)) { sw.WriteLine(message); } ctx.Response.Close(); } public void Dispose() { var toDispose = Interlocked.Exchange(ref inner, null); if (toDispose != null) { toDispose.Dispose(); } } } } ================================================ FILE: test/Squirrel.Tests/UpdateManagerTests.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Win32; using Squirrel; using Squirrel.Tests.TestHelpers; using Xunit; using System.Net; using NuGet; namespace Squirrel.Tests { public class UpdateManagerTests { public class CreateUninstallerRegKeyTests { [Fact] public async Task CallingMethodTwiceShouldUpdateInstaller() { string remotePkgPath; string path; using (Utility.WithTempDirectory(out path)) { using (Utility.WithTempDirectory(out remotePkgPath)) using (var mgr = new UpdateManager(remotePkgPath, "theApp", path)) { IntegrationTestHelper.CreateFakeInstalledApp("1.0.0.1", remotePkgPath); await mgr.FullInstall(); } using (var mgr = new UpdateManager("http://lol", "theApp", path)) { await mgr.CreateUninstallerRegistryEntry(); var regKey = await mgr.CreateUninstallerRegistryEntry(); Assert.False(String.IsNullOrWhiteSpace((string)regKey.GetValue("DisplayName"))); mgr.RemoveUninstallerRegistryEntry(); } // NB: Squirrel-Aware first-run might still be running, slow // our roll before blowing away the temp path Thread.Sleep(1000); } var key = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default) .OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall"); using (key) { Assert.False(key.GetSubKeyNames().Contains("theApp")); } } } public class UpdateLocalReleasesTests { [Fact] public async Task UpdateLocalReleasesSmokeTest() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var appDir = Path.Combine(tempDir, "theApp"); var packageDir = Directory.CreateDirectory(Path.Combine(appDir, "packages")); new[] { "Squirrel.Core.1.0.0.0-full.nupkg", "Squirrel.Core.1.1.0.0-delta.nupkg", "Squirrel.Core.1.1.0.0-full.nupkg", }.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(tempDir, "theApp", "packages", x))); var fixture = new UpdateManager.ApplyReleasesImpl(appDir); await fixture.updateLocalReleasesFile(); var releasePath = Path.Combine(packageDir.FullName, "RELEASES"); File.Exists(releasePath).ShouldBeTrue(); var entries = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasePath, Encoding.UTF8)); entries.Count().ShouldEqual(3); } } [Fact] public async Task InitialInstallSmokeTest() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var remotePackageDir = Directory.CreateDirectory(Path.Combine(tempDir, "remotePackages")); var localAppDir = Path.Combine(tempDir, "theApp"); new[] { "Squirrel.Core.1.0.0.0-full.nupkg", }.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(remotePackageDir.FullName, x))); using (var fixture = new UpdateManager(remotePackageDir.FullName, "theApp", tempDir)) { await fixture.FullInstall(); } var releasePath = Path.Combine(localAppDir, "packages", "RELEASES"); File.Exists(releasePath).ShouldBeTrue(); var entries = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasePath, Encoding.UTF8)); entries.Count().ShouldEqual(1); new[] { "ReactiveUI.dll", "NSync.Core.dll", }.ForEach(x => File.Exists(Path.Combine(localAppDir, "app-1.0.0.0", x)).ShouldBeTrue()); } } [Fact(Skip = "This test is currently failing in CI")] public async Task SpecialCharactersInitialInstallTest() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var remotePackageDir = Directory.CreateDirectory(Path.Combine(tempDir, "remotePackages")); var localAppDir = Path.Combine(tempDir, "theApp"); new[] { "SpecialCharacters-0.1.0-full.nupkg", }.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(remotePackageDir.FullName, x))); using (var fixture = new UpdateManager(remotePackageDir.FullName, "theApp", tempDir)) { await fixture.FullInstall(); } var releasePath = Path.Combine(localAppDir, "packages", "RELEASES"); File.Exists(releasePath).ShouldBeTrue(); var entries = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasePath, Encoding.UTF8)); entries.Count().ShouldEqual(1); new[] { "file space name.txt" }.ForEach(x => File.Exists(Path.Combine(localAppDir, "app-0.1.0", x)).ShouldBeTrue()); } } [Fact] public async Task WhenBothFilesAreInSyncNoUpdatesAreApplied() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var appDir = Path.Combine(tempDir, "theApp"); var localPackages = Path.Combine(appDir, "packages"); var remotePackages = Path.Combine(tempDir, "releases"); Directory.CreateDirectory(localPackages); Directory.CreateDirectory(remotePackages); new[] { "Squirrel.Core.1.0.0.0-full.nupkg", "Squirrel.Core.1.1.0.0-delta.nupkg", "Squirrel.Core.1.1.0.0-full.nupkg", }.ForEach(x => { var path = IntegrationTestHelper.GetPath("fixtures", x); File.Copy(path, Path.Combine(localPackages, x)); File.Copy(path, Path.Combine(remotePackages, x)); }); var fixture = new UpdateManager.ApplyReleasesImpl(appDir); // sync both release files await fixture.updateLocalReleasesFile(); ReleaseEntry.BuildReleasesFile(remotePackages); // check for an update UpdateInfo updateInfo; using (var mgr = new UpdateManager(remotePackages, "theApp", tempDir, new FakeUrlDownloader())) { updateInfo = await mgr.CheckForUpdate(); } Assert.NotNull(updateInfo); Assert.Empty(updateInfo.ReleasesToApply); } } [Fact] public async Task WhenRemoteReleasesDoNotHaveDeltasNoUpdatesAreApplied() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var appDir = Path.Combine(tempDir, "theApp"); var localPackages = Path.Combine(appDir, "packages"); var remotePackages = Path.Combine(tempDir, "releases"); Directory.CreateDirectory(localPackages); Directory.CreateDirectory(remotePackages); new[] { "Squirrel.Core.1.0.0.0-full.nupkg", "Squirrel.Core.1.1.0.0-delta.nupkg", "Squirrel.Core.1.1.0.0-full.nupkg", }.ForEach(x => { var path = IntegrationTestHelper.GetPath("fixtures", x); File.Copy(path, Path.Combine(localPackages, x)); }); new[] { "Squirrel.Core.1.0.0.0-full.nupkg", "Squirrel.Core.1.1.0.0-full.nupkg", }.ForEach(x => { var path = IntegrationTestHelper.GetPath("fixtures", x); File.Copy(path, Path.Combine(remotePackages, x)); }); var fixture = new UpdateManager.ApplyReleasesImpl(appDir); // sync both release files await fixture.updateLocalReleasesFile(); ReleaseEntry.BuildReleasesFile(remotePackages); UpdateInfo updateInfo; using (var mgr = new UpdateManager(remotePackages, "theApp", tempDir, new FakeUrlDownloader())) { updateInfo = await mgr.CheckForUpdate(); } Assert.NotNull(updateInfo); Assert.Empty(updateInfo.ReleasesToApply); } } [Fact] public async Task WhenTwoRemoteUpdatesAreAvailableChoosesDeltaVersion() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var appDir = Path.Combine(tempDir, "theApp"); var localPackages = Path.Combine(appDir, "packages"); var remotePackages = Path.Combine(tempDir, "releases"); Directory.CreateDirectory(localPackages); Directory.CreateDirectory(remotePackages); new[] { "Squirrel.Core.1.0.0.0-full.nupkg", }.ForEach(x => { var path = IntegrationTestHelper.GetPath("fixtures", x); File.Copy(path, Path.Combine(localPackages, x)); }); new[] { "Squirrel.Core.1.0.0.0-full.nupkg", "Squirrel.Core.1.1.0.0-delta.nupkg", "Squirrel.Core.1.1.0.0-full.nupkg", }.ForEach(x => { var path = IntegrationTestHelper.GetPath("fixtures", x); File.Copy(path, Path.Combine(remotePackages, x)); }); var fixture = new UpdateManager.ApplyReleasesImpl(appDir); // sync both release files await fixture.updateLocalReleasesFile(); ReleaseEntry.BuildReleasesFile(remotePackages); using (var mgr = new UpdateManager(remotePackages, "theApp", tempDir, new FakeUrlDownloader())) { UpdateInfo updateInfo; updateInfo = await mgr.CheckForUpdate(); Assert.True(updateInfo.ReleasesToApply.First().IsDelta); updateInfo = await mgr.CheckForUpdate(ignoreDeltaUpdates: true); Assert.False(updateInfo.ReleasesToApply.First().IsDelta); } } } [Fact] public async Task WhenFolderDoesNotExistThrowHelpfulError() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var directory = Path.Combine(tempDir, "missing-folder"); var fixture = new UpdateManager(directory, "MyAppName"); using (fixture) { await Assert.ThrowsAsync(() => fixture.CheckForUpdate()); } } } [Fact] public async Task WhenReleasesFileDoesntExistThrowACustomError() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var fixture = new UpdateManager(tempDir, "MyAppName"); using (fixture) { await Assert.ThrowsAsync(() => fixture.CheckForUpdate()); } } } [Fact] public async Task WhenReleasesFileIsBlankThrowAnException() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { var fixture = new UpdateManager(tempDir, "MyAppName"); File.WriteAllText(Path.Combine(tempDir, "RELEASES"), ""); using (fixture) { await Assert.ThrowsAsync(typeof(Exception), () => fixture.CheckForUpdate()); } } } [Fact] public async Task WhenUrlResultsInWebExceptionWeShouldThrow() { // This should result in a WebException (which gets caught) unless you can actually access http://lol using (var fixture = new UpdateManager("http://lol", "theApp")) { await Assert.ThrowsAsync(typeof(WebException), () => fixture.CheckForUpdate()); } } [Theory] [InlineData("C:\\Foo\\Bar\\Test.exe", default(string))] [InlineData("%LocalAppData%\\theApp\\app-1.0.0.1\\Test.exe", "1.0.0.1")] [InlineData("%LocalAppData%\\aDifferentApp\\app-1.0.0.1\\Test.exe", default(string))] public void CurrentlyInstalledVersionTests(string input, string expectedVersion) { input = Environment.ExpandEnvironmentVariables(input); var expected = expectedVersion != null ? new SemanticVersion(expectedVersion) : default(SemanticVersion); using (var fixture = new UpdateManager("http://lol", "theApp")) { Assert.Equal(expected, fixture.CurrentlyInstalledVersion(input)); } } [Theory] [InlineData(0, 0, 25, 0)] [InlineData(12, 0, 25, 3)] [InlineData(55, 0, 25, 13)] [InlineData(100, 0, 25, 25)] [InlineData(0, 25, 50, 25)] [InlineData(12, 25, 50, 28)] [InlineData(55, 25, 50, 38)] [InlineData(100, 25, 50, 50)] public void CalculatesPercentageCorrectly(int percentageOfCurrentStep, int stepStartPercentage, int stepEndPercentage, int expectedPercentage) { var percentage = UpdateManager.CalculateProgress(percentageOfCurrentStep, stepStartPercentage, stepEndPercentage); Assert.Equal(expectedPercentage, percentage); } [Fact] public void CalculatesPercentageCorrectlyForUpdateExe() { // Note: this mimicks the update.exe progress reporting of multiple steps var progress = new List(); // 3 % (3 stages), check for updates foreach (var step in new [] { 0, 33, 66, 100 }) { progress.Add(UpdateManager.CalculateProgress(step, 0, 3)); Assert.InRange(progress.Last(), 0, 3); } Assert.Equal(3, progress.Last()); // 3 - 30 %, download releases for (var step = 0; step <= 100; step++) { progress.Add(UpdateManager.CalculateProgress(step, 3, 30)); Assert.InRange(progress.Last(), 3, 30); } Assert.Equal(30, progress.Last()); // 30 - 100 %, apply releases for (var step = 0; step <= 100; step++) { progress.Add(UpdateManager.CalculateProgress(step, 30, 100)); Assert.InRange(progress.Last(), 30, 100); } Assert.Equal(100, progress.Last()); } } } } ================================================ FILE: test/Squirrel.Tests/UtilityTests.cs ================================================ using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using Squirrel.SimpleSplat; using Squirrel; using Squirrel.Tests.TestHelpers; using Xunit; using Squirrel.Shell; namespace Squirrel.Tests.Core { public class UtilityTests : IEnableLogger { [Fact] public void SetAppIdOnShortcutTest() { var sl = new ShellLink() { Target = @"C:\Windows\Notepad.exe", Description = "It's Notepad", }; sl.SetAppUserModelId("org.anaïsbetts.test"); var path = Path.GetFullPath(@".\test.lnk"); sl.Save(path); Console.WriteLine("Saved to " + path); } [Theory] [InlineData(10, 1)] [InlineData(100, 1)] [InlineData(0, 1)] [InlineData(30000, 2)] [InlineData(50000, 2)] [InlineData(10000000, 3)] public void TestTempNameGeneration(int index, int expectedLength) { string result = Utility.tempNameForIndex(index, ""); Assert.Equal(result.Length, expectedLength); } [Fact] public void RemoveByteOrderMarkerIfPresent() { var utf32Be = new byte[] { 0x00, 0x00, 0xFE, 0xFF }; var utf32Le = new byte[] { 0xFF, 0xFE, 0x00, 0x00 }; var utf16Be = new byte[] { 0xFE, 0xFF }; var utf16Le = new byte[] { 0xFF, 0xFE }; var utf8 = new byte[] { 0xEF, 0xBB, 0xBF }; var utf32BeHelloWorld = combine(utf32Be, Encoding.UTF8.GetBytes("hello world")); var utf32LeHelloWorld = combine(utf32Le, Encoding.UTF8.GetBytes("hello world")); var utf16BeHelloWorld = combine(utf16Be, Encoding.UTF8.GetBytes("hello world")); var utf16LeHelloWorld = combine(utf16Le, Encoding.UTF8.GetBytes("hello world")); var utf8HelloWorld = combine(utf8, Encoding.UTF8.GetBytes("hello world")); var asciiMultipleChars = Encoding.ASCII.GetBytes("hello world"); var asciiSingleChar = Encoding.ASCII.GetBytes("A"); var emptyString = string.Empty; string nullString = null; byte[] nullByteArray = {}; Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(emptyString)); Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(nullString)); Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(nullByteArray)); Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf32Be)); Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf32Le)); Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf16Be)); Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf16Le)); Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf8)); Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf32BeHelloWorld)); Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf32LeHelloWorld)); Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf16BeHelloWorld)); Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf16LeHelloWorld)); Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf8HelloWorld)); Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(asciiMultipleChars)); Assert.Equal("A", Utility.RemoveByteOrderMarkerIfPresent(asciiSingleChar)); } [Fact] public void ShaCheckShouldBeCaseInsensitive() { var sha1FromExternalTool = "75255cfd229a1ed1447abe1104f5635e69975d30"; var inputPackage = IntegrationTestHelper.GetPath("fixtures", "Squirrel.Core.1.0.0.0.nupkg"); var stream = File.OpenRead(inputPackage); var sha1 = Utility.CalculateStreamSHA1(stream); Assert.NotEqual(sha1FromExternalTool, sha1); Assert.Equal(sha1FromExternalTool, sha1, StringComparer.OrdinalIgnoreCase); } [Fact(Skip="This test takes forever")] public void CanDeleteDeepRecursiveDirectoryStructure() { string tempDir; using (Utility.WithTempDirectory(out tempDir)) { for (var i = 0; i < 50; i++) { var directory = Path.Combine(tempDir, newId()); CreateSampleDirectory(directory); } var files = Directory.GetFiles(tempDir, "*", SearchOption.AllDirectories); var count = files.Count(); this.Log().Info("Created {0} files under directory {1}", count, tempDir); var sw = new Stopwatch(); sw.Start(); Utility.DeleteDirectory(tempDir).Wait(); sw.Stop(); this.Log().Info("Delete took {0}ms", sw.ElapsedMilliseconds); Assert.False(Directory.Exists(tempDir)); } } [Fact] public void CreateFakePackageSmokeTest() { string path; using (Utility.WithTempDirectory(out path)) { var output = IntegrationTestHelper.CreateFakeInstalledApp("0.3.0", path); Assert.True(File.Exists(output)); } } [Theory] [InlineData("foo.dll", true)] [InlineData("foo.DlL", true)] [InlineData("C:\\Foo\\Bar\\foo.Exe", true)] [InlineData("Test.png", false)] [InlineData(".rels", false)] public void FileIsLikelyPEImageTest(string input, bool result) { Assert.Equal(result, Utility.FileIsLikelyPEImage(input)); } [Theory] [InlineData("C:\\Users\\bob\\temp\\pkgPath\\lib\\net45\\foo.exe", "C:\\Users\\bob\\temp\\pkgPath", true)] [InlineData("C:\\Users\\bob\\temp\\pkgPath\\lib\\net45\\node_modules\\foo.exe", "C:\\Users\\bob\\temp\\pkgPath", false)] [InlineData("C:\\Users\\bob\\temp\\pkgPath\\lib\\net45\\node_modules\\foo\\foo.exe", "C:\\Users\\bob\\temp\\pkgPath", false)] [InlineData("foo.png", "C:\\Users\\bob\\temp\\pkgPath", false)] public void IsFileTopLevelInPackageTest(string input, string packagePath, bool result) { Assert.Equal(result, Utility.IsFileTopLevelInPackage(input, packagePath)); } [Fact] public void WeCanFetchAllProcesses() { var result = UnsafeUtility.EnumerateProcesses(); Assert.True(result.Count > 1); Assert.True(result.Count != 2048); } static void CreateSampleDirectory(string directory) { Random prng = new Random(); while (true) { Directory.CreateDirectory(directory); for (var j = 0; j < 100; j++) { var file = Path.Combine(directory, newId()); if (file.Length > 260) continue; File.WriteAllText(file, Guid.NewGuid().ToString()); } if (prng.NextDouble() > 0.5) { var childDirectory = Path.Combine(directory, newId()); if (childDirectory.Length > 248) return; directory = childDirectory; continue; } break; } } static string newId() { var text = Guid.NewGuid().ToString(); var bytes = Encoding.Unicode.GetBytes(text); var provider = new SHA1Managed(); var hashString = string.Empty; foreach (var x in provider.ComputeHash(bytes)) { hashString += String.Format("{0:x2}", x); } if (hashString.Length > 7) { return hashString.Substring(0, 7); } return hashString; } static byte[] combine(params byte[][] arrays) { var rv = new byte[arrays.Sum(a => a.Length)]; var offset = 0; foreach (var array in arrays) { Buffer.BlockCopy(array, 0, rv, offset, array.Length); offset += array.Length; } return rv; } } } ================================================ FILE: test/Squirrel.Tests/fixtures/RELEASES-OnePointOh ================================================ 94689fede03fed7ab59c24337673a27837f0c3ec MyCoolApp-1.0.nupkg 1004502 ================================================ FILE: test/Squirrel.Tests/fixtures/RELEASES-OnePointOne ================================================ 94689fede03fed7ab59c24337673a27837f0c3ec MyCoolApp-1.0.nupkg 1004502 3a2eadd15dd984e4559f2b4d790ec8badaeb6a39 MyCoolApp-1.1.nupkg 1040561 14db31d2647c6d2284882a2e101924a9c409ee67 MyCoolApp-1.1-delta.nupkg 80396 ================================================ FILE: test/Squirrel.Tests/fixtures/Squirrel.Core.1.1.0.0.nuspec ================================================ 1.1.0.0 Administrator Administrator NSync.Core false Description ## Release Notes for 1.1 1. Did some cool stuff 1. Did another thing 1. Did a third thing, pretty crazy! Make *sure* to refrobulate the confabulator or **terrible things will happen!** ================================================ FILE: test/Squirrel.Tests/fixtures/SquirrelInstalledApp.nuspec ================================================  SquirrelInstalledApp 0.1.0 Anaïs Betts false My package description. ================================================ FILE: test/Squirrel.Tests/fixtures/content-types/basic-merged.xml ================================================ ================================================ FILE: test/Squirrel.Tests/fixtures/content-types/basic.xml ================================================ ================================================ FILE: test/Squirrel.Tests/fixtures/content-types/complex-merged.xml ================================================ ================================================ FILE: test/Squirrel.Tests/fixtures/content-types/complex.xml ================================================ ================================================ FILE: test/Squirrel.Tests/fixtures/slack-1.1.8-full.nupkg ================================================ [File too large to display: 56.2 MB] ================================================ FILE: test/test_official.cmd ================================================ @echo off setlocal pushd %~dp0 set _C=Release set _S=src\build_official.cmd :parse_args if /i "%1"=="debug" (set _C=Debug) & (set _S=devbuild.cmd) if not "%1"=="" shift & goto parse_args :: Init if "%VCToolsVersion%"=="" call :StartDeveloperCommandPrompt || exit /b :: Test if not exist ..\build\%_C%\test\net45\Squirrel.Tests.dll (echo Run %_S% before running test\test_official.cmd) & (exit -1) VSTest.Console.exe ..\build\%_C%\test\net45\*.Tests.dll || exit /b goto LExit :StartDeveloperCommandPrompt if not "%SquirrelSkipVsDevCmd%"=="" ( echo Skipping initializing developer command prompt exit /b ) echo Initializing developer command prompt if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" ( "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" exit /b 2 ) for /f "usebackq delims=" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version [16.0^,18.0^) -property installationPath`) do ( if exist "%%i\Common7\Tools\vsdevcmd.bat" ( call "%%i\Common7\Tools\vsdevcmd.bat" -no_logo exit /b ) echo developer command prompt not found in %%i ) echo No versions of developer command prompt found exit /b 2 :LExit popd endlocal ================================================ FILE: vendor/wix/candle.exe.config ================================================ ================================================ FILE: vendor/wix/light.exe.config ================================================ ================================================ FILE: vendor/wix/template.wxs ================================================ ================================================ FILE: version.json ================================================ { "version": "2.0.1", "publicReleaseRefSpec": [ "^refs/heads/master$" ], "cloudBuild": { "buildNumber": { "enabled": false } } }