├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── ci ├── azure-pipelines.yml └── build-win32.yml ├── config.gypi ├── gyp_library.py ├── main.gyp ├── scripts └── generate_pkg.py ├── src ├── explorer_command.cc └── explorer_command.def └── template └── AppxManifest.xml /.gitignore: -------------------------------------------------------------------------------- 1 | out/* -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "node-gyp"] 2 | path = node-gyp 3 | url = https://github.com/nodejs/node-gyp 4 | [submodule "deps/wil"] 5 | path = deps/wil 6 | url = https://github.com/microsoft/wil 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to 4 | agree to a Contributor License Agreement (CLA) declaring that you have the right to, 5 | and actually do, grant us the rights to use your contribution. For details, visit 6 | https://cla.microsoft.com. 7 | 8 | When you submit a pull request, a CLA-bot will automatically determine whether you need 9 | to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the 10 | instructions provided by the bot. You will only need to do this once across all repositories using our CLA. 11 | 12 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 13 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 14 | or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | NOTICES 2 | 3 | This repository incorporates material as listed below or described in the code. 4 | 5 | Component. microsoft/wil (https://github.com/microsoft/wil) 6 | 7 | Open Source License/Copyright Notice. 8 | 9 | MIT License 10 | 11 | Copyright (c) Microsoft Corporation. All rights reserved. 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE 30 | 31 | Component. nodejs/node-gyp (https://github.com/nodejs/node-gyp) 32 | 33 | Open Source License/Copyright Notice. 34 | 35 | (The MIT License) 36 | 37 | Copyright (c) 2012 Nathan Rajlich 38 | 39 | Permission is hereby granted, free of charge, to any person 40 | obtaining a copy of this software and associated documentation 41 | files (the "Software"), to deal in the Software without 42 | restriction, including without limitation the rights to use, 43 | copy, modify, merge, publish, distribute, sublicense, and/or sell 44 | copies of the Software, and to permit persons to whom the 45 | Software is furnished to do so, subject to the following 46 | conditions: 47 | 48 | The above copyright notice and this permission notice shall be 49 | included in all copies or substantial portions of the Software. 50 | 51 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 52 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 53 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 54 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 55 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 56 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 57 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 58 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vscode-explorer-command 2 | 3 | > Generates shell extension dll for x86, x64 and arm64 windows 4 | 5 | Allows extending the Windows 11 context menu once published as a 6 | sparse package. This repository is only responsible for creating 7 | the shell extensions and unsigned sparse package, `microsoft/vscode` is responsible 8 | for code signing and installing it through Inno. 9 | 10 | ## Contributing 11 | 12 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 13 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 14 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 15 | 16 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 17 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 18 | provided by the bot. You will only need to do this once across all repos using our CLA. 19 | 20 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 21 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 22 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 23 | 24 | ## Trademarks 25 | 26 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft 27 | trademarks or logos is subject to and must follow 28 | [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). 29 | Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. 30 | Any use of third-party trademarks or logos are subject to those third-party's policies. 31 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # TODO: The maintainer of this repo has not yet edited this file 2 | 3 | **REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? 4 | 5 | - **No CSS support:** Fill out this template with information about how to file issues and get help. 6 | - **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps. 7 | - **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide. 8 | 9 | *Then remove this first heading from this SUPPORT.MD file before publishing your repo.* 10 | 11 | # Support 12 | 13 | ## How to file issues and get help 14 | 15 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 16 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 17 | feature request as a new Issue. 18 | 19 | For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE 20 | FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER 21 | CHANNEL. WHERE WILL YOU HELP PEOPLE?**. 22 | 23 | ## Microsoft Support Policy 24 | 25 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above. 26 | -------------------------------------------------------------------------------- /ci/azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | branches: 3 | include: ['*'] 4 | tags: 5 | include: ['*'] 6 | 7 | jobs: 8 | - job: Windows 9 | pool: 10 | vmImage: windows-latest 11 | variables: 12 | arch: x64 13 | steps: 14 | - template: build-win32.yml 15 | 16 | - job: Windows32 17 | pool: 18 | vmImage: windows-latest 19 | variables: 20 | arch: x86 21 | steps: 22 | - template: build-win32.yml 23 | 24 | - job: WindowsArm64 25 | pool: 26 | vmImage: windows-latest 27 | variables: 28 | arch: arm64 29 | steps: 30 | - template: build-win32.yml 31 | -------------------------------------------------------------------------------- /ci/build-win32.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: UsePythonVersion@0 3 | inputs: 4 | versionSpec: "3.x" 5 | addToPath: true 6 | 7 | - powershell: | 8 | git submodule update --init 9 | $env:GYP_MSVS_VERSION="2022" 10 | python3 .\gyp_library.py $(arch) 11 | #Dir -Recurse . | Get-Childitem 12 | displayName: Generate project solution 13 | 14 | - task: MSBuild@1 15 | inputs: 16 | solution: out/main.sln 17 | msbuildArchitecture: $(arch) 18 | 19 | - powershell: | 20 | python3 .\scripts\generate_pkg.py code $(arch) .\template\AppxManifest.xml 21 | python3 .\scripts\generate_pkg.py code_insiders $(arch) .\template\AppxManifest.xml 22 | # 23 | # Add Windows SDK to path 24 | # 25 | $sdk = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x64" 26 | #echo $makeappx 27 | #Dir -Recurse "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\$(arch)" | Get-Childitem 28 | $env:Path += ';' + $sdk 29 | # 30 | # Create Sparse Package for stable and insiders 31 | # 32 | makeappx pack /d "out/code_explorer_pkg_$(arch)" /p "out/code_explorer_$(arch).appx" /nv 33 | makeappx pack /d "out/code_insiders_explorer_pkg_$(arch)" /p "out/code_insiders_explorer_$(arch).appx" /nv 34 | # 35 | # Create final packages for release 36 | # 37 | Compress-Archive -LiteralPath "out/code_explorer_$(arch).appx","out/Default/code_explorer_command.dll" -DestinationPath "out/code_explorer_$(arch).zip" 38 | Compress-Archive -LiteralPath "out/code_insiders_explorer_$(arch).appx","out/Default/code_insiders_explorer_command.dll" -DestinationPath "out/code_insiders_explorer_$(arch).zip" 39 | displayName: Create sparse pkg 40 | 41 | - task: PublishPipelineArtifact@0 42 | inputs: 43 | artifactName: code_explorer_$(arch) 44 | targetPath: out/code_explorer_$(arch).zip 45 | displayName: "Publish Stable Release artifacts" 46 | 47 | - task: PublishPipelineArtifact@0 48 | inputs: 49 | artifactName: code_insiders_explorer_$(arch) 50 | targetPath: out/code_insiders_explorer_$(arch).zip 51 | displayName: "Publish Insider Release artifacts" 52 | 53 | - task: PublishPipelineArtifact@0 54 | inputs: 55 | artifactName: output-$(arch)-debug 56 | targetPath: out/Default 57 | displayName: "Publish build artifacts" 58 | -------------------------------------------------------------------------------- /config.gypi: -------------------------------------------------------------------------------- 1 | {'variables': {'target_arch': 'x86'}} -------------------------------------------------------------------------------- /gyp_library.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from __future__ import print_function 3 | import ast 4 | import os 5 | import pprint 6 | import sys 7 | 8 | root = os.path.dirname(__file__) 9 | 10 | sys.path.insert(0, os.path.join(root, 'node-gyp', 'gyp', 'pylib')) 11 | import gyp 12 | 13 | def edit_config_gypi(arch): 14 | config_gypi = os.path.join(root, 'config.gypi') 15 | with open(config_gypi, 'r') as f: 16 | content = f.read() 17 | config = ast.literal_eval(content) 18 | v = config['variables'] 19 | v['target_arch'] = arch 20 | with open(config_gypi, 'w+') as f: 21 | f.write(pprint.pformat(config, indent=2)) 22 | 23 | def edit_main_gyp(arch): 24 | main_gyp = os.path.join(root, 'main.gyp') 25 | with open(main_gyp, 'r') as f: 26 | content = f.read() 27 | config = ast.literal_eval(content) 28 | v = config['target_defaults'] 29 | v['msvs_configuration_platform'] = 'ARM64' 30 | with open(main_gyp, 'w+') as f: 31 | f.write(pprint.pformat(config, indent=2)) 32 | 33 | def run_gyp(arch, args): 34 | edit_config_gypi(arch) 35 | if arch == 'arm64': 36 | edit_main_gyp(arch) 37 | 38 | args.append('main.gyp') 39 | args.extend(['-I', 'config.gypi']) 40 | args.append('--depth=.') 41 | args.append('--generator-output=out') 42 | args.append('-Dlibrary=shared_library') 43 | 44 | rc = gyp.main(args) 45 | if rc != 0: 46 | print('Error running GYP') 47 | sys.exit(rc) 48 | 49 | 50 | if __name__ == '__main__': 51 | run_gyp(sys.argv[1], sys.argv[2:]) -------------------------------------------------------------------------------- /main.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'target_defaults': { 3 | 'conditions': [ 4 | [ 'OS=="win"', { 5 | 'sources': [ 6 | 'src/explorer_command.cc', 7 | 'src/explorer_command.def', 8 | ], 9 | 'include_dirs': [ 10 | 'deps/wil/include', 11 | ], 12 | 'defines': [ 13 | '_WINDLL', 14 | 'WIN32_LEAN_AND_MEAN', 15 | '_UNICODE', 16 | 'UNICODE', 17 | '_CRT_SECURE_NO_DEPRECATE', 18 | '_CRT_NONSTDC_NO_DEPRECATE', 19 | ], 20 | 'msvs_settings': { 21 | 'VCLinkerTool': { 22 | 'AdditionalOptions': [ 23 | '/guard:cf', 24 | ], 25 | 'OptimizeReferences': 2, # /OPT:REF 26 | 'EnableCOMDATFolding': 2, # /OPT:ICF 27 | }, 28 | 'VCCLCompilerTool': { 29 | 'AdditionalOptions': [ 30 | '/Zc:__cplusplus', 31 | '-std:c++17', 32 | '/Qspectre', 33 | '/guard:cf', 34 | ], 35 | 'BufferSecurityCheck': 'true', 36 | 'ExceptionHandling': 1, # /EHsc 37 | 'EnableFunctionLevelLinking': 'true', 38 | 'Optimization': 3, # /Ox, full optimization 39 | }, 40 | }, 41 | 'libraries': [ 42 | '-ladvapi32.lib', 43 | '-lruntimeobject.lib', 44 | '-lshlwapi.lib', 45 | '-lonecore.lib', 46 | ] 47 | }], 48 | ], 49 | }, 50 | 'targets': [{ 51 | 'target_name': 'code_explorer_command', 52 | 'type': 'shared_library', 53 | 'defines': [ 54 | 'EXE_NAME="Code.exe"', 55 | ], 56 | 'conditions': [ 57 | [ 'OS=="win"', { 58 | 'conditions': [ 59 | ['target_arch=="x86"', { 60 | 'TargetMachine' : 1, # /MACHINE:X86 61 | 'defines': [ 62 | 'DLL_UUID="0632BBFB-D195-4972-B458-53ADEB984588"', 63 | ], 64 | }], 65 | ['target_arch=="x64"', { 66 | 'TargetMachine' : 17, # /MACHINE:X64 67 | 'defines': [ 68 | 'DLL_UUID="1C6DF0C0-192A-4451-BE36-6A59A86A692E"', 69 | ], 70 | }], 71 | ['target_arch=="arm64"', { 72 | 'TargetMachine' : 18, # /MACHINE:ARM64 https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.vcprojectengine.machinetypeoption?view=visualstudiosdk-2022 73 | 'defines': [ 74 | 'DLL_UUID="F5EA5883-1DA8-4A05-864A-D5DE2D2B2854"', 75 | ], 76 | }], 77 | ], 78 | }], 79 | ], 80 | }, { 81 | 'target_name': 'code_insiders_explorer_command', 82 | 'type': 'shared_library', 83 | 'defines': [ 84 | 'EXE_NAME="Code - Insiders.exe"', 85 | 'INSIDER=1', 86 | ], 87 | 'conditions': [ 88 | [ 'OS=="win"', { 89 | 'conditions': [ 90 | ['target_arch=="x86"', { 91 | 'TargetMachine' : 1, # /MACHINE:X86 92 | 'defines': [ 93 | 'DLL_UUID="B9949795-B37D-457F-ADDE-6A950EF85CA7"', 94 | ], 95 | }], 96 | ['target_arch=="x64"', { 97 | 'TargetMachine' : 17, # /MACHINE:X64 98 | 'defines': [ 99 | 'DLL_UUID="799F4F7E-5934-4001-A74C-E207F44F05B8"', 100 | ], 101 | }], 102 | ['target_arch=="arm64"', { 103 | 'TargetMachine' : 18, # /MACHINE:ARM64 104 | 'defines': [ 105 | 'DLL_UUID="7D34756D-32DD-4EE6-B99F-2691C0DAD875"', 106 | ], 107 | }], 108 | ], 109 | }], 110 | ], 111 | }], 112 | } -------------------------------------------------------------------------------- /scripts/generate_pkg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from __future__ import print_function 3 | from shutil import copy 4 | import os 5 | import sys 6 | 7 | code_clsid_map = { 8 | 'x86': '0632BBFB-D195-4972-B458-53ADEB984588', 9 | 'x64': '1C6DF0C0-192A-4451-BE36-6A59A86A692E', 10 | 'arm64': 'F5EA5883-1DA8-4A05-864A-D5DE2D2B2854' 11 | } 12 | 13 | code_insiders_clsid_map = { 14 | 'x86': 'B9949795-B37D-457F-ADDE-6A950EF85CA7', 15 | 'x64': '799F4F7E-5934-4001-A74C-E207F44F05B8', 16 | 'arm64': '7D34756D-32DD-4EE6-B99F-2691C0DAD875' 17 | } 18 | 19 | root = os.path.dirname(os.path.dirname(__file__)) 20 | out_dir = os.path.join(root, 'out') 21 | pkg_type = sys.argv[1] 22 | arch = sys.argv[2] 23 | pkg_dir = os.path.join(out_dir, pkg_type + '_explorer_pkg_' + arch) 24 | 25 | # Create output directory. 26 | os.mkdir(pkg_dir) 27 | 28 | # Update AppxManifest. 29 | manifest = os.path.join(root, 'template', 'AppxManifest.xml') 30 | with open(manifest, 'r') as f: 31 | content = f.read() 32 | content = content.replace('@@PackageDLL@@', pkg_type + '_explorer_command.dll') 33 | content = content.replace('@@PackageDescription@@', pkg_type + ' context menu handler') 34 | if pkg_type == 'code': 35 | content = content.replace('@@PackageName@@', 'Microsoft.VSCode') 36 | content = content.replace('@@PackageDisplayName@@', 'Visual Studio Code') 37 | content = content.replace('@@Application@@', 'Code.exe') 38 | content = content.replace('@@ApplicationIdShort@@', 'Code') 39 | content = content.replace('@@MenuID@@', 'OpenWithCode') 40 | content = content.replace('@@CLSID@@', code_clsid_map[arch]) 41 | if pkg_type == 'code_insiders': 42 | content = content.replace('@@PackageName@@', 'Microsoft.VSCodeInsiders') 43 | content = content.replace('@@PackageDisplayName@@', 'Visual Studio Code - Insiders') 44 | content = content.replace('@@Application@@', 'Code - Insiders.exe') 45 | content = content.replace('@@ApplicationIdShort@@', 'CodeInsiders') 46 | content = content.replace('@@MenuID@@', 'OpenWithCodeInsiders') 47 | content = content.replace('@@CLSID@@', code_insiders_clsid_map[arch]) 48 | 49 | # Copy AppxManifest file to the package directory. 50 | manifest_output = os.path.join(pkg_dir, 'AppxManifest.xml') 51 | with open(manifest_output, 'w+') as f: 52 | f.write(content) -------------------------------------------------------------------------------- /src/explorer_command.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "wil/stl.h" 17 | #include "wil/filesystem.h" 18 | #include "wil/win32_helpers.h" 19 | 20 | using Microsoft::WRL::ClassicCom; 21 | using Microsoft::WRL::ComPtr; 22 | using Microsoft::WRL::InhibitRoOriginateError; 23 | using Microsoft::WRL::Module; 24 | using Microsoft::WRL::ModuleType; 25 | using Microsoft::WRL::RuntimeClass; 26 | using Microsoft::WRL::RuntimeClassFlags; 27 | 28 | extern "C" BOOL WINAPI DllMain(HINSTANCE instance, 29 | DWORD reason, 30 | LPVOID reserved) { 31 | switch (reason) { 32 | case DLL_PROCESS_ATTACH: 33 | case DLL_PROCESS_DETACH: 34 | case DLL_THREAD_ATTACH: 35 | case DLL_THREAD_DETACH: 36 | break; 37 | } 38 | 39 | return true; 40 | } 41 | 42 | namespace { 43 | // Extracted from 44 | // https://source.chromium.org/chromium/chromium/src/+/main:base/command_line.cc;l=109-159 45 | 46 | std::wstring QuoteForCommandLineArg(const std::wstring& arg) { 47 | // We follow the quoting rules of CommandLineToArgvW. 48 | // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx 49 | std::wstring quotable_chars(L" \\\""); 50 | if (arg.find_first_of(quotable_chars) == std::wstring::npos) { 51 | // No quoting necessary. 52 | return arg; 53 | } 54 | 55 | std::wstring out; 56 | out.push_back('"'); 57 | for (size_t i = 0; i < arg.size(); ++i) { 58 | if (arg[i] == '\\') { 59 | // Find the extent of this run of backslashes. 60 | size_t start = i, end = start + 1; 61 | for (; end < arg.size() && arg[end] == '\\'; ++end) {} 62 | size_t backslash_count = end - start; 63 | 64 | // Backslashes are escapes only if the run is followed by a double quote. 65 | // Since we also will end the string with a double quote, we escape for 66 | // either a double quote or the end of the string. 67 | if (end == arg.size() || arg[end] == '"') { 68 | // To quote, we need to output 2x as many backslashes. 69 | backslash_count *= 2; 70 | } 71 | for (size_t j = 0; j < backslash_count; ++j) 72 | out.push_back('\\'); 73 | 74 | // Advance i to one before the end to balance i++ in loop. 75 | i = end - 1; 76 | } else if (arg[i] == '"') { 77 | out.push_back('\\'); 78 | out.push_back('"'); 79 | } else { 80 | out.push_back(arg[i]); 81 | } 82 | } 83 | out.push_back('"'); 84 | 85 | return out; 86 | } 87 | 88 | } 89 | 90 | class __declspec(uuid(DLL_UUID)) ExplorerCommandHandler final : public RuntimeClass, IExplorerCommand> { 91 | public: 92 | // IExplorerCommand implementation: 93 | IFACEMETHODIMP GetTitle(IShellItemArray* items, PWSTR* name) { 94 | const size_t kMaxStringLength = 1024; 95 | wchar_t value_w[kMaxStringLength]; 96 | wchar_t expanded_value_w[kMaxStringLength]; 97 | DWORD value_size_w = sizeof(value_w); 98 | #if defined(INSIDER) 99 | const wchar_t kTitleRegkey[] = L"Software\\Classes\\VSCodeInsidersContextMenu"; 100 | #else 101 | const wchar_t kTitleRegkey[] = L"Software\\Classes\\VSCodeContextMenu"; 102 | #endif 103 | HKEY subhkey = nullptr; 104 | LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, kTitleRegkey, 0, KEY_READ, &subhkey); 105 | if (result != ERROR_SUCCESS) { 106 | result = RegOpenKeyEx(HKEY_CURRENT_USER, kTitleRegkey, 0, KEY_READ, &subhkey); 107 | } 108 | 109 | DWORD type = REG_EXPAND_SZ; 110 | RegQueryValueEx(subhkey, L"Title", nullptr, &type, 111 | reinterpret_cast(&value_w), &value_size_w); 112 | RegCloseKey(subhkey); 113 | value_size_w = ExpandEnvironmentStrings(value_w, expanded_value_w, kMaxStringLength); 114 | return (value_size_w && value_size_w < kMaxStringLength) 115 | ? SHStrDup(expanded_value_w, name) 116 | : SHStrDup(L"UnExpected Title", name); 117 | } 118 | 119 | IFACEMETHODIMP GetIcon(IShellItemArray* items, PWSTR* icon) { 120 | std::filesystem::path module_path{ wil::GetModuleFileNameW(wil::GetModuleInstanceHandle()) }; 121 | module_path = module_path.remove_filename().parent_path().parent_path(); 122 | module_path /= EXE_NAME; 123 | return SHStrDupW(module_path.c_str(), icon); 124 | } 125 | 126 | IFACEMETHODIMP GetToolTip(IShellItemArray* items, PWSTR* infoTip) { 127 | *infoTip = nullptr; 128 | return E_NOTIMPL; 129 | } 130 | 131 | IFACEMETHODIMP GetCanonicalName(GUID* guidCommandName) { 132 | *guidCommandName = GUID_NULL; 133 | return S_OK; 134 | } 135 | 136 | IFACEMETHODIMP GetState(IShellItemArray* items, BOOL okToBeSlow, EXPCMDSTATE* cmdState) { 137 | *cmdState = ECS_ENABLED; 138 | return S_OK; 139 | } 140 | 141 | IFACEMETHODIMP GetFlags(EXPCMDFLAGS* flags) { 142 | *flags = ECF_DEFAULT; 143 | return S_OK; 144 | } 145 | 146 | IFACEMETHODIMP EnumSubCommands(IEnumExplorerCommand** enumCommands) { 147 | *enumCommands = nullptr; 148 | return E_NOTIMPL; 149 | } 150 | 151 | IFACEMETHODIMP Invoke(IShellItemArray* items, IBindCtx* bindCtx) { 152 | if (items) { 153 | std::filesystem::path module_path{ wil::GetModuleFileNameW(wil::GetModuleInstanceHandle()) }; 154 | module_path = module_path.remove_filename().parent_path().parent_path(); 155 | module_path /= EXE_NAME; 156 | DWORD count; 157 | RETURN_IF_FAILED(items->GetCount(&count)); 158 | for (DWORD i = 0; i < count; ++i) { 159 | ComPtr item; 160 | auto result = items->GetItemAt(i, &item); 161 | if (SUCCEEDED(result)) { 162 | wil::unique_cotaskmem_string path; 163 | result = item->GetDisplayName(SIGDN_FILESYSPATH, &path); 164 | if (SUCCEEDED(result)) { 165 | auto command{ wil::str_printf(LR"-("%s" %s)-", module_path.c_str(), QuoteForCommandLineArg(path.get()).c_str()) }; 166 | wil::unique_process_information process_info; 167 | STARTUPINFOW startup_info = {sizeof(startup_info)}; 168 | RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW( 169 | nullptr, 170 | command.data(), 171 | nullptr /* lpProcessAttributes */, 172 | nullptr /* lpThreadAttributes */, 173 | false /* bInheritHandles */, 174 | CREATE_NO_WINDOW, 175 | nullptr, 176 | nullptr, 177 | &startup_info, 178 | &process_info)); 179 | } 180 | } 181 | } 182 | } 183 | return S_OK; 184 | } 185 | }; 186 | 187 | CoCreatableClass(ExplorerCommandHandler) 188 | CoCreatableClassWrlCreatorMapInclude(ExplorerCommandHandler) 189 | 190 | STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { 191 | if (ppv == nullptr) 192 | return E_POINTER; 193 | *ppv = nullptr; 194 | return Module::GetModule().GetClassObject(rclsid, riid, ppv); 195 | } 196 | 197 | STDAPI DllCanUnloadNow(void) { 198 | return Module::GetModule().GetObjectCount() == 0 ? S_OK : S_FALSE; 199 | } 200 | 201 | STDAPI DllGetActivationFactory(HSTRING activatableClassId, 202 | IActivationFactory** factory) { 203 | return Module::GetModule().GetActivationFactory(activatableClassId, factory); 204 | } 205 | -------------------------------------------------------------------------------- /src/explorer_command.def: -------------------------------------------------------------------------------- 1 | ; Declares the module parameters. 2 | 3 | LIBRARY 4 | 5 | EXPORTS 6 | DllGetClassObject PRIVATE 7 | DllCanUnloadNow PRIVATE 8 | DllGetActivationFactory PRIVATE -------------------------------------------------------------------------------- /template/AppxManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 19 | 20 | @@PackageDisplayName@@ 21 | Microsoft Corporation 22 | resources\app\resources\win32\code_150x150.png 23 | true 24 | disabled 25 | disabled 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 54 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | --------------------------------------------------------------------------------